Move the build and connector tags

git-svn-id: https://svn.apache.org/repos/asf/tomcat/tc5.5.x/tags/TOMCAT_5_5_24@802189 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/build/.classpath b/build/.classpath
new file mode 100644
index 0000000..24b6a38
--- /dev/null
+++ b/build/.classpath
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="resources"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
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/.project b/build/.project
new file mode 100644
index 0000000..1f143ea
--- /dev/null
+++ b/build/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>build</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
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..87f3e63
--- /dev/null
+++ b/build/BUILDING.txt
@@ -0,0 +1,155 @@
+$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
+  "apache-ant-[version]").  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) Install Subversion 1.3.x on your computer
+
+* If Subversion 1.3.x is already installed on your computer, skip to (3).
+
+* Download a binary distribution of Subversion 1.3.x from:
+
+    http://subversion.tigris.org/project_packages.html
+
+* Unpack the binary distribution into a convenient location so that the
+  Subversion release resides in its own directory.
+
+* Modify the PATH environment variable to include the directory 
+  ${svn.home}/bin in its list, where "${svn.home}" is the full pathname 
+  of the subversion release directory. This makes the "svn" command
+  available, which will be used to checkout the tomcat sources.
+
+* NOTE: If you're running behind a proxy server, the SVN checkout of Tomcat
+  source code may fail.  See http://subversion.tigris.org/faq.html#proxy for
+  ways to work around this.
+
+(3) Building Tomcat 5.5
+
+(3.1) Download main build script and build binary distribution
+
+* Download the main build.xml script from:
+  http://tomcat.apache.org/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
+
+(3.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 the Apache
+  source code repository, 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
+
+
+(4) 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
+
+
+(5) 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.
+
+(6) 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
+
+(7) Building the servlet and jsp API documentation
+
+The documentation can be easly rebuild, do
+    cd ${tomcat.source}/build
+    ant dist-javadoc
+
+(8) Building a release running tests:
+
+do
+    cd ${tomcat.source}/build
+    ant release
+
diff --git a/build/KEYS b/build/KEYS
new file mode 100644
index 0000000..5c12887
--- /dev/null
+++ b/build/KEYS
@@ -0,0 +1,368 @@
+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-----
+
+pub  1024D/33C60243 2004-09-12 Mark E D Thomas <markt@apache.org>
+     Key fingerprint = DCFD 35E0 BF8C A734 4752  DE8B 6FB2 1E89 33C6 0243
+sig         33C60243 2004-09-12   Mark E D Thomas <markt@apache.org>
+sub  2048g/0BECE548 2004-09-12
+sig         33C60243 2004-09-12   Mark E D Thomas <markt@apache.org>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.2.1 (MingW32)
+
+mQGiBEFEjegRBADocGttfROvtLGrTOW3xRqZHmFWybmEaI6jmnRdN/1gGXmb3wQL
+rHsS3fLFIIOYLPph0Kov9q4qNq36LekShIvjMBDFoj2/wRxaUtFq81asaRZg8Mcw
+4kVeIoe8OIOuWmvYhU8SH2jJNUnVVrpTPAa6QWquTmseNi6UJMjLxuL7DwCg//9u
+k2yj0vk6e4WSO6Fe5+EkQDED/AjQsy0kj9TpNHkKSSUR2evRlWPYA0YtxBSbsgON
+tT0cYipAp5IcYt6Zq5QzHiZreyQXLAjItDS2oGCIXfNbTYJ3kxxJTCU/3wlefVdq
+LBh4ttm7gmWaiTDTgG4axLF5oMpAb3m4v6s1KvXVVj2pqkhBknfuoRh1wPqbtwks
+7HOIBADVezl1/vny5YzdoqsDx1ByXMLi7CuMexQPllhRbdN+an+ZiJ5YP8J9rPdl
+NCELsCCcDKLGLjlp43XfMxsgYAPEZNG2ObjKTarhk3uGYN3aJrx7s+G+c2bu8o2n
+SyAFQ1iDsjS87PgSPCONA2/36ZShmv1OjLWz5Vo7hGSPcW4ZdLQiTWFyayBFIEQg
+VGhvbWFzIDxtYXJrdEBhcGFjaGUub3JnPohdBBARAgAdBQJBRI3oBwsJCAcDAgoC
+GQEFGwMAAAAFHgEAAAAACgkQb7IeiTPGAkOkvgCg0AcTAfe8m2ZSWkbsoqplLDsM
+0+oAoNl4EjXT+T2j2z8jdUYPaA8LztJguQINBEFEjekQCAD2Qle3CH8IF3Kiutap
+QvMF6PlTETlPtvFuuUs4INoBp1ajFOmPQFXz0AfGy0OplK33TGSGSfgMg71l6RfU
+odNQ+PVZX9x2Uk89PY3bzpnhV5JZzf24rnRPxfx2vIPFRzBhznzJZv8V+bv9kV7H
+AarTW56NoKVyOtQa8L9GAFgr5fSI/VhOSdvNILSd5JEHNmszbDgNRR0PfIizHHxb
+LY7288kjwEPwpVsYjY67VYy4XTjTNP18F1dDox0YbN4zISy1Kv884bEpQBgRjXyE
+pwpy1obEAxnIByl6ypUM2Zafq9AKUJsCRtMIPWakXUGfnHy9iUsiGSa6q6Jew1Xp
+Mgs7AAICCACuLSE3vBSOeTMM04ezuPt4zZUp0PFQGQL3bzuZp24f18S8P3BemGAk
+2V3HZYJmzmNgd4L0vIC9xyFduICFgbiV9uyzKPwHvCgQwaupFvFLGn7Q9LJ0nlaw
+GN7Km13vJTG3rrT/UMKwLTk+IMEYQwUgBht6HTnBaM+UqVx/eB4PHobimt5Redz9
+CnT4DrlA0M6Oh3ePWBD69Nnhwo2AN42dX/W2KcnDe2iRNu/JEbOYsssj0e3VmHwE
+mwa064TpQpw1fClyW7sf4aWOcQvcT12R0hNvRhTR1TV0pzjIMkbRPkRezhIY55AT
+TIfcaZrw+Yubmmw/pp/1wIDRzHexOq9riEwEGBECAAwFAkFEjekFGwwAAAAACgkQ
+b7IeiTPGAkN8ogCg4tHmgylXw4Y3ujF+J4cf2ollGa0AnRkyX8X+u/NrMi2g2xhE
+vpsTbAGW
+=r1gT
+-----END PGP PUBLIC KEY BLOCK-----
+
+pub   1024D/D7B65864 8/22/2006 Filip Hanik (Filips Apache Key) <fhanik@apache.org>
+UserID                                              KeyId Long         Key Id
+Filip Hanik (Filips Apache Key) <fhanik@apache.org> 0x7B87E296D7B65864 0xD7B65864
+Fingerprint 1429 6D83 984C 223A 9761  F633 7B87 E296 D7B6 5864
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.5 (MingW32) - GPGshell v3.52
+
+mQGiBETrKsYRBACQmyglB/jdLFSU1gczfd42MmO5tA6YeZjhzejU/JJtLpgdSW1S
+1me/2SYDLsyuwrE7P5DVz1geWSfqXqzO9ThzY1Ixgqrg6n6o/3wq5SMBCxrAJXcN
+Zi6Vbwxt/LMcOzOfUKFPkGBUM+qsj3ItmInfsWBgOrwxQ37KtjCMgNHOowCg5o+D
+Nmmw8JEWjHq8XVMvcGUJR1UD/3lU6qiqOUH+75UdyYfAO1OZkccnUeR+5r1ZcjZL
+hJl+0TT18twa5v3UC5812TGPsTo0RRwpEqeO7urv26XulfofKV9b8JBRIVpunBd1
+9NpEN/SBfSQ+RzFWhg7xRirlBV0C30toFqhkI72vkYvbKhbaQKGqSt0daXLz0dU3
+NgxVA/9yOisu/gNHs2IsrPjCPO5uU7BIaiLfvTENVcfenV1O/YG0DjCsaccUBh0b
+34agdWldR+ak6beGKp8My+6m4+v9Mh5GI372EovUU5Aqyts71fDPyC+RHL7pFIPf
+HQNR+asb+QvY6Fgon8HUxL/bPFKN1JjnT3yWZTZrX9xUhe5cMbQzRmlsaXAgSGFu
+aWsgKEZpbGlwcyBBcGFjaGUgS2V5KSA8ZmhhbmlrQGFwYWNoZS5vcmc+iGAEExEC
+ACAFAkTrKsYCGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRB7h+KW17ZYZFm8
+AKDTdlIxDHP802gyr22mQaB5xPsqnwCeI07ItFbIWz2izl5wltluNzfiUdu5Ag0E
+ROsq0hAIANOpE8Z+bXbiPp8W/0fkzgjtMojmF2sqFg728kzPrtADfqfsS8javY18
+Zu2kxbmiAnUxVhisfOB4DMeCqpKT1DNlPeNowXeO7x1XJcFyzc59cl9fPC52+qwS
+fb3w1R7/nbFZqZXgsNg0IuOUJ29weAWvhugLrd2mRqUO6NtmCvH8NlBVpminESLE
+L4hpfyGGSJeAxeEAT0B3amwJ77cv2xIoVD5zowiS+WM1qDvpXQm+b7uiz4YxviWO
+4ZKSHSQ2iq8SQc2JxoOOPlTHe8+h1MI+Uqmb7eQKEik8Y3hXuY7g+55cLYT0NMz0
+2Tn2tblghqu2QPphN9IQ/5iA1AQ4/SMAAwYH/2cTjiBXa6/2bAywR6csbrW1r0/s
+ugY882+p6xM1HFzl9pv9zdS9je8kWcDv3sEvhVfdAqeQ0SCA/5ZrjuIn0f77P/vR
+U4cZjjJUecrD2YjkFkXETd1Fr4TwtbiVWiDqLGZOaZnDYdhDNKXF0zfcAixjSP3x
+IXd9R3Slrg33TLYmWHdVWpp0GR+rdQwPFFXbi036aPLT2YB7kFjGeb/qUnlFPVn4
+my+STMKLuvl4qIZl9UE6TRc0G5L2vJeJ/43BNEEovyQpV0TIJ3NSK5wMUOF6bCA0
+yVoMPkWmQIGKk0bSD5L+LbV2zqKebpG9jbTcKmP8aSrnPsTzDC3rUkolj6iISQQY
+EQIACQUCROsq0gIbDAAKCRB7h+KW17ZYZJLjAJ9Av5B/TShKgq2C+D64050m871p
+4QCcD5ciTfRByolZe+fQ3lktOpO0Mpc=
+=ttOX
+-----END PGP PUBLIC KEY BLOCK-----
+
+pub   1024D/0D811BBE 2006-11-14
+      Key fingerprint = F3A0 4C59 5DB5 B6A5 F1EC  A43E 3B7B BB10 0D81 1BBE
+uid                  Yoav Shapira <yoavs@computer.org>
+sig 3        0D811BBE 2006-11-14  Yoav Shapira <yoavs@computer.org>
+sub   2048g/286BACF1 2006-11-14
+sig          0D811BBE 2006-11-14  Yoav Shapira <yoavs@computer.org>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.5 (MingW32)
+
+mQGiBEVZ4AwRBAC9WDbCjRX9Q81Con7cycGkkui6JZndLhX3Jbzlc/eHG0/fUetP
+0c5ZdIvTyjj+L/DRI6btrgl+jR64qkuapsYD/KDXQGkpK5zPpmxUPmXJ5tfVTbOj
+gacUm2cZjYjSK3dsIs4sDUqNYfBfdwesJ+Hycc7XqkTF1lO2MN9yp8g+4wCg0W8x
+/ZYCTb9D8JOPzfSNf8cIFCkD/j5GXA2xlSXuAFBgWpFak5OkeF8cwEkv0CQ0zCqP
+R/rTmDGO/73dpQEzgY+gLMSvtkK0pVEYaE15lg2mxma9d0pGE+fmsu5w7SQUip15
+HN5E3qP/VB4X1yp+YiHPGTDjRgJm+xbvTGSFFr0wNSSYCVpVGdYHNmetYsB5JqkH
+YmiWA/4vnkWnkzQeUNNPvep0lSrEG9jiON4k/d5opWwjxIP4aev8V7//V9ASzznF
+D6eEL23ePX5ZuKLyDeOSRAwaPpa/Rp4AkiUGzKK21wAwKip+lcbT5m2ButoQhgNI
+ZlmnfhN7E2t2S6iS9VzHEo1S8Jv9uQZJ89Tp5fiFe1pXL2qBHrQhWW9hdiBTaGFw
+aXJhIDx5b2F2c0Bjb21wdXRlci5vcmc+iGAEExECACAFAkVZ4AwCGwMGCwkIBwMC
+BBUCCAMEFgIDAQIeAQIXgAAKCRA7e7sQDYEbvgiLAKDHEgeJyxlrxNJ4m51jOnhG
+xlsLOQCbBWAdTjpMVcNqmd6Fa5fgyCbh8XS5Ag0ERVngFBAIAIEVU1iOoq4CyD3I
+f+AChfSFAgqjKmjqEyPv2RDLPkI3g2FvC6HvOlUucIe6IjqvTXztdxSRQu2EGq5i
+W8e5ajTZiI9ZNs26XLL4/q/gYRaDjUsI7J3PAOL9lNdws6ZoqlCh44R/cvekuixx
+HoHGskGxAHBRdjv9Oqy4x5hR5kebGq9Ayf8CQZ3l7aRekwlMUyCsmMkNxmqMls2g
+ViBFD0/9a/xodE2VMVMg5eQ8A0enlrGDghG9d2m586JtOje55rMVnVPkEuNkz9AC
+DkR0CiwQqX72Ub10t/qcNqbDeMHFacOBYRKfS7Qdm3/jb8Tc4jO/AXFcUGbH5niz
+pPGs+UMAAwUH/1M2unaFyfJddVPQRZCJEFxdlxkg40tewgjaNJLwnqOJXw1RENNM
+bSx4Gvz8M6WvZtkvITt29P+O4EmGq+LYTKmLM/E399KuqoZGbyCu3Gm+RIxKmRkf
+Y3izseOhrUX2ycUIOF1BFzIYs6HeO/sZeba1bapOFo/xS6NwnuJl6uXUmynGjVtY
+gvQ+dLYAcDXUQJd+QjyXdsbnp1jmdSrqqscPGOquRAw7/sp/ivom4DDHMTz4HErz
+NfHzn4z8kUE7T1EEpnFU4SBiJkpm/+yEhEq9hDG2GZmxfQd34iRXpC5B4ZbRiwW8
+p/bhzvcxZcrahQzu5yyq2+kGhK7IA9APFGSISQQYEQIACQUCRVngFAIbDAAKCRA7
+e7sQDYEbvppSAJ9+i7TzCNvZ4PK/odiIWeZ61+KKyACfSjQXnC5UbxndwtkOzFKf
+Io8ZP0E=
+=xT1M
+-----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..0714bd7
--- /dev/null
+++ b/build/RELEASE-NOTES
@@ -0,0 +1,180 @@
+
+
+                     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
+* 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 the fulldocs downloadable file(s) 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.x)
+* 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)
+* 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).
+
+
+================
+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
+
+Please note, that these are only recommendations and may not apply in some cases.
+Before you change this variable, make sure you understand its impact, and what it does.
+A brief explanation can be found in the mailing archives at 
+http://marc.theaimsgroup.com/?l=tomcat-dev&m=115689139313901&w=2
+For further assistance, please consult your JVM vendor.
+
+
+=============================
+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://tomcat.apache.org/faq/misc.html#invoker.
+
+
+==============================
+Viewing the Tomcat Change Log:
+==============================
+See changelog.html in this directory.
+
+
+====================
+When all else fails:
+====================
+See the FAQ
+http://tomcat.apache.org/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..4343d78
--- /dev/null
+++ b/build/RUNNING.txt
@@ -0,0 +1,187 @@
+$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 JRE_HOME to the pathname of
+      the directory into which you installed the JRE, e.g. c:\jre5.0
+      or /usr/local/java/jre5.0.
+
+NOTE: You may also use the full JDK rather than just the JRE. In this
+      case set the JAVA_HOME environment variable to the pathname of
+      the directory into which you installed the JDK, 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://tomcat.apache.org
+
+(2.2) Unpack the binary distribution into a convenient location so that the
+      distribution resides in its own directory (conventionally named
+      "apache-tomcat-[version]").  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://tomcat.apache.org
+
+
+(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
+====================================
+
+(1) Obtain the compat package:
+
+(1.1) Download the compat package from the binary download site:
+      http://tomcat.apache.org
+
+      * 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..1cb5013
--- /dev/null
+++ b/build/build.properties.default
@@ -0,0 +1,261 @@
+# -----------------------------------------------------------------------------
+# build.properties.default
+#
+# 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$
+# -----------------------------------------------------------------------------
+
+# ----- Vesion Control Flags -----
+version.major=5
+version.minor=5
+version.build=0
+version.patch=0
+#Set the pretty version name
+version=5.5.24
+
+# ----- 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
+
+
+# ----- Default Base Path for Dependent Packages -----
+# Please note this path must be absolute, not relative,
+# as it is referenced with different working directory
+# contexts by the various build scripts.
+base.path=/usr/share/java
+#base.path=C:/path/to/the/repository
+#base.path=/usr/local
+
+# ----- Jakarta files base location -----
+base-jakarta.loc=http://archive.apache.org/dist/jakarta
+
+# ----- Logging files base location -----
+base-logging.loc=http://archive.apache.org/dist/logging
+
+# ----- XML files base location -----
+base-xml.loc=http://archive.apache.org/dist/xml
+
+# ----- Struts files base location -----
+base-struts.loc=http://archive.apache.org/dist/struts
+
+# ----- Sourceforge files base location -----
+base-sf.loc=http://switch.dl.sourceforge.net/sourceforge
+#base-sf.loc=http://heanet.dl.sourceforge.net/sourceforge
+#base-sf.loc=http://internap.dl.sourceforge.net/sourceforge
+
+# ----- Additional Tomcat files base location -----
+base-tomcat.loc=http://archive.apache.org/dist/tomcat
+
+# --------------------------------------------------
+#                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.1
+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.1.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.7
+commons-digester.lib=${commons-digester.home}
+commons-digester.jar=${commons-digester.lib}/commons-digester-1.7.jar
+commons-digester.loc=${base-jakarta.loc}/commons/digester/binaries/commons-digester-1.7.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 2.0 or later -----
+commons-modeler.home=${base.path}/commons-modeler-2.0
+commons-modeler.lib=${commons-modeler.home}
+commons-modeler.jar=${commons-modeler.lib}/commons-modeler-2.0.jar
+commons-modeler.loc=${base-jakarta.loc}/commons/modeler/binaries/commons-modeler-2.0.tar.gz
+
+# ----- Xerces XML Parser, version 2.8.0 -----
+xerces.home=${base.path}/xerces-2_8_0
+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.8.0.tar.gz
+
+
+# ----- Eclipse JDT, version 3.1.2 or later -----
+jdt.home=${base.path}/eclipse/plugins
+jdt.lib=${jdt.home}
+jdt.jar=${jdt.lib}/org.eclipse.jdt.core_3.1.2.jar
+jdt.loc=http://archive.eclipse.org/eclipse/downloads/drops/R-3.1.2-200601181600/eclipse-JDT-3.1.2.zip
+
+
+# ----- Tomcat native library -----
+tomcat-native.home=${base.path}/tomcat-native-current
+tomcat-native.tar.gz=${tomcat-native.home}/tomcat-native.tar.gz
+tomcat-native.loc=${base-tomcat.loc}/tomcat-connectors/native/tomcat-native-current.tar.gz
+
+
+# --------------------------------------------------
+#              CORE OPTIONAL LIBRARIES
+# --------------------------------------------------
+
+
+# ----- Commons DBCP, version 1.1 or later -----
+commons-dbcp.version=1.2.2
+commons-dbcp.home=${base.path}/commons-dbcp-1.2.2-src
+commons-dbcp-src.loc=${base-jakarta.loc}/commons/dbcp/source/commons-dbcp-1.2.2-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.3-src
+commons-pool-src.loc=${base-jakarta.loc}/commons/pool/source/commons-pool-1.3-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-3.0.1
+jmx.lib=${jmx.home}/lib
+jmx.jar=${jmx.lib}/mx4j.jar
+jmx-tools.jar=${jmx.lib}/mx4j-tools.jar
+jmx-remote.jar=${jmx.lib}/mx4j-remote.jar
+jmx.loc=${base-sf.loc}/mx4j/mx4j-3.0.1.tar.gz
+
+
+# ----- 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
+
+
+# ----- Rhino ------
+rhino.home=${base.path}/rhino1_6R2
+rhino.jar=${rhino.home}/js.jar
+rhino.loc=http://ftp.mozilla.org/pub/mozilla.org/js/rhino1_6R2.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.nsisdl.dll=${nsis.home}/Plugins/nsisdl.dll
+nsis.loc=${base-sf.loc}/nsis/nsis20.exe
+
+
+# ----- Struts, version 1.2.4 or later -----
+struts.home=${base.path}/struts-1.2.7
+struts.lib=${struts.home}/lib
+struts.jar=${struts.lib}/struts.jar
+struts.loc=${base-struts.loc}/binaries/struts-1.2.7.tar.gz
+
+
+# --------------------------------------------------
+#                OPTIONAL LIBRARIES
+# --------------------------------------------------
+
+
+# ----- Java Activation Framework (JAF), version 1.0.1 or later -----
+activation.home=${base.path}/jaf-1.0.2
+activation.lib=${activation.home}
+activation.jar=${activation.lib}/activation.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_1b
+jta.lib=${jta.home}
+jta.jar=${jta.lib}/jta.jar
+
+# ----- Java Mail, version 1.2 or later -----
+mail.home=${base.path}/javamail-1.3.3_01
+mail.lib=${mail.home}
+mail.jar=${mail.lib}/mail.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 API v2.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 v2.0 -----
+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
diff --git a/build/build.xml b/build/build.xml
new file mode 100644
index 0000000..384a3e1
--- /dev/null
+++ b/build/build.xml
@@ -0,0 +1,2072 @@
+<?xml version="1.0"?>
+<!-- This is the main Tomcat 5.5 build file: it invokes most of the others. -->
+<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="2006" />
+  <property name="version.major"         value="5" />
+  <property name="version.minor"         value="5" />
+  <property name="version.build"         value="0" />
+  <property name="version.patch"         value="0" />
+  <property name="version"               value="5.5.0-dev" />
+  <property name="version.number"        value="${version.major}.${version.minor}.${version.build}.${version.patch}" />
+
+  <property name="project"               value="apache-tomcat" />
+  <property name="final.name"            value="${project}-${version}" />
+  <property name="final-src.name"        value="${project}-${version}-src" />
+
+  <!-- Subprojects -->
+  <property name="api.project"           value="servletapi" />
+  <property name="tomcat.project"        value="build" />
+  <property name="catalina.project"      value="container" />
+  <property name="jtc.project"           value="connectors" />
+  <property name="jasper.project"        value="jasper" />
+  <property name="ant.jar"               value="${ant.home}/lib/ant.jar"/>
+  <property name="ant-launcher.jar"      value="${ant.home}/lib/ant-launcher.jar"/>
+
+  <!-- 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}"/>
+  <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" />
+  <property name="tester.delay"         value="8" />
+  
+  <!-- constant to declare a file binary for md5sum -->
+  <property name="md5sum.binary-prefix" value=" *" />
+
+  <!-- =================== 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="tomcatjkstatus.build.notrequired"
+              targetfile="${tomcat.build}/server/lib/tomcat-jkstatus-ant.jar">
+      <srcfiles dir="${jtc.home}/jk/jkstatus/src/share" 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>
+
+  </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 tofile="${tomcat.build}/bin/tomcat-native.tar.gz"
+            file="${tomcat-native.tar.gz}" />
+
+    <!-- <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-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/coyote/ajp/**" />
+        <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="compile.tomcat5">
+      <property name="catalina.home" value="${tomcat.build}"/>
+      <property name="build.home" value="${tomcat.build}"/>
+      <property name="tomcat5.detect" value="true"/>
+      <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-tomcatjkstatus" 
+          unless="tomcatjkstatus.build.notrequired" 
+          depends="init" description="Build j-t-c/jkstatus">
+    <echo>========== Building: tomcat-jkstatus-ant </echo>
+
+    <ant dir="${jtc.home}/jk/jkstatus" target="dist">
+      <property name="catalina.build" value="${tomcat.build}"/>
+      <property name="jtc.home" value="${jtc.home}"/>
+    </ant>
+
+  	<!-- jkstatus ant API -->
+  	<!--<delete file="${tomcat.build}/server/lib/tomcat-jkstatus-ant.jar" />-->
+    <ant dir="${jtc.home}/jk/jkstatus" target="copy">
+      <property name="catalina.build" value="${tomcat.build}"/>
+      <property name="jtc.home" value="${jtc.home}"/>
+    </ant>
+
+  </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-tomcatapr"
+              unless="tomcatapr.build.notrequired"
+              depends="init" description="builds j-t-c/apr">
+        <echo>========== Building: tomcat-apr </echo>
+
+        <ant dir="${jtc.home}/jni" 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>
+
+        <!-- APR -->
+        <jar jarfile="${tomcat.build}/server/lib/tomcat-apr.jar" index="true">
+          <fileset dir="${tomcat.build}/classes">
+            <include name="org/apache/tomcat/jni/**" />
+            <!-- Javadoc and i18n exclusions -->
+            <exclude name="**/package.html" />
+            <exclude name="**/LocalStrings_*" />
+          </fileset>
+        </jar>
+
+      </target>
+
+      <target name="build-juli"
+              unless="tomcatjuli.build.notrequired"
+              depends="init" description="builds j-t-c/juli">
+        <echo>========== Building: tomcat-juli </echo>
+
+        <ant dir="${jtc.home}/juli" target="compile-only">
+          <property name="build.home" value="${tomcat.build}"/>
+        </ant>
+
+        <!-- Java.util.logging Implementation -->
+        <jar jarfile="${tomcat.build}/bin/tomcat-juli.jar" index="true">
+          <fileset dir="${tomcat.build}/classes">
+            <include name="org/apache/juli/**" />
+            <!-- Javadoc and i18n exclusions -->
+            <exclude name="**/package.html" />
+            <exclude name="**/LocalStrings_*" />
+          </fileset>
+        </jar>
+
+        <copy file="${basedir}/resources/logging.properties"
+             todir="${tomcat.build}/conf" />
+
+      </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="config.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-tomcatapr"/>
+    <antcall target="build-tomcatutil"/>
+    <antcall target="build-tomcatcoyote"/>
+    <antcall target="build-tomcathttp11"/>
+
+    <antcall target="build-catalina"/>
+
+    <antcall target="build-tomcatjk"/>
+    <antcall target="build-tomcatjkstatus"/>
+
+    <antcall target="build-juli"/>
+
+    <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-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="**/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">
+        <exclude name="WEB-INF/tagPlugins.xml" />
+      </fileset>
+    </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/host-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}/server/webapps/host-manager/WEB-INF/lib" />
+    <jar jarfile="${tomcat.build}/server/webapps/host-manager/WEB-INF/lib/catalina-host-manager.jar"
+         index="true">
+       <fileset dir="${tomcat.build}/server/webapps/host-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/host-manager/host-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 -->
+    <copy todir="embed/lib" file="${commons-logging.jar}"/>
+    <copy todir="embed/lib" file="${commons-modeler.jar}"/>
+
+    <!-- Connector -->
+    <copy todir="embed/lib">
+      <fileset dir="build/server/lib">
+        <!--<include name="tomcat-ajp.jar"/>-->
+        <include name="tomcat-coyote.jar"/>
+        <include name="tomcat-http.jar"/>
+        <include name="tomcat-util.jar"/>
+      </fileset>
+    </copy>
+
+    <!-- Servlet API implementation -->
+    <copy todir="embed/lib">
+      <fileset dir="build/common/lib">
+        <include name="naming-factory.jar"/>
+        <include name="servlet-api.jar"/>
+        <include name="naming-resources.jar"/>
+      </fileset>
+      <fileset dir="build/server/lib">
+        <include name="catalina.jar"/>
+        <include name="catalina-optional.jar"/>
+        <include name="servlets-default.jar"/>
+      </fileset>
+    </copy>
+
+    <!-- JNDI extra
+    <copy todir="embed/lib">
+      <fileset dir="build/common/lib">
+        <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"/>
+      </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="jasper-compiler.jar"/>
+        <include name="jasper-compiler-jdt.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"/>
+    <delete file="embed/conf/context.xml" />
+    <copy todir="embed/conf">
+      <fileset dir="build/conf">
+         <include name="context.xml"/>
+         <include name="tomcat-users.xml"/>
+         <include name="web.xml"/>
+         <!-- no longer needed
+            <include name="server.xml"/>
+         -->
+      </fileset>
+    </copy>
+    <replace file="embed/conf/context.xml" token="&lt;Context" value="&lt;Context privileged='true'" />
+
+    <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" >
+
+    <!-- Servlet and JSP -->
+    <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"
+           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" />
+          <include name="org/apache/tomcat/util/*" />
+          <include name="org/apache/tomcat/util/digester/*" />
+          <exclude name="**/package.html" />
+          <exclude name="**/LocalStrings_*" />
+       </fileset>
+    </jar>
+
+    <!-- Main build script -->
+    <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-depend"
+   description="Clean depend src components">
+    <delete dir="${tomcat-dbcp.home}"/>    
+    <delete dir="${jasper-compiler-jdt.home}"/>
+  </target>
+  
+  <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="${tester.delay}"/>
+
+            <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>
+
+  <!-- ====================== 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">
+    <filter token="VERSION" value="${version}"/>
+
+    <!-- Copy the top-level documentation files -->
+    <copy todir="${tomcat.dist}" filtering="true" >
+      <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" />
+        <exclude name="jk2.manifest" />
+        <exclude name="jk2.properties" />
+        <exclude name="jkconf.ant.xml" />
+        <exclude name="jkconfig.manifest" />
+        <exclude name="shm.manifest" />
+        <exclude name="tomcat-jk2.manifest" />
+        <exclude name="uriworkermap.properties" />
+        <exclude name="workers2.properties" />
+        <exclude name="workers2.properties.minimal" />
+        <exclude name="workers.properties" />
+        <exclude name="workers.properties.minimal" />
+      </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/**" />
+        <exclude name="host-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="**/ROOT/WEB-INF/classes/**" />
+        <exclude name="**/WEB-INF/src/**" />
+      </fileset>
+    </copy>
+
+    <!-- Bugzilla 37035: http://issues.apache.org/bugzilla/show_bug.cgi?id=37035 -->
+    <touch file="${tomcat.dist}/temp/bugzilla37035-safeToDelete.tmp" />
+
+    <!-- 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}"/>
+
+    <!-- Main build file -->
+    <copy todir="${tomcat.dist}/src">
+      <fileset dir="${basedir}/resources">
+        <include name="build.xml" />
+      </fileset>
+    </copy>
+
+    <!-- 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>
+
+    <!-- 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>
+
+    <!-- tomcat container source -->
+    <copy todir="${tomcat.dist}/src/${catalina.project}">
+      <fileset dir="${catalina.home}">
+        <exclude name="**/build/**"/>
+        <exclude name="**/dist/**"/>
+      </fileset>
+    </copy>
+
+    <!-- tomcat jasper source -->
+    <copy todir="${tomcat.dist}/src/${jasper.project}">
+      <fileset dir="${jasper.home}">
+        <exclude name="**/build/**"/>
+        <exclude name="**/dist/**"/>
+      </fileset>
+    </copy>
+
+    <!-- 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"/>
+    <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="${nsis.nsisdl.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" />
+
+    <checksum file="${tomcat.release}/v${version}/bin/${final.name}.exe"
+              forceOverwrite="yes" fileext=".md5" />
+    <echo     file="${tomcat.release}/v${version}/bin/${final.name}.exe.md5" 
+                      message="${md5sum.binary-prefix}${final.name}.exe${line.separator}" append="true" />
+  </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>
+
+  <!-- Packages the core zip distro -->
+  <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>
+
+    <checksum file="${tomcat.release}/v${version}/bin/${final.name}.zip"
+              forceOverwrite="yes" fileext=".md5" />
+    <echo     file="${tomcat.release}/v${version}/bin/${final.name}.zip.md5"
+                      message="${md5sum.binary-prefix}${final.name}.zip${line.separator}" append="true" />
+  </target>
+
+  <!-- Packages the embedded Tomcat distro in zip format -->
+  <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>
+
+    <checksum file="${tomcat.release}/v${version}/bin/${final.name}-embed.zip"
+              forceOverwrite="yes" fileext=".md5" />
+    <echo     file="${tomcat.release}/v${version}/bin/${final.name}-embed.zip.md5"
+                      message="${md5sum.binary-prefix}${final.name}-embed.zip${line.separator}" append="true" />
+  </target>
+
+  <!-- Packages the deployer distribution in zip format -->
+  <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>
+
+    <checksum file="${tomcat.release}/v${version}/bin/${final.name}-deployer.zip"
+              forceOverwrite="yes" fileext=".md5" />
+    <echo     file="${tomcat.release}/v${version}/bin/${final.name}-deployer.zip.md5"
+                      message="${md5sum.binary-prefix}${final.name}-deployer.zip${line.separator}" append="true" />
+  </target>
+
+  <!-- Packages the JDK 1.4 compatibility distro in zip format -->
+  <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>
+ 
+    <checksum file="${tomcat.release}/v${version}/bin/${final.name}-compat.zip"
+              forceOverwrite="yes" fileext=".md5" />
+    <echo     file="${tomcat.release}/v${version}/bin/${final.name}-compat.zip.md5"
+                      message="${md5sum.binary-prefix}${final.name}-compat.zip${line.separator}" append="true" />
+  </target>
+
+  <!-- Packages the admin webapp distro in zip format -->
+  <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>
+   
+    <checksum file="${tomcat.release}/v${version}/bin/${final.name}-admin.zip"
+              forceOverwrite="yes" fileext=".md5" />
+    <echo     file="${tomcat.release}/v${version}/bin/${final.name}-admin.zip.md5"
+                      message="${md5sum.binary-prefix}${final.name}-admin.zip${line.separator}" append="true" />
+  </target>
+
+  <!-- Prepares for cutting the release -->
+  <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"/>
+
+    <!-- This is why releasing must be done on a Windows box: 
+         otherwise this check fails and the .exe distro is not generated -->
+    <condition property="execute.installer">
+      <and>
+        <os family="windows" />
+        <available file="${nsis.exe}" />
+        <available file="${nsis.installoptions.dll}" />
+        <available file="${nsis.nsexec.dll}" />
+        <available file="${nsis.nsisdl.dll}" />
+      </and>
+    </condition>
+
+  </target>
+
+  <!-- Packages the core tar.gz distro -->
+  <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>
+
+    <checksum file="${tomcat.release}/v${version}/bin/${final.name}.tar.gz"
+              forceOverwrite="yes" fileext=".md5" />
+    <echo     file="${tomcat.release}/v${version}/bin/${final.name}.tar.gz.md5"
+                      message="${md5sum.binary-prefix}${final.name}.tar.gz${line.separator}" append="true" />
+  </target>
+
+  <!-- Packages the embedded Tomcat distro in tar.gz format -->
+  <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>
+
+    <checksum file="${tomcat.release}/v${version}/bin/${final.name}-embed.tar.gz"
+              forceOverwrite="yes" fileext=".md5" />
+    <echo     file="${tomcat.release}/v${version}/bin/${final.name}-embed.tar.gz.md5"
+                      message="${md5sum.binary-prefix}${final.name}-embed.tar.gz${line.separator}" append="true" />
+  </target>
+
+  <!-- Packages the deployer Tomcat distro in tar.gz format -->
+  <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>
+
+    <checksum file="${tomcat.release}/v${version}/bin/${final.name}-deployer.tar.gz"
+              forceOverwrite="yes" fileext=".md5" />
+    <echo     file="${tomcat.release}/v${version}/bin/${final.name}-deployer.tar.gz.md5"
+                      message="${md5sum.binary-prefix}${final.name}-deployer.tar.gz${line.separator}" append="true" />
+  </target>
+
+  <!-- Packages the 1.4 compatibility distro in tar.gz format -->
+  <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>
+
+    <checksum file="${tomcat.release}/v${version}/bin/${final.name}-compat.tar.gz"
+              forceOverwrite="yes" fileext=".md5" />
+    <echo     file="${tomcat.release}/v${version}/bin/${final.name}-compat.tar.gz.md5"
+                      message="${md5sum.binary-prefix}${final.name}-compat.tar.gz${line.separator}" append="true" />
+  </target>
+
+  <!-- Packages the admin webapp distro in tar.gz format -->
+  <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>
+
+    <checksum file="${tomcat.release}/v${version}/bin/${final.name}-admin.tar.gz"
+              forceOverwrite="yes" fileext=".md5" />
+    <echo     file="${tomcat.release}/v${version}/bin/${final.name}-admin.tar.gz.md5"
+                      message="${md5sum.binary-prefix}${final.name}-admin.tar.gz${line.separator}" append="true" />
+  </target>
+
+  <!-- Packages the documentation distro in tar.gz format -->
+  <target name="package-docs-tgz" depends="prepare-release">
+    <!-- Generate docs -->
+    <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>
+
+    <!-- Package gocs -->
+    <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" />
+        <include name="RUNNING.txt" />
+      </tarfileset>
+      <tarfileset dir="${tomcat.dist}/webapps/tomcat-docs" prefix="tomcat-5.5-doc">
+        <include name="**" />
+      </tarfileset>
+    </tar>
+
+    <checksum file="${tomcat.release}/v${version}/bin/${final.name}-fulldocs.tar.gz"
+              forceOverwrite="yes" fileext=".md5" />
+    <echo     file="${tomcat.release}/v${version}/bin/${final.name}-fulldocs.tar.gz.md5"
+                      message="${md5sum.binary-prefix}${final.name}-fulldocs.tar.gz${line.separator}" append="true" />
+  </target>
+
+  <!-- Packages the source code distribution in zip format -->
+  <target name="package-src-zip">
+    <zip zipfile="${tomcat.release}/v${version}/src/${final-src.name}.zip">
+      <zipfileset dir="${tomcat.dist}/src" prefix="${final-src.name}" />
+    </zip>
+
+    <checksum file="${tomcat.release}/v${version}/src/${final-src.name}.zip"
+              forceOverwrite="yes" fileext=".md5" />
+    <echo     file="${tomcat.release}/v${version}/src/${final-src.name}.zip.md5"
+                      message="${md5sum.binary-prefix}${final-src.name}.zip${line.separator}" append="true" />
+  </target>
+
+  <!-- Packages the source code distribution in tar.gz format -->
+  <target name="package-src-tgz">
+    <fixcrlf srcdir="${tomcat.dist}"
+     excludes="**/*.jar,**/*.gif,**/*.bmp,**/*.jpg,**/*.ico,**/*.war" 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>
+
+    <checksum file="${tomcat.release}/v${version}/src/${final-src.name}.tar.gz"
+              forceOverwrite="yes" fileext=".md5" />
+    <echo     file="${tomcat.release}/v${version}/src/${final-src.name}.tar.gz.md5"
+                      message="${md5sum.binary-prefix}${final-src.name}.tar.gz${line.separator}" append="true" />
+  </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="${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="downloadgz">
+      <param name="sourcefile" value="${jmx.loc}"/>
+      <param name="destfile" value="${jmx.jar}"/>
+    </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="downloadfile">
+      <param name="sourcefile" value="${tomcat-native.loc}"/>
+      <param name="destfile" value="${tomcat-native.tar.gz}"/>
+      <param name="destdir" value="${tomcat-native.home}"/>
+    </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" depends="proxyflags" 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="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>
+
+
+  <!-- ==================== 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="Target for Gump builds"
+       depends="gumpy-prepare,prepare-release,dist,dist-source,installer,package-zip,package-tgz,package-src-zip,package-src-tgz" />
+
+  <target name="gumpy-prepare" description="Collect Gump-built jar files" >
+    <mkdir dir="${tomcat.build}"/>
+    <mkdir dir="${tomcat.build}/classes" />
+    <mkdir dir="${tomcat.build}/server/lib" />
+    <mkdir dir="${tomcat.build}/common/lib" />
+    <copy file="${tomcat-util.jar}" tofile="${tomcat.build}/server/lib/tomcat-util.jar"/>
+    <copy file="${tomcat-ajp.jar}" tofile="${tomcat.build}/server/lib/tomcat-ajp.jar"/>
+    <copy file="${tomcat-coyote.jar}" tofile="${tomcat.build}/server/lib/tomcat-coyote.jar"/>
+    <copy file="${tomcat-http11.jar}" tofile="${tomcat.build}/server/lib/tomcat-http11.jar"/>
+    <copy file="${tomcat-jni.jar}" tofile="${tomcat.build}/server/lib/tomcat-apr.jar" /> 
+    <property name="tomcat.aprbuild.notrequired" value="true" />
+    <copy file="${jasper-runtime.jar}" tofile="${tomcat.build}/common/lib/jasper-runtime.jar"/>
+    <copy file="${jasper-compiler.jar}" tofile="${tomcat.build}/common/lib/jasper-compiler.jar"/>
+    <property name="jasper.build.notrequired" value="true" />
+  </target>
+</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..af497d5
--- /dev/null
+++ b/build/resources/build.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0"?>
+<!-- This file is the Tomcat 5.x net build file.  It's what users run to build Tomcat -->
+<!-- from scratch.  See BUILDING.txt for the full instructions.                       -->
+<project name="Tomcat 5 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="2006" />
+  <property name="version"               value="5.5" />
+  <property name="project"               value="apache-tomcat" />
+  <property name="final.name"            value="${project}-${version}" />
+  <property name="final-src.name"        value="${project}-${version}-src" />
+
+  <!-- SVNROOT -->
+  <property name="svnroot" 
+           value="http://svn.apache.org/repos/asf/" />
+
+  <!-- Subprojects -->
+  <property name="api.project"           value="servletapi" />
+  <property name="tomcat.project"        value="build" />
+  <property name="catalina.project"      value="container" />
+  <property name="jtc.project"           value="connectors" />
+  <property name="jasper.project"        value="jasper" />
+
+  <property name="current.loc"           value="tomcat/current/tc5.5.x" />
+
+  <!-- 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}"/>
+  <property name="jtc.home"
+           value="${basedir}/${jtc.project}"/>
+  <property name="tomcat.home"
+           value="${basedir}/${tomcat.project}"/>
+
+  <target name="build" depends="check.source,get.source"
+   description="Builds all components">
+
+    <ant dir="${tomcat.home}" target="download" />
+    <ant dir="${tomcat.home}" target="deploy" />
+
+  </target>
+
+  <!-- Top-level clean target added per Bugzilla 33325 -->
+  <target name="clean"
+          description="Clean (delete) all project files">
+    <echo message="Deleting all project files" />
+    <delete dir="${api.home}" />
+    <delete dir="${catalina.home}" />
+    <delete dir="${jasper.home}" />
+    <delete dir="${jtc.home}" />
+    <delete dir="${tomcat.home}" />
+  </target>
+
+  <target name="checkout"
+          description="Update or checkout required sources from SVN">
+
+    <echo level="info"
+          message="If the checkout fails, see http://tomcat.apache.org/svn.html and http://subversion.tigris.org/faq.html#proxy" />
+
+    <!-- Bugzilla 37977: http://issues.apache.org/bugzilla/show_bug.cgi?id=37977 -->
+    <!--  hackish: inputstring="t${line.separator}" is t+<enter> for svn         -->
+    <!--  to temporarily accept the certificate of svn.apache.org.               -->
+    <exec dir="${basedir}"
+          executable="svn"
+          inputstring="t${line.separator}"
+          failonerror="true">
+      <arg value="checkout" />
+      <arg value="${svnroot}/${current.loc}" />
+      <arg value="${basedir}" />
+    </exec>
+
+  </target>
+
+  <!-- *************** UTILITY TARGETS *************** -->
+
+  <target name="check.source">
+
+    <available property="source.exists"
+                   file="${basedir}/${tomcat.project}" type="dir" />
+
+  </target>
+
+  <target name="get.source" unless="source.exists">
+
+    <antcall target="checkout" />
+
+  </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..809fc9a
--- /dev/null
+++ b/build/resources/confinstall/server_1.xml
@@ -0,0 +1,77 @@
+<!-- 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">
+
+  <!-- Comment these entries out to disable JMX MBeans support used for the 
+       administration web application -->
+  <Listener className="org.apache.catalina.core.AprLifecycleListener" />
+  <Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" />
+  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
+  <Listener className="org.apache.catalina.storeconfig.StoreConfigLifecycleListener"/>
+
+  <!-- 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..f3ee0f1
--- /dev/null
+++ b/build/resources/confinstall/server_2.xml
@@ -0,0 +1,287 @@
+               maxHttpHeaderSize="8192"
+               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
+               enableLookups="false" redirectPort="8443" acceptCount="100"
+               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" maxHttpHeaderSize="8192"
+               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
+               enableLookups="false" disableUploadTimeout="true"
+               acceptCount="100" scheme="https" secure="true"
+               clientAuth="false" sslProtocol="TLS" />
+    -->
+
+    <!-- Define an AJP 1.3 Connector on port 8009 -->
+    <Connector port="8009" 
+               enableLookups="false" redirectPort="8443" 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" 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" jvmRoute="jvm1">         
+    --> 
+         
+    <!-- Define the top level container in our container hierarchy -->
+    <Engine name="Catalina" defaultHost="localhost">
+
+      <!-- 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"
+             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"
+             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"
+             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"
+             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" 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
+
+             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: (only for SimpleTcpReplicationManager)
+                            <%
+                            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"
+                 notifyListenersOnReplication="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"
+                ackTimeout="15000"/>
+
+            <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" />
+        -->
+
+        <!-- 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"/>
+        -->
+
+        <!-- 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.
+             This access log implementation is optimized for maximum performance,
+             but is hardcoded to support only the "common" and "combined" patterns.
+        -->
+        <!--
+        <Valve className="org.apache.catalina.valves.FastCommonAccessLogValve"
+                 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/logging.properties b/build/resources/logging.properties
new file mode 100644
index 0000000..08c293e
--- /dev/null
+++ b/build/resources/logging.properties
@@ -0,0 +1,55 @@
+handlers = 1catalina.org.apache.juli.FileHandler, 2localhost.org.apache.juli.FileHandler, 3manager.org.apache.juli.FileHandler, 4admin.org.apache.juli.FileHandler, 5host-manager.org.apache.juli.FileHandler, java.util.logging.ConsoleHandler
+
+.handlers = 1catalina.org.apache.juli.FileHandler, java.util.logging.ConsoleHandler
+
+############################################################
+# Handler specific properties.
+# Describes specific configuration info for Handlers.
+############################################################
+
+1catalina.org.apache.juli.FileHandler.level = FINE
+1catalina.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
+1catalina.org.apache.juli.FileHandler.prefix = catalina.
+
+2localhost.org.apache.juli.FileHandler.level = FINE
+2localhost.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
+2localhost.org.apache.juli.FileHandler.prefix = localhost.
+
+3manager.org.apache.juli.FileHandler.level = FINE
+3manager.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
+3manager.org.apache.juli.FileHandler.prefix = manager.
+
+4admin.org.apache.juli.FileHandler.level = FINE
+4admin.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
+4admin.org.apache.juli.FileHandler.prefix = admin.
+
+5host-manager.org.apache.juli.FileHandler.level = FINE
+5host-manager.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
+5host-manager.org.apache.juli.FileHandler.prefix = host-manager.
+
+java.util.logging.ConsoleHandler.level = FINE
+java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
+
+
+############################################################
+# Facility specific properties.
+# Provides extra control for each logger.
+############################################################
+
+org.apache.catalina.core.ContainerBase.[Catalina].[localhost].level = INFO
+org.apache.catalina.core.ContainerBase.[Catalina].[localhost].handlers = 2localhost.org.apache.juli.FileHandler
+
+org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].level = INFO
+org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].handlers = 3manager.org.apache.juli.FileHandler
+
+org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/admin].level = INFO
+org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/admin].handlers = 4admin.org.apache.juli.FileHandler
+
+org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager].level = INFO
+org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager].handlers = 5host-manager.org.apache.juli.FileHandler
+
+# For example, set the com.xyz.foo logger to only log SEVERE
+# messages:
+#org.apache.catalina.startup.ContextConfig.level = FINE
+#org.apache.catalina.startup.HostConfig.level = FINE
+#org.apache.catalina.session.ManagerBase.level = FINE
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..8d63ae2
--- /dev/null
+++ b/build/resources/mbeans/tomcat5-ant.xml
@@ -0,0 +1,279 @@
+<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"/>
+    </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">
+
+    <property name="catalina.useNaming" value="false" />
+
+<!--
+    <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=9080" 
+              code="org.apache.catalina.connector.Connector"
+              modeler="true">
+          <attribute name="port" value="9080" />
+       </mbean>
+
+
+<!-- Optional: when the context is created it'll create a host if none is found. -->
+<!-- Setting appBase will make use of auto deployment. Regular deployment can be used using
+     static declarations of MBeans for each of the individual contexts -->
+
+       <mbean name="${domain}:type=Host,host=localhost" 
+              code="org.apache.catalina.core.StandardHost" modeler="true">
+         <attribute name="name" value="localhost"/>
+         <attribute name="appBase" value="webapps"/>
+       </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" />
+         <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..4494b7e
--- /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..5e5c8ca
--- /dev/null
+++ b/build/resources/welcome.bin.html
@@ -0,0 +1,43 @@
+<!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://tomcat.apache.org/tomcat-5.5-doc/changelog.html">Changelog</a></li>
+<li><a href="http://tomcat.apache.org/tomcat-5.5-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><font color="red">Tomcat 5.5 requires JRE 5.0 by default.  Read the 
+RELEASE-NOTES and the RUNNING.txt file in the distribution for more details.
+</font></p>
+
+<p>Packaging Details (or "What Should I Download?")
+  <ul>
+    <li>apache-tomcat-[version].zip or .tar.gz: base distro, all non-embedded users download this.</li>
+    <li>apache-tomcat-[version].exe: Windows installer for Tomcat.  Please note that while this distribution includes the vast majority of the base distribution, some of the command-line scripts for launching Tomcat are not included.  This distribution is intended for those users planning to launch Tomcat through the Windows shortcuts or services.</li>
+    <li>apache-tomcat-[version]-admin.zip or .tar.gz: the Tomcat Administration webapp only.</li>
+    <li>apache-tomcat-[version]-compat.zip or .tar.gz: required in addition to the base distro for using Tomcat with a Java 1.4 environment.</li>
+    <li>apache-tomcat-[version]-deployer.zip or .tar.gz: the standalone Tomcat Web Application Deployer.</li>
+    <li>apache-tomcat-[version]-embed.zip or .tar.gz: for using Tomcat only as an embedded servlet container.</li>
+  </ul>
+</p>
+
+<P>Thank you for using <A href="http://tomcat.apache.org/">Tomcat</A>!. 
+</P>
+<P><B>The Apache Tomcat Project</B> <BR><A 
+href="http://tomcat.apache.org/">http://tomcat.apache.org/</A> </P>
+<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..2543490
--- /dev/null
+++ b/build/resources/welcome.main.html
@@ -0,0 +1,43 @@
+<!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://tomcat.apache.org/tomcat-5.5-doc/changelog.html">Changelog</a></li>
+<li><a href="http://tomcat.apache.org/tomcat-5.5-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><font color="red">Tomcat 5.5 requires JRE 5.0 by default.  Read the 
+RELEASE-NOTES and the RUNNING.txt file in the distribution for more details.
+</font></p>
+
+<p>Packaging Details (or "What Should I Download?")
+  <ul>
+    <li>apache-tomcat-[version].zip or .tar.gz: base distro, all non-embedded users download this.</li>
+    <li>apache-tomcat-[version].exe: Windows installer with base distro contents + Windows installation.</li>
+    <li>apache-tomcat-[version]-admin.zip or .tar.gz: the Tomcat Administration webapp only.</li>
+    <li>apache-tomcat-[version]-compat.zip or .tar.gz: required in addition to the base distro for using tomcat with a Java 1.4 environment.</li>
+    <li>apache-tomcat-[version]-deployer.zip or .tar.gz: the standalone Tomcat Web Application Deployer.</li>
+    <li>apache-tomcat-[version]-embed.zip or .tar.gz: for using Tomcat only as an embedded servlet container.</li>
+  </ul>
+</p>
+
+<P>Thank you for using <A href="http://tomcat.apache.org/">Tomcat</A>!. 
+</P>
+<P><B>The Apache Tomcat Project</B> <BR><A 
+href="http://tomcat.apache.org/">http://tomcat.apache.org/</A> </P>
+<P>
+<P></P></BODY></HTML>
diff --git a/build/sign.bat b/build/sign.bat
new file mode 100644
index 0000000..61bad1b
--- /dev/null
+++ b/build/sign.bat
@@ -0,0 +1,12 @@
+
+rem Example script to sign the entire release
+rem pass in your password as the first argument, then this script will
+rem sign all the files in the release directory
+
+rem todo - make one for unix as well, and avoid signing the .md5 files
+
+@echo off
+FOR /R %cd%\release\v5.5.19 %%i in (*.*) do (
+  echo Signing %%i
+  echo %1|gpg --passphrase-fd 0 -a -b %%i 
+)
diff --git a/build/tag.bat b/build/tag.bat
new file mode 100644
index 0000000..91b84c8
--- /dev/null
+++ b/build/tag.bat
@@ -0,0 +1,20 @@
+@echo off
+rem  Expects one argument, the tag name (no quotes needed, e.g. TOMCAT_5_5_13)
+
+rem  Build
+svn copy https://svn.apache.org/repos/asf/tomcat/build/tc5.5.x https://svn.apache.org/repos/asf/tomcat/build/tags/tc5.5.x/%1 -m "Tagging Tomcat version %1."
+
+rem  Connectors
+svn copy https://svn.apache.org/repos/asf/tomcat/connectors/trunk https://svn.apache.org/repos/asf/tomcat/connectors/tags/tc5.5.x/%1 -m "Tagging Tomcat version %1."
+
+rem  Container
+svn copy https://svn.apache.org/repos/asf/tomcat/container/tc5.5.x https://svn.apache.org/repos/asf/tomcat/container/tags/tc5.5.x/%1 -m "Tagging Tomcat version %1."
+
+rem  Jasper
+svn copy https://svn.apache.org/repos/asf/tomcat/jasper/tc5.5.x https://svn.apache.org/repos/asf/tomcat/jasper/tags/tc5.5.x/%1 -m "Tagging Tomcat version %1."
+
+rem  ServletAPI
+svn copy https://svn.apache.org/repos/asf/tomcat/servletapi/servlet2.4-jsp2.0-tc5.x https://svn.apache.org/repos/asf/tomcat/servletapi/tags/servlet2.4-jsp2.0-tc5.x/%1 -m "Tagging Tomcat version %1."
+
+rem  Site
+svn copy https://svn.apache.org/repos/asf/tomcat/site/trunk https://svn.apache.org/repos/asf/tomcat/site/tags/%1 -m "Tagging Tomcat version %1."
\ No newline at end of file
diff --git a/build/tomcat.nsi b/build/tomcat.nsi
new file mode 100644
index 0000000..6622563
--- /dev/null
+++ b/build/tomcat.nsi
@@ -0,0 +1,700 @@
+
+; 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-2005 The Apache Software Foundation"
+  VIAddVersionKey FileDescription "Apache Tomcat Installer"
+  VIAddVersionKey FileVersion "2.0"
+  VIAddVersionKey ProductVersion "@VERSION@"
+  VIAddVersionKey Comments "tomcat.apache.org"
+  VIAddVersionKey InternalName "apache-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 //MR//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
+  Page custom CheckUserType
+  !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 as a Windows service."
+    LangString DESC_SecTomcatCore ${LANG_ENGLISH} "Install the Tomcat Servlet container core."
+    LangString DESC_SecTomcatService ${LANG_ENGLISH} "Automatically start the Tomcat service when the computer is started. This requires Windows NT 4.0, Windows 2000 or Windows XP."
+    LangString DESC_SecTomcatNative ${LANG_ENGLISH} "Downloads and installs Tomcat native .dll for better performance and scalability in production environments."
+;    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)."
+;    LangString DESC_SecCompat ${LANG_ENGLISH} "Installs Java2™ compatibility package. This release of Apache Tomcat was packaged to run on J2SE 5.0 or later. It can be run on earlier JVMs by installng this package."
+
+  ;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 RO
+
+  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\tomcat-juli.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
+  File /r server\webapps\host-manager
+  SetOutPath $INSTDIR\webapps
+  File /r webapps\ROOT
+  SetOutPath $INSTDIR\conf\Catalina\localhost
+  File conf\Catalina\localhost\manager.xml
+  File conf\Catalina\localhost\host-manager.xml
+
+  Call configure
+  Call findJavaPath
+  Pop $2
+
+  IfSilent +2 0
+  !insertmacro MUI_INSTALLOPTIONS_READ $2 "jvm.ini" "Field 2" "State"
+
+  StrCpy "$JavaHome" $2
+  Call findJVMPath
+  Pop $2
+
+  DetailPrint "Using Jvm: $2"
+
+  InstallRetry:
+  ClearErrors
+  nsExec::ExecToLog '"$INSTDIR\bin\tomcat5.exe" //IS//Tomcat5 --DisplayName "Apache Tomcat" --Description "Apache Tomcat @VERSION@ Server - http://tomcat.apache.org/" --LogPath "$INSTDIR\logs" --Install "$INSTDIR\bin\tomcat5.exe" --Jvm "$2" --StartPath "$INSTDIR" --StopPath "$INSTDIR"'
+  Pop $0
+  StrCmp $0 "0" InstallOk
+    MessageBox MB_ABORTRETRYIGNORE|MB_ICONSTOP \
+      "Failed to install Tomcat5 service.$\r$\nCheck your settings and permissions$\r$\nIgnore and continue anyway (not recommended)?" \
+       /SD IDIGNORE IDIGNORE InstallOk IDRETRY InstallRetry
+  Quit
+  InstallOk:
+  ClearErrors
+
+SectionEnd
+
+Section "Service Startup" 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
+  Call findJVMPath
+  Pop $2
+
+  nsExec::ExecToLog '"$INSTDIR\bin\tomcat5.exe" //US//Tomcat5 --Startup auto'
+  ; Bahave like Apache Httpd (put the icon in try on login)
+  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Run" "ApacheTomcatMonitor" '"$INSTDIR\bin\tomcat5w.exe" //MS//Tomcat5'
+
+  ClearErrors
+
+SectionEnd
+
+Section "Native" SecTomcatNative
+
+  SectionIn 3
+
+  NSISdl::download /TIMEOUT=30000 http://tomcat.heanet.ie/native/1.1.4/binaries/win32/tcnative-1.dll $INSTDIR\bin\tcnative-1.dll
+  Pop $0
+  StrCmp $0 success success
+    SetDetailsView show
+    DetailPrint "download failed from http://tomcat.heanet.ie/native/1.1.4/binaries/win32/tcnative-1.dll: $0"
+  success:
+
+  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://tomcat.apache.org/"
+
+  CreateShortCut "$SMPROGRAMS\Apache Tomcat 5.5\Welcome.lnk" \
+                 "http://127.0.0.1:$R0/"
+
+;  IfFileExists "$INSTDIR\server\webapps\admin" 0 NoAdminApp
+;
+;  CreateShortCut "$SMPROGRAMS\Apache Tomcat 5.5\Tomcat Administration.lnk" \
+;                 "http://127.0.0.1:$R0/admin/"
+;NoAdminApp:
+
+  IfFileExists "$INSTDIR\server\webapps\manager" 0 NoManagerApp
+
+  CreateShortCut "$SMPROGRAMS\Apache Tomcat 5.5\Tomcat Manager.lnk" \
+                 "http://127.0.0.1:$R0/manager/html"
+
+NoManagerApp:
+
+  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 "Compatibility" SecCompat
+;
+;  SetOutPath $INSTDIR
+;  File /oname=bin\jmx.jar ..\compat\bin\jmx.jar
+;  File /oname=common\endorsed\xercesImpl.jar ..\compat\common\endorsed\xercesImpl.jar
+;  File /oname=common\endorsed\xml-apis.jar  ..\compat\common\endorsed\xml-apis.jar
+;
+;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#-Dcatalina.base=$INSTDIR#-Djava.endorsed.dirs=$INSTDIR\common\endorsed#-Djava.io.tmpdir=$INSTDIR\temp#-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager#-Djava.util.logging.config.file=$INSTDIR\conf\logging.properties" --StdOutput auto --StdError auto'
+
+  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 ${SecTomcatNative} $(DESC_SecTomcatNative)
+;  !insertmacro MUI_DESCRIPTION_TEXT ${SecTomcatSource} $(DESC_SecTomcatSource)
+;  !insertmacro MUI_DESCRIPTION_TEXT ${SecCompat} $(DESC_SecCompat)
+  !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
+
+
+; =====================
+; CheckUserType Function
+; =====================
+;
+; Check the user type, and warn if it's not an administrator.
+; Taken from Examples/UserInfo that ships with NSIS.
+Function CheckUserType
+  ClearErrors
+  UserInfo::GetName
+  IfErrors Win9x
+  Pop $0
+  UserInfo::GetAccountType
+  Pop $1
+  StrCmp $1 "Admin" 0 +3
+    ; This is OK, do nothing
+    Goto done
+
+    MessageBox MB_OK|MB_ICONEXCLAMATION 'Note: the current user is not an administrator. \
+               To run Tomcat as a Windows service, you must be an administrator. \
+               You can still run Tomcat from the command-line as this type of user.'
+    Goto done
+
+  Win9x:
+    # This one means you don't need to care about admin or
+    # not admin because Windows 9x doesn't either
+    MessageBox MB_OK "Error! This DLL can't run under Windows 9x!"
+
+  done:
+FunctionEnd
+
+
+; =====================
+; 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
+
+  ClearErrors
+  
+  ;Step one: Is this a JRE path (Program Files\Java\XXX)
+  StrCpy $1 "$JavaHome"
+  
+  StrCpy $2 "$1\bin\hotspot\jvm.dll"
+  IfFileExists "$2" FoundJvmDll
+  StrCpy $2 "$1\bin\server\jvm.dll"
+  IfFileExists "$2" FoundJvmDll
+  StrCpy $2 "$1\bin\client\jvm.dll"  
+  IfFileExists "$2" FoundJvmDll
+  StrCpy $2 "$1\bin\classic\jvm.dll"
+  IfFileExists "$2" FoundJvmDll
+
+  ;Step two: Is this a JDK path (Program Files\XXX\jre)
+  StrCpy $1 "$JavaHome\jre"
+  
+  StrCpy $2 "$1\bin\hotspot\jvm.dll"
+  IfFileExists "$2" FoundJvmDll
+  StrCpy $2 "$1\bin\server\jvm.dll"
+  IfFileExists "$2" FoundJvmDll
+  StrCpy $2 "$1\bin\client\jvm.dll"  
+  IfFileExists "$2" FoundJvmDll
+  StrCpy $2 "$1\bin\classic\jvm.dll"
+  IfFileExists "$2" FoundJvmDll
+
+  ClearErrors
+  ;Step tree: Read defaults from registry
+  
+  ReadRegStr $1 HKLM "SOFTWARE\JavaSoft\Java Runtime Environment" "CurrentVersion"
+  ReadRegStr $2 HKLM "SOFTWARE\JavaSoft\Java Runtime Environment\$1" "RuntimeLib"
+  
+  IfErrors 0 FoundJvmDll
+  StrCpy $2 ""
+
+  FoundJvmDll:
+  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|MB_ICONSTOP "No Java Virtual Machine found in folder:$\r$\n$3"
+  Quit
+NoErrors1:
+  StrCpy "$JavaHome" $3
+  Call findJVMPath
+  Pop $4
+  StrCmp $4 "" 0 NoErrors2
+  MessageBox MB_OK|MB_ICONSTOP "No Java Virtual Machine found in folder:$\r$\n$3"
+  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"
+
+  ; Stop Tomcat service monitor if running
+  nsExec::ExecToLog '"$INSTDIR\bin\tomcat5w.exe" //MQ//Tomcat5'
+  ; 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"
+  DeleteRegValue HKLM "Software\Microsoft\Windows\CurrentVersion\Run" "ApacheTomcatMonitor"
+  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\tomcat-docs"
+  RMDir /r "$INSTDIR\webapps\servlets-examples"
+  RMDir /r "$INSTDIR\webapps\jsp-examples"
+  RMDir /r "$INSTDIR\webapps\webdav"
+  RMDir /r "$INSTDIR\work"
+  RMDir /r "$INSTDIR\temp"
+  RMDir /r "$INSTDIR\src"
+  RMDir "$INSTDIR"
+
+  IfSilent Removed 0
+
+  ; 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
+    RMDir /r "$INSTDIR\webapps\ROOT" ; this would be skipped if the user hits no
+    RMDir "$INSTDIR\webapps"
+    Delete "$INSTDIR\*.*" 
+    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/.classpath b/connectors/.classpath
new file mode 100644
index 0000000..26eb640
--- /dev/null
+++ b/connectors/.classpath
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry excluding="org/apache/coyote/tomcat3/|org/apache/coyote/tomcat4/" kind="src" path="coyote/src/java"/>
+	<classpathentry kind="src" path="http11/src/java"/>
+	<classpathentry kind="src" path="jni/examples"/>
+	<classpathentry kind="src" path="jni/java"/>
+	<classpathentry kind="src" path="juli/src/java"/>
+	<classpathentry excluding="org/apache/tomcat/util/net/puretls/" kind="src" path="util/java"/>
+	<classpathentry kind="src" path="util/loader"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="var" path="TOMCAT_LIBS_BASE/commons-logging-1.0.4/commons-logging-api.jar"/>
+	<classpathentry kind="var" path="TOMCAT_LIBS_BASE/mx4j-3.0.1/lib/mx4j.jar"/>
+	<classpathentry kind="var" path="TOMCAT_LIBS_BASE/commons-modeler-1.1/commons-modeler.jar"/>
+	<classpathentry kind="var" path="TOMCAT_LIBS_BASE/commons-collections-3.1/commons-collections-3.1.jar"/>
+	<classpathentry kind="var" path="ANT_HOME/lib/ant.jar"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/connectors/.project b/connectors/.project
new file mode 100644
index 0000000..a63c22e
--- /dev/null
+++ b/connectors/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>connectors</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/connectors/KEYS b/connectors/KEYS
new file mode 100644
index 0000000..3b26bbe
--- /dev/null
+++ b/connectors/KEYS
@@ -0,0 +1,727 @@
+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/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-----
+
+pub  1024D/08C975E5 1999-04-14 Jim Jagielski <jim@apache.org>
+uid                            Jim Jagielski <jim@jaguNET.com>
+uid                            Jim Jagielski <jim@jimjag.com>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.2.1 (Darwin)
+
+mQGiBDcUl9QRBADl5tF8kOD0uddlnl9qsaG70/hwujGTsSXATnqoLseTsWORoVXf
+oBklokEAGmT2+Cl8XIXZ31Wh+GaJ3CTbEv8Ok1vapOt+ltPgOKzZEB4uP25EbhC2
+LWf+lUoafcd2Xi0KBV4fqXqEEuDGP1TAdZ6k7NVqgpjvbJ5TdqL0LrWOOwCg/0b4
++/p/avQr+uZRU2rdmYu/b/0D/2LnjcEqUjsslh2e9m0OgAu+gnYAmQH6Dbnp+iKl
+jffWPChwIMFZd/7FnGOzYDzoqnzTFyA4VE5PHWL61V2lpHJWB21K9D6rbEcx0iYB
+AHHxZQEmxSBU6PmGnbF+2P7vC0Jz9gZ5dCbjtGboYxd00/XQlZwCs8jHueTpSfx9
+n7dYBACFpW+v2pSlG0ReiS6Ult3gaGWiw81D0nFVvCp5BlxgQDymyF1MS6FbCj/g
+FGILosMhlsIHTFaC0DD0LSXyN1rm0ykPvi+vULIlKNJwW7fCi+33j1Azx+zfMNeO
+T5vqAfF6cvsZ6qPb9CcYvU4jEKvkovA1U3jMFehqcGkTV5sfvbQeSmltIEphZ2ll
+bHNraSA8amltQGFwYWNoZS5vcmc+iE4EEBECAA4ECwMCAQIZAQUCNxSX1QAKCRCL
+OmAfCMl15UklAKDq2PsXa7PbJPtGlXblJjD1OZgjTwCgkCz0EAdWS4Fuhi0mmSm7
+h1gtH/WIRgQQEQIABgUCOG/pBQAKCRAXWVXOQ77mqH+PAJ9dDpaaBXMgi3vGWOm9
+lYJW7u7ygwCgryMo/UAMLq1++TaIXP6+9aAD9PGIRgQQEQIABgUCPd09owAKCRAh
+RUrwzIsPfumzAJ9P9vDbz5dk0T8aPIcWeGY3q5U9WwCgwJnx7gXLt+k0eTr7mo1s
+/o1hvC2IRgQQEQIABgUCPdxFiQAKCRA0SoRNdR1/J0F1AJ4pqIkrFpHgtwwktfJN
+z/EvKYdlCACg0oFghrXkEC7JpBnYbGqXxb1f9xSIRgQSEQIABgUCPM7ulgAKCRBE
+NFayp7vgnD+kAJ4xS4K6DlOkpmNDhg8xKBT1EeYmvwCgprBsRuA7Op7cPegdvgiO
+ogQmjA2IRgQQEQIABgUCPdxEnQAKCRBVkeLAZmTAeB7vAKCkJ1Dr/pv4HqCKxnRU
+bxL/t06BQACgtHs2NqTZuDEp65k9hLroiT5zS8GIRgQQEQIABgUCPZzxgAAKCRBz
+uRv/obHYChzuAJ9fKiY6R0hFz6+xB3LX3AZ+O9Kf/ACcCzPtBJC6qknpOf3xg0xx
+gyIwZlaJARwEEAECAAYFAj0+5LgACgkQeNSOEHXh4lo+4wgAmYG1IaFDa5egQr5e
+T4Ew0jODmFyIyVJ6qdiNMXhQRNsNJ3TD5MuWpmXgc25ilBIF/x7DFnLf9XLTEtcC
+IkhgMMJ6phsuPt6lPfoSV42PTG9SqVtB/udiIiNed2NHlPdGh3zJEMIyMiywZGIh
+p23hP7jpxQQqfh6HchtLcxDDBETpGJJFIh1hx/Ps/ASvxDebnFKzNvdxlnNijJoC
+7wfP/ovkM0QJHQfctztMsmJ7LM1ZgQ4/XL3hM6GGRk2pJAoEaS0FDmrjWi58yY6f
+06/PaIYCzPhu7kz/uC8v3NlfJqGyr74BNkiC6hDKdceTAlEd1Pp7VfsyDigzP63X
+uOLILIhGBBMRAgAGBQI9pEHgAAoJEIvYLm8wuUtcRAUAn2hIyuND9XdZg4ttdIq+
+ytS6F1efAJwMuK2pkZZ98fgoBygiN+fImWHOa4kAlQMFED3cRGaazTzAqZ913QEB
+IBAD/00nrWc22FUeuCiTfwi0COY2JSv8zBjR3JQuloiqdiVlRdv14N2EldzDthhb
+2gaL/iU7xIbfOSk3DhDgDC+FvM2b07TuXXx7sksSP+PV7ubxSp6wNywv0XEqdDsV
+irTadFFvgQZ/Vlg0Dn11nRvEtul8xTfXI5MoD3Pbdp/ePey2iD8DBRA93ESn3bpk
+uiwxLS8RAt38AJ49/2yoSubOIMhEVVNqV4UKBM4p6gCdF/ZAZ7jtJac2O1eUlYuk
+mYufTbm0H0ppbSBKYWdpZWxza2kgPGppbUBqYWd1TkVULmNvbT6ISwQQEQIACwUC
+OsodagQLAwIBAAoJEIs6YB8IyXXlajkAoL2wNKsEorxLhZQAPRNa8kcv5uaCAKCc
+KvWB5TIgPvXc9KIyu7YwfYiLg4hGBBARAgAGBQI93T2lAAoJECFFSvDMiw9+NUcA
+n0K9Q9ePknWNHuQb4nkTve7kbYf2AJ9JR55QoQXLAC7y4jb9gxaqOdLMwYhGBBAR
+AgAGBQI9mcVrAAoJEEQ0VrKnu+CcznQAoIkcmrM+HAAzzEa7Nt0WFo0fLXc6AJ9P
+8javqUVxgWrx1pzYiOxI94xhL4hGBBARAgAGBQI93EncAAoJEFWR4sBmZMB4TN8A
+nRN3aMHwKTtDWJPRWHcNQfFgo5GPAJ45eMWtPyzO2DyX/W4LUq7SIQky9YhGBBAR
+AgAGBQI9nPGCAAoJEHO5G/+hsdgKxoUAnjKsp2V9in0fop9UA2Vbem+4cVYEAJ9b
+BnFkmkbUIzLhoC5RGe4RVXhWbIhGBBMRAgAGBQI9pEHiAAoJEIvYLm8wuUtcxokA
+niNbSTTL/lEMWtMN8AdOPsejHm5dAKCe9OF4wPI8gQa95IuMCkzaKBrO04kAlQMF
+ED3cRHmazTzAqZ913QEBJz4D/RJDlI4ji32MDqcCaaH1RISyRrvWUS99LC97OgcB
+/tbnEA+Yl/At0IwpYmJqff1tSsDHCi0QQbUtWAPhwoJ0gkqyfWFMkjd7Qwiu9mPC
+WYcGpUROne4y609uBkG/0g6t9WB/FZ80PmUTcrg1SBbx6HVqqOg89keWCo3h2asO
+mRYMiD8DBRA93ESw3bpkuiwxLS8RAiOiAKDyV9143Gqaspcnv3LbqBdl7l7iRgCg
++gmlTcPX9/HoxJgZAItwseZUfX20HkppbSBKYWdpZWxza2kgPGppbUBqaW1qYWcu
+Y29tPohcBBMRAgAcBQI9z/izAhsDBAsHAwIDFQIDAxYCAQIeAQIXgAAKCRCLOmAf
+CMl15W9wAKDjlNbPxfDyQMWEMAEJpSsWFJ2R3QCfbiFmNsNlFDKU8OObZCd8aZWs
+F2OIRgQQEQIABgUCPdxJ1AAKCRBVkeLAZmTAeOzsAKDqYkkBwxmnftHQXx//Vcpa
+aqLNzQCeODoA3Tx9SDvIugBd4lZiMruhgfuJAJUDBRA93ESFms08wKmfdd0BAZrx
+BACdG7OHGLy7nv7D5I09Nc/dhRZY4HRkPa0J19nTYUT0M4vGsH6mdBc49S/sDfmY
+Pt+4d5n8LGQb1DQ4WdCSq3pL+ZuNPKs2j86tilT4NTJwtjDnWEB2w1UZ1LsoXWOk
+HjSbf5EWKXgDJLUZdJxjeEmUshV+uw8UtutjTXjWYnsjkYg/AwUQPdxEud26ZLos
+MS0vEQIu4wCaA486Iwxc0WIteWo1/83j93XIZvoAoIX1B+ifAd+Af56INa39ymQ0
+eR9vuQINBDcUl9UQCAD2Qle3CH8IF3KiutapQvMF6PlTETlPtvFuuUs4INoBp1aj
+FOmPQFXz0AfGy0OplK33TGSGSfgMg71l6RfUodNQ+PVZX9x2Uk89PY3bzpnhV5JZ
+zf24rnRPxfx2vIPFRzBhznzJZv8V+bv9kV7HAarTW56NoKVyOtQa8L9GAFgr5fSI
+/VhOSdvNILSd5JEHNmszbDgNRR0PfIizHHxbLY7288kjwEPwpVsYjY67VYy4XTjT
+NP18F1dDox0YbN4zISy1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6ypUM2Zafq9AK
+UJsCRtMIPWakXUGfnHy9iUsiGSa6q6Jew1XpMgs7AAICCACEhzcRGEc3y3/4YNaG
+89FmtIRpFU5zoaZxxDrmUiS1HdhqFykv8ozaTyjfImCuhq8i6DG15oGudxPma7Ey
+sCcA/qmQEBVrXFK2DYTFW3UnPyqiE822plo0d45u1csKzPvGpHYVGC4HOEKCghRy
+/54nH0fsKV3VSlIXAhRG3LIstzAtslrSYELW1Lov53GK+YZpRDJTbLAxjIYB8kEY
+hiQYzHm/cbBeRpjG9BpoBQh54dNOj22CU8HC4KvZSnDcLAzmDyrQFXFfffvJtQ7+
+HH2iIWKMFOjpRHh2ZK6uhJb03Yo/v+admKs1HSEFdV5VJUCkqymhKT0OiWnXmNHq
+QUfliEYEGBECAAYFAjcUl9UACgkQizpgHwjJdeUwTgCeKiAx5Xdgs1/oLVjb6TwX
+eu3RL84AoMcX7B/EsPMLGyhBvB6whQAqBvse
+=s/wJ
+-----END PGP PUBLIC KEY BLOCK-----
+
+pub  1024D/33C60243 2004-09-12 Mark E D Thomas <markt@apache.org>
+     Key fingerprint = DCFD 35E0 BF8C A734 4752  DE8B 6FB2 1E89 33C6 0243
+sig         33C60243 2004-09-12   Mark E D Thomas <markt@apache.org>
+sub  2048g/0BECE548 2004-09-12
+sig         33C60243 2004-09-12   Mark E D Thomas <markt@apache.org>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.2.1 (MingW32)
+
+mQGiBEFEjegRBADocGttfROvtLGrTOW3xRqZHmFWybmEaI6jmnRdN/1gGXmb3wQL
+rHsS3fLFIIOYLPph0Kov9q4qNq36LekShIvjMBDFoj2/wRxaUtFq81asaRZg8Mcw
+4kVeIoe8OIOuWmvYhU8SH2jJNUnVVrpTPAa6QWquTmseNi6UJMjLxuL7DwCg//9u
+k2yj0vk6e4WSO6Fe5+EkQDED/AjQsy0kj9TpNHkKSSUR2evRlWPYA0YtxBSbsgON
+tT0cYipAp5IcYt6Zq5QzHiZreyQXLAjItDS2oGCIXfNbTYJ3kxxJTCU/3wlefVdq
+LBh4ttm7gmWaiTDTgG4axLF5oMpAb3m4v6s1KvXVVj2pqkhBknfuoRh1wPqbtwks
+7HOIBADVezl1/vny5YzdoqsDx1ByXMLi7CuMexQPllhRbdN+an+ZiJ5YP8J9rPdl
+NCELsCCcDKLGLjlp43XfMxsgYAPEZNG2ObjKTarhk3uGYN3aJrx7s+G+c2bu8o2n
+SyAFQ1iDsjS87PgSPCONA2/36ZShmv1OjLWz5Vo7hGSPcW4ZdLQiTWFyayBFIEQg
+VGhvbWFzIDxtYXJrdEBhcGFjaGUub3JnPohdBBARAgAdBQJBRI3oBwsJCAcDAgoC
+GQEFGwMAAAAFHgEAAAAACgkQb7IeiTPGAkOkvgCg0AcTAfe8m2ZSWkbsoqplLDsM
+0+oAoNl4EjXT+T2j2z8jdUYPaA8LztJguQINBEFEjekQCAD2Qle3CH8IF3Kiutap
+QvMF6PlTETlPtvFuuUs4INoBp1ajFOmPQFXz0AfGy0OplK33TGSGSfgMg71l6RfU
+odNQ+PVZX9x2Uk89PY3bzpnhV5JZzf24rnRPxfx2vIPFRzBhznzJZv8V+bv9kV7H
+AarTW56NoKVyOtQa8L9GAFgr5fSI/VhOSdvNILSd5JEHNmszbDgNRR0PfIizHHxb
+LY7288kjwEPwpVsYjY67VYy4XTjTNP18F1dDox0YbN4zISy1Kv884bEpQBgRjXyE
+pwpy1obEAxnIByl6ypUM2Zafq9AKUJsCRtMIPWakXUGfnHy9iUsiGSa6q6Jew1Xp
+Mgs7AAICCACuLSE3vBSOeTMM04ezuPt4zZUp0PFQGQL3bzuZp24f18S8P3BemGAk
+2V3HZYJmzmNgd4L0vIC9xyFduICFgbiV9uyzKPwHvCgQwaupFvFLGn7Q9LJ0nlaw
+GN7Km13vJTG3rrT/UMKwLTk+IMEYQwUgBht6HTnBaM+UqVx/eB4PHobimt5Redz9
+CnT4DrlA0M6Oh3ePWBD69Nnhwo2AN42dX/W2KcnDe2iRNu/JEbOYsssj0e3VmHwE
+mwa064TpQpw1fClyW7sf4aWOcQvcT12R0hNvRhTR1TV0pzjIMkbRPkRezhIY55AT
+TIfcaZrw+Yubmmw/pp/1wIDRzHexOq9riEwEGBECAAwFAkFEjekFGwwAAAAACgkQ
+b7IeiTPGAkN8ogCg4tHmgylXw4Y3ujF+J4cf2ollGa0AnRkyX8X+u/NrMi2g2xhE
+vpsTbAGW
+=r1gT
+-----END PGP PUBLIC KEY BLOCK-----
+
+pub   1024D/35C7E942 2006-06-28
+      Key fingerprint = 4DBE BB77 94DD A20A 01F4  482A 9038 4D9A 35C7 E942
+uid                  Rainer Jung (CODE SIGNING KEY) <rjung@apache.org>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.4 (FreeBSD)
+
+mQGiBESizvgRBACA8YC9nvnfrnaZ4UlGAxsr3MyZbV9yqW8gzVvCRZrZbwoXFoYk
+yWAj7ZOB1t2aCTBUI/l8mZiwlQ7uMz0jv4AKW7I9wViHu4yy60lH2TDspWy/g1fC
+AJiPsCCE6WcP7aQR1iciUgsmWsX0W0y8ykhfSocOjw+JTd2m0PRa/o53JwCg+6hb
+UGX/hOX+OIXh8xhT1jR65JcD/2ly45aNAI8Tik5Aqon29CvJ74ZlpQ/LiwMq+1FT
+8n+dfROkcRTpZsN6m0v2Vn9IMT1UaDr5jui2wR4YqegslGxN9q3r5wZyGi1Bg/dU
++npdD9D3PH5kAnIvn0uOleexfU3OxbJ5qXy2SVeDr6cGDIJ7vySGx5nbeqyWvCpw
+h3LzA/9QM7jwWN+mk5mmbNOKOE82IlGwKqAXU9qjguxUQZxkq3KZJhdodRGNCfSW
+UUTyK7mMfMYXzg0yLp4iVLXiN9eW379R0Q/wDWEnIUrP+8fmWhyA37YukVCqO4n0
+7VgcaBxuTpgX8nut+MPSW0fMPe1/aro+k1uFRonpo7ikdgbKxrQxUmFpbmVyIEp1
+bmcgKENPREUgU0lHTklORyBLRVkpIDxyanVuZ0BhcGFjaGUub3JnPohgBBMRAgAg
+BQJEos74AhsDBgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQkDhNmjXH6UJy6ACe
+POKL9N3T3MkdexuGv1Fc41aWQRcAn3GUI1TJ4wJDGCPAJMb16IrnxUdt
+=C30m
+-----END PGP PUBLIC KEY BLOCK-----
+
+Type bits /keyID    Date       User ID
+pub  1024D/E55B0D0E 2007/01/01 "Guenter Knauf" ("CODE SIGNING KEY") <fuankg@apache.org>
+     Key fingerprint = 3E6A C004 854F 3A7F 0356  6B59 2FF0 6894 E55B 0D0E
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.6 (MingW32)
+
+mQGiBEWZmwYRBAD0q6ODLHHR2D/8L7yANKOviQZwhRDOxfxIL3PoZ/xN/bMPeo8t
+wcv6Wh/VnIdz153kl2EkdYhCNbc+d0g/WMfFNe7ch8hqeYJKKQBj2fViS30ZLfju
+EwVesWzr7kUah2ETHC2Lo+vd0+x2yLsTriWUhK/ZU+85MTZRs5HX4e03NwCg5SpM
+iJp1pLwJGEt1zsAfIqUVfXcD/3uOlJamsYXmNGtEsndP3V67gsQxrleWWrLQzkt5
+WP/Myq5uY3XtIa0j6NE9TZtXwFQzVoeLQhpUg8bMuPRg3VzB5oq3ClkS09w38xyF
+eEZlNjeJg9TfvDk4bYpLKHtJeFn3pFR13Ojz+r7AHo/wbcO/Jho6XXLJPCjeMBFg
+MVagA/oDalIMI9cUVmM5In2WexaGuYqOaLD/2CEpNpL2zppv/5ES75oU1I6bNVw8
+jVI5pHIVn7wTH4ja3GfK238uncqot4SM6DxdW1F4Gj4cKG1YXyPbD+kqqX4IrzFW
+VL92VtA7LJdt2t/z+OFg2StDKiKtpGdSGcWEimDu7RapK251d7Q4Ikd1ZW50ZXIg
+S25hdWYiICgiQ09ERSBTSUdOSU5HIEtFWSIpIDxmdWFua2dAYXBhY2hlLm9yZz6I
+ZgQTEQIAJgUCRZmbBgIbAwUJCWYBgAYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJ
+EC/waJTlWw0OzdYAnR7vfP8ygtxJ0d7zMxM+LphquxSJAKCCcOTJ4VO0NnidBb/n
+FHN99o9pdbkEDQRFmZtOEBAAwlQPuku80T82g6aS1HpnsXyzxG+k1k0Nj66ZvRM0
+f8gQKmGanVTsbmjXRD1Q0N/WR3F5ynrk+dvgdN0/IqBxR+Ek6L0hYFqa0Mp8Rqw7
+0M7oUI5iwMa/873koCnBhFet6Eu91mVPOSE9mdVIw/vlq8KT8VuxcqpvUKYgjC9h
+HK3GQniaR4JKkCQjk4ZynQiDckKcfaR765GYvw3Fq+v92Ebrdu5qYh4Tq2ncEY6q
+fCcxTu6THXNPVgX+A+5ASlcLAxMAD1W25//abwPdvBIZ/76nXXGnZI4NQDQQf1W3
+btrcHxQUXrvHM1DTV70wlifIA3xjuWTIRr31gXv5QVrVQdn58AhWoHB2eSGX/jhH
+zQfghS/FkBIt//THDNDYhNtqHLz6SihMHNDIUcBNNZDF1tO2slsrAtKkooX1J91l
+BtWRWwkU3VK1HttMhAB5xUswbn3XExKK/J7IiI2M+Qz/QIKVIWcPyYmPBUkwtNtV
+RiGPcq2OMF2BEKRVyCZa1rQuPNRLv2iuFD7iaRfMMakWGIY2uIeLAy3KNS6VbH0q
+a7LUfeUv0Be2yyEAr5luMwomWzAHpW4xhCLG0kjMiNCceTJwASG6eTg/JpWxcXca
+8M0ZvcbVBZ7nzvwDRBCtH0W+9w+gNwgtToE4UnglrO4TsXtWwOyLfv+GaVQIngqH
+4lsAAwUQAL/VaXfDuLOe1j3jn1sKjDIfIHzTS0THdxpP3B0WahZ7xHAtuFdd8jXi
+CmIGi9hKO99Mn5xsW3/cEPVfTjkXbfsgUc5xHsC8zJ9BCHctkgPvaHUJ7lAyZbW+
+y+CrW+dpCN49Y8H50NhwjiHiwT7GynNKnOxMFg/aZKLBVFFOzjqtDQ8qjiPdgAld
+O/tV7/WV4HTZvGUqQloEqqMjWTJO+fw9Mau/ejvBBuz/P78XkCku8cST6hjg89FX
+gyB0J7TL3ykvJltIS1gBNK6BF3N2es4qIaT/MUUbB5wdpltYt1HZHBUtvF8ywySu
+Tu3ymyYfLQdRt+e32tMWgbWL00d2YJDeEOFeylBbOErzHI+tqxAQlwigKzHyUQkN
+Kel2MRmdsaiHMlc8Btj6YsFTf+sXnLo/zkq3BLVJd9qRS+udbbT02TIVhKybbJbx
+tgg5ajhDzOuRHb+RhpqjAaM6HGM2Vv1y3OLSzAN981LyEKgqIG2cwmNmubQLgwi1
+UDhcrTLlaXpFB+SHpvZPsnBQ329YKurlIV/rL38ZajIIMYPmk6bZIqTW0cwy7aHL
+XgBEgIpUjHDBOnq80rcyc4dauctgU1muYFh6y47yqtxmTkHu5nwTbZEn6/l1/Ffo
+GCRMkLtwRdDm/4BfuuPExBNkplzglJ/cMAvvr0/mwv+tqvQTT0CMiE8EGBECAA8F
+AkWZm04CGwwFCQlmAYAACgkQL/BolOVbDQ4R3ACdHuJ0qhGNF2HmkKxkEvyQ8XYB
+KGYAoI/VBpQ3XajbV/CPs3YP4qBMddA/
+=LiRc
+-----END PGP PUBLIC KEY BLOCK-----
+
+
+pub   1024D/6210BFC0 2007-03-01
+      Key fingerprint = 3A6F 081D DFD3 DE93 02C3  1329 0F45 0A26 6210 BFC0
+uid                  Jean-Frederic Clere <jfclere@apache.org>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.6 (FreeBSD)
+
+mQGiBEAzyJIRBADK4dV4yXWrn/Y8QkbrRVIK8Tzct2UY/OXxYfIv/Tfqxi9h448d
+lkDxjFWjnyOc1m1M6pU500o/KIeray6MOQM/zVBPo7iyv+FC7Rk5kczpcbu7EbS6
+nzOJaSVS8ExighNo5IACGcGP+Xs3p8CkHaHhxgKz4c1UX/u+hdabON4wZwCgrw6K
+u7BuceEkEtNmT5FQC6SGFG8D/ixq6PVUg5vSFM592vdgFyqO9QRvffDe7dHTCA57
+PnSFbRSlpM08DsxmiMyUiZkwtVLvBJggOG2JJlq459xCUVFAw0OcPl+H5+DVU4HE
+S0BWUL+i79SO+uuxl4EZ22q7uFyFII7AKDi+p5Ft88Y1CCNAtQdyGdxjcK27cC2Y
+TZEZA/4yfGD0iPwYJo9je1XKmFV+tgjHKq/CNt4iEDU7hwmG642h1LBLDCXp4+F0
+cbNg4q8txjVW0ndpwv6/y+0yFTENdeBMhMEVgpsK70/1TNf8kf0dsv9129LNQ2Cs
+6BfybRnQSKO9d6T9Lo4Zg2t6h3KOkT5dqj3MHaBUdVrDSHRXTIhSBCARAgASBQJF
+ZC30Cx0CUEMgc3RvbGVuAAoJEPs32kAzLmPNblsAn2bWzVQiIMJkFXSr7hGThBkW
+9CnoAKCY8TW1FFaaD9BEcuxrZjCAnp1z6bQoSmVhbi1GcmVkZXJpYyBDbGVyZSA8
+amZjbGVyZUBhcGFjaGUub3JnPoheBBMRAgAeBQJAM8iSAhsDBgsJCAcDAgMVAgMD
+FgIBAh4BAheAAAoJEPs32kAzLmPNFQwAniz4ReBMjflXPuj/FMKSSsyeJjQuAJ0T
+wWVBk9PTB7cvWZyT0AurUFPs5IicBBABAgAGBQJDYgMzAAoJEDfj9F/uZeMhn/8D
+/2xuhUHiI7tlHQp2tQ8X5/JxmErDc6Kb7zQiCLAft+lGrJV5nLah05HOOtxYLP2o
+kv8c1Zzbsa/C3k9v7BGg8+5vDfN89Bj2iCLb19G01O3pGqE0B1G1L2arLsysoW4b
+1YrZIwqsEUkz3rR3ZNtI6M/z7WJboao6uWdpMjeWqMQdiEYEEBECAAYFAkSi3bsA
+CgkQ9cJgFkzu11/4hgCfbvtk4qfyvo+jzaoj5u73suaQ8rEAn1L4lQECQe9blVWJ
+LrFohPHBjUcJiEYEEBECAAYFAkSjAv0ACgkQlFBD9TXBAPCvVwCfYtR8GRKbudzU
+YZRGBaYj6wXfc1sAoMsTuSm2cx5OBOiW6A0WHuXtr3ZUiEYEEBECAAYFAkSjnLIA
+CgkQLSGrZWVGG3loNACfbdRmrrD3dJXZc6peaJvBNtITDVMAnAmCu9p+p/+a1Yps
+TiuCuyRso9m5iEYEEBECAAYFAkSj9PEACgkQN/aP9QFa/IpmigCg0Ww8CYMRVBv7
+ncOTlvXOkE1yyrkAoMGb4A14TFxoQTx9rmDFh2+rWojRiEYEEBECAAYFAkSnztsA
+CgkQ1TNOdbExPeIsugCgyuyNjR1fCib9WBETW5EfZFk17AUAn3cMSj/jnfXft3qB
+4eap4X/JiyBiiEYEEBECAAYFAkSpMGoACgkQMsnkzjZCy0vymgCff2fuOEmRJGLs
+D5mxgS6oax66BzQAnje+9oq6le3fKMX3VvyjrQdrP45+iEYEEBECAAYFAkSqsTsA
+CgkQNYuqM1D5YRZwXQCdFHcDmoi2lK5guXZicaBLY4zRhL4An2H+k5bal1hA6xZm
+pvWUuZyphNTtiEYEEBECAAYFAkUPgtsACgkQsr68QBUpJK8yYwCgz/MUW/injaht
+jWRl/xthjPwQ9jYAn1dLFLRJaAhU+d/EpmmAzdtz9GgQiEYEERECAAYFAkSjHO4A
+CgkQVg0IYe59x062kwCcCGwnfHR9vrTpTEMMsxivaLYZoboAnAr60mqfvtNM1UNv
+hEedDHmZRRVCiEYEERECAAYFAkSjHP0ACgkQynKdYmA9T1ShsgCgnrNGFIFuQEl5
+NAHsLiL7SHsPZgAAniE/Ff9pFF8A/90zxIwbrDslBJoGiJwEEwECAAYFAkSjqhsA
+CgkQms08wKmfdd0bTAQAyImsO6JYo//1zsoat+alQUjHEYLUWAggRQJAfmWbuW/T
+Vo+H/mOhKA7iDJ5uj9ExIAv5CwLQ5Tg909DQ8MMov94RMw1Sn/NjermzoDqP7nmW
+nVpVg1+BgQz5Ji3oIH6sTtIUozGTpB+fSmkXm7fDUKiB/1bEHNyf4PqjvJbrhtmI
+RgQTEQIABgUCRKOqIgAKCRDdumS6LDEtL+x5AKD8h7qqWvYNxS7TzTETbF92WNBo
+FACg5q7jqPYRXr/V1JasNAl7PZqpymCIRgQTEQIABgUCRKOqMAAKCRBQjq7FMC2l
+aKu6AJ4ujKZ3Fd9aW2btic+uYnyRGg7xAACcD4fv3S8u1eqJz0PGKH/dLpaPDHKI
+RgQTEQIABgUCRKYATwAKCRBMBCgYMRo95QM0AKCJNquhe46m8reJUUMBepYeCsmC
+mACfbGSSJFbU2flAdfpLnyvmr6phPB+4iwRAM8jyAQQAuHeAOvCiADEmN+dVkPXm
+P94SEvbOSQzn/34X9LTIlXRiIOXk9+ZoZnpaV2uIT+ItJki/fCCp+CdNuVpZ1YMS
+Y24mR8aPYoSgoI884tz/8fMgz02Lr5FPpMRW8R8rLte53p5DHL7Hv4D1Z1kE2yaN
+iBiH9sUZ7Ex3EQv3eekreEkABimISQQYEQIACQUCQDPI8gIbDAAKCRD7N9pAMy5j
+zUeRAJ44QklQSHL39ni2k3+YUyg/+TlthwCgqd/hn4yAhG8/AAlwjppHhRrcZq2Z
+AaIEQC/CrhEEALB7z1RJSreS8HrYONKIx/i+owXCSpdPCkATg5j0Gd3b9f4h1bux
+k/7usQVZCua4O5/C6caCoW4UgjRBk149JC+n2wLt/KeQkFeGr2H7y+FuPJuuBCDU
+6Xm/KgIr1glDisLjD7d1NI7eTxAdfs+rlsusWYAgDebAf/ZSaGvs8PobAKCpdYM4
+NRiYl60KpFvJ67960zp55QP/SsQ844FvnELZnbcrynxwhfLJIzFky2cLCfNK7nlX
+r1zCxQB7SWAoVjWbd13bxhEtmAled2r5ci07cPr97CXCg0lInhFEjLqEIm4uoBKM
+e4q2qvVsQC0PbYU1A3H6XjxOy4tourmorT1A/O02RSGJ7321ny5otpBFjGQaCbtS
+NHkEAIzX7RWLDLCWLJ0iNOTKKzcvjrxeLWmz/1WOei7Az9O9zBdXGIYWA3U0UPnz
+E5mxNZi35F+RqcmSxQXbQhgKQ63c54c8RlVYumTYBmDKAoEFL1uhYSl2oA4zVAMB
+Sm6zCFsAFGdTIoN/VOkuQI5KaYi2nhi9UyJqdPBD27+e5inKtCdKZWFuLWZyZWRl
+cmljIENsZXJlIDxqZmNsZXJlQHNpbml4Lm5ldD6IWwQTEQIAGwUCQC/CrgYLCQgH
+AwIDFQIDAxYCAQIeAQIXgAAKCRD4wwzmNuZ6lGrFAJ91qqQz1OlOZuKBEaS9hVq+
+Hf/yNACgpmujwA4wWegMW10nF+SEg/AWm1uInAQTAQIABgUCQDHNBgAKCRA34/Rf
+7mXjIaYrA/9zT5pKPLrawlEr5jpbc/cc/UcZ48uJtNUctfcjkO3+VgFWMdWAAMXY
+LxzPmgggQ1l/IIxp6tRj/KWULu/DRfqDbxc98toyKXnyQG/FlOQqt6oXuqbc28Ae
+wlTlQnQRyGdGjZidRWraipZt1ila4xeeBhNwNdb08AC53Q/PQFdGprkBDQRAL8Kz
+EAQAi87yfcGyMDdZWwzXO24tfNmn8nWkSjNOXPcGAHY+vWLF/whufOWYWXbK8YV0
+l2Sll+Z0yMLtPE6sbr7kxYRKesNwRkFCNL3adO9wbdq4pBKxLhFWiMvNkzB/OcHa
+76mAtUSzexe7pgbLPfd2JvoHsai7FlufqNxhKW1E5sXBLVsAAwUD/RzUbwyE0i09
+TO5fFA2YtjaFm5GQiacQi8yQapxhW7ajOZMy66qqWxPq1xxA4WJWMJgnvctZNKv2
+FyH8Pd5Ghkr+tM74d1HblIzUJyChh28Km4Bd6fG/NW30xPiZQtjI/8a6QCNUAJbc
+baCvzzd8oz1+ogsN8uc7NQwodiiuRYk8iEYEGBECAAYFAkAvwrMACgkQ+MMM5jbm
+epQCOQCePlE/HzrLaAs1IlqYsvzIzaExZSoAniCz7BeXwtJjMfNxrvVItnOo0rCg
+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
+ZW1lbnMuY29tPohGBBARAgAGBQJAEVHcAAoJEFd7hHVGA0DQsUsAoK2SuAnTybek
+AJkFmei/kbv59fA+AJ4/smxUz6EGuNmWOYwnHTxPz09c4IhXBBMRAgAXBQI7/Rsf
+BQsHCgMEAxUDAgMWAgECF4AACgkQ0+/mtoaGe6amGQCeJU5VZ8QCi8+PY0QJHPA6
+3e5uPyoAmgOWIwFm8A/xmW8qjEvVAWtbTjZxiJwEEwECAAYFAj/F+7gACgkQN+P0
+X+5l4yHDEwQAwp2yr9mwHIti5D2tvgNDQjOw9K/qbGbQ1IH3f/BLF0J5EHxYxUSt
+E0glh8GPJOi1U+0Jv999W/8xnOs5Tf16d0DmhscXvylxWYGis1PcoqA2zemaWuVM
+nBoJaA89gPiwconEEvdxym7vBGK1Lodz+ote2doiuCsWSjcfl74LFxeInAQTAQIA
+BgUCP8dbpwAKCRArAsNF4kSQGcqYA/9ZkL7Uwy1xZ8EPAQT6eXVCmS743C3Mc2kr
+Cv8CEnsVNQrsQMYzkU2OjsJ09semmIHnvpx14i8cGlLRoKpfNn7V+O6wmMEHr2zn
+HlMtDsgvk8FK12cDGGxUlHr9sz6wOTQyZG3bNLA9iQPUlHAjTTZlISx55i3/opKQ
+yI5LzOMaSrkBDQQ7/RsjEAQAgmxZUhfrnVv0M9qJJw3p/B6KpeENUOwlEE2z46EA
+HGIGYC67EeoXPWgiH9yreKmQYENTnvTPwHpjhYVNOFDad3YB5PdRGEb8fNEbmvfd
+qf/Fe0DocCAVW9lxPGrOZy9o/MOic+3krP24r8Zvcg9AB/yOR9NUQCYr0Wzk7FIE
+pA8AAwUD+QEZVtjanNFNe2rELJjGL09VDIjosgcFA/wyBBtl8tYZRL3ZY4PFlNuo
+3I6RqjK0+vU+GfnmdqFcokUE1uKjFASnNDW8/PYS9Kr/SuNRMccR/iYBNXLEVb5s
+AE/CMpZ4aH+0yS0Qbb2p9/qdtSsZdbkOzG5i0/O+bvr3PidPfHyIiEYEGBECAAYF
+Ajv9GyMACgkQ0+/mtoaGe6Z/VACfX9EytEEDrY0Ab8QIrLKKWIQyhxgAnj2YNJPb
+I+vcsJ1VqwUvX7xQvY3AmI0DNi9rBwAAAQQA7+Us5BVixRL6XvJqf5HOvR6F9ZGq
+WB42xXgNDTFO5B7AP0rmWbu6NHC63GU1p/l1eMTbiaWyJE3woOn58rCbta2q/j3T
+GaHNL/rLx6TAHTY/h+L8kk1fU9stLRwvr5vPZ0WosOGHTc6/PcbXpj2F7masnY80
+5Zr0N+P0X+5l4yEABRG0Ik1hcnRpbiBLcmFlbWVyIDxtYXJ0aW5AYXBhY2hlLm9y
+Zz6IPwMFEEBfU3vMsutG52z20BECVYEAn1YYChSD4JjSugTnQ/XFI83ysPLuAKCd
+Mp5S8p30T85NjNSPAFSHfdZ++Yg/AwUQQeug+NvSRfyzsqEsEQKVgwCfV6HiL9+x
+jOX8rMla6npW+/ApQ5YAoO7Q2RvwUDfFNRuVwKCtKKFwi+F9iEYEEBECAAYFAjhv
+6QUACgkQF1lVzkO+5qgJ7wCglwijjcMKmq7F50Yc1+rpktcbYqgAnivId+oJ9iwi
+/18y6Y6t5zxk53i8iEYEEBECAAYFAj6kVw0ACgkQMYbNBE8J8FWYcwCg7jxg3ZlA
+FW0ZoqdhBmmp6RrOwe8AoK6auKAwEGGFSJmrlcsuiFX0ZTZsiEYEEBECAAYFAj+/
+rZMACgkQsr68QBUpJK+o7gCg8FyvVvioZtTgAnqR4sAY6obNINIAn1WQh/Dd3PQV
+lDc73VpdUmP+b+4riEYEEBECAAYFAj+/rcEACgkQwR57R5ZPMdmfmgCffg8AxHb7
+noRXibKHJe+XWp5R1/cAn0pmS1swwp1xEjxvjbdMypTgfFUHiEYEEBECAAYFAj/B
+I+cACgkQGgnRg/0JPEHAGgCgtCXyxA9p00dDGUQ4T1UO9Mcp3Q4AoKUm5+cqtH5C
+pZ3IuPehZ1o83GUfiEYEEBECAAYFAj/CGFkACgkQidhMr0ayYsaonwCfRsVVtkKx
+oXq22dET6Sbk59w7ic8An06p4Z6ubNIRFVWo5jK4gFWJ1ixViEYEEBECAAYFAj/G
+INMACgkQbQvHOkBYGDe2nwCfUvTp8jFFGazZUWn6MmYRwHPzEZoAoIp9/q9tWarF
+tVAZwCF3eRAYw5ONiEYEEBECAAYFAj/Lw64ACgkQpFkk8La0Xx+FLQCfeSZQxjQd
+THY1g7ZfdeBIjg8gnY4AoLYUr2I1lWJA3kUVsM2I2pmXB7utiEYEEBECAAYFAj/d
+6dAACgkQ0+/mtoaGe6brHwCfVonKGuut8boMEOU8sGtJoaRB15QAn1ldSO7wASUf
+7wqa3EUrFTBNjpbOiEYEEBECAAYFAkLe0lYACgkQymk5dIEDo37XXwCgph3WPWCS
+z144ktywff3BD7cyGwAAmwfwU8UZAX2rSNMuYemIa3w63WqaiEYEEBECAAYFAkLf
+QPIACgkQQeoJoFeTSY9dQwCeMU1qXnSEG8cTvsu14sRcBLtaV4QAoPHKDZS8kEVy
+UEcjDhXTUNiNK6MhiEYEEBECAAYFAkLfkkoACgkQAQVmvOQTY5JEJwCfT/UGfYmS
+mZ9e7m8P+4x2NToxdhYAn18ZNQG2Q83N8RYKbVgUNC0iv6sxiEYEEBECAAYFAkLg
+1y0ACgkQjON2uBzUhh9CmQCeO9D3OzllrMRWdYjl/TBHwlMvc/4Amwbq5Z5wlbVG
+U/R2Yp2NsbybPJPAiEYEEBECAAYFAkLjhSwACgkQyXxQllwcOtdOTwCcDf92siPF
+rItlE75OdkXyWRGgJoQAnjyFMrxVpErxhpOTjIWhQI493eQdiEYEEBECAAYFAkLj
+pRwACgkQBJE0Quobo41cGACfcTmPvrPayI6fKmUIwDcbKQXsRA0AoJO0k+tkF9Jp
+9f7LCpbvEiYZ+A9viEYEEBECAAYFAkLkhAcACgkQYRlqLjM+ToRF1gCffqxtQ3Nr
+JrvsprdSJuUkH0p643gAnRgB2T2Lc9YIJzEIxC5dZvmK4Hd/iEYEEhECAAYFAj3i
+UNoACgkQiNfNvfQ8L5L+qwCfdgPwwfKJxDGH7S+8llIfUtQAsRMAoI4J55ch47lh
+ayilgtr/MzqoS8k/iEYEEhECAAYFAj+53/cACgkQuSRSrEyRZbaJrwCeOVTRdXKe
+HI8NZGbEKIpBSD2942IAn2jD5cP1e6iZQBp/KcxFSs1BPGzLiEYEEhECAAYFAj+/
+/KQACgkQ7Jk9Y2X9ze77JgCfYMYZSvfrfmiT6sEakpM1BKFF5yQAn0xk37icMlRt
+d0z6YX7IwQjG0qQSiEYEEhECAAYFAj/BU9sACgkQzCEwIKHWl1lVMQCfdTs5GZS6
+l2UceNEexsOQMVKtHwAAn2VKxs3WTKBYeFS5l7PVMIzyJKkPiEYEEhECAAYFAkAw
+HfoACgkQLovQgMxpzu1kbwCfUmYmb5clHPHSMHF98plowgP0k0cAoKATY8YJlyG9
+oWflYryeAXJtwBMziEYEExECAAYFAj2kRFUACgkQi9gubzC5S1zRIACeImAkzwdt
+/xZZu6ja6FJehlV9vAkAn0a6K3WHkj/iu0EsZOCC/DwBG0iTiEYEExECAAYFAj+5
+pRMACgkQEPpMiKVNot+xugCeJ98eHsR5SWidG9GqZaOyIXs6ocwAoJmcs5cqZ4s9
+QoFhSmsjMzXWL+bhiEYEExECAAYFAj+5q9YACgkQqB7mSc4Z1cY0nQCfV+AqxCMF
+n0giccSDF9e4EUGZZi4AoLAvh6iQuWrB+gohKn2dv5DRxVpYiEYEExECAAYFAj+5
+wvYACgkQyzKhB4jDpaX2cQCfSx4yFkoJvJEmA6faBkggxB8cRLkAn0t1PJ2ujChD
+obTDZyQTGbHaH6RRiEYEExECAAYFAj+6g54ACgkQlxayKTuqOuDBXgCdE6UopS7i
+xaU0PlutyqcEE6HLlaMAn3tjDl+8GTNjFa3i/yI9VGssa2L+iEYEExECAAYFAj+6
+mGsACgkQXP03+sx4yJNz/QCgg3v/aY1/FiWX7stgNWVYb/NYQ3MAn07lLMFj+/uc
+cnq24P5URPB4iKsGiEYEExECAAYFAj+90X4ACgkQvrorh/X8S0IVwgCguyjhA/9g
+kXcy9BvRQPQqCLEzkhYAn3WA9UD4caq7Nxu8FGy5ZJtdb/rEiEYEExECAAYFAj/C
+rzIACgkQCVpF3KEdVvvxXACfdPcOV8uwAt/WnYWZvwzgCNKbiqsAoMj/vW1wisf1
+I5edoRRh2f92uFFyiEYEExECAAYFAj/Hb+QACgkQ+jyNJhQrUJtvoACdE49VSa6t
+Ogzfq8ZDpQFTMW1vTT4AnAgQZ7pbJOt/H3jvGjqAHwYvkDR+iEYEExECAAYFAj/H
+2GYACgkQpHQPOdFHt3bpMACdF09EksOB99yIjz0gPPEj85I4UnEAoIbdIOl0XczZ
+WlElR1vTzwyw6TQFiEYEExECAAYFAj/K6BQACgkQaFp3yu5WVQ4+UQCgjy2PP532
+3EF0l22OvnC/HX1N1mQAoKSV9xEv9Ph4iQLfCtpclV7QHvDZiEYEExECAAYFAj/N
+3wsACgkQEy5J1OQe3H7SjgCgn/PJkIb0XOhftNId65Mig6KybRgAnR28oSAqRyYd
+RcAHH3OcMKhnKj71iEYEExECAAYFAkAV2tcACgkQFT+gzXWmdpJ1oQCeM0Bsk/kZ
+EcV394DeA2I2/CEzd6AAn1LzhlMUM2rVrus3hmw3xFfWYGcyiEYEExECAAYFAkGO
+7MsACgkQW5aAEOBPmomRuACgiQqAy1QMOzB5ypaL3ZSho9gbYJsAn1lWiE4iqj4B
+3v7UhF0OLpv8eiatiEYEExECAAYFAkLepXcACgkQi5YpQ/wkPzyduACfaH4wAwRL
+BwNiTeiP1SJIh94rOFoAnRn1WB1RjWh7kpvnsvx0hptnPme5iEYEExECAAYFAkLe
+r8cACgkQ/W+IxiHQpxuFBQCgxaQotDXe40gwkP9AWdnI/I5IU/IAoLmLc38Xq8B0
+0G/2gkjrs/Di0YqoiEYEExECAAYFAkLe0o8ACgkQ7tHqOSJh0HMsFACcC1SE534S
+u0ruTnYqkwag32izcJAAnjXp+3kt3z7HmW3SjC13ih9lD5qWiEYEExECAAYFAkLf
+bSoACgkQUI6uxTAtpWg1qwCdEkVZazODnO3EYPp3pb9SJIX2QBwAoJrRAhQSsHA6
+SZMbzrO1qXZcrzRQiEYEExECAAYFAkLfbSsACgkQ3bpkuiwxLS+2fgCfaovgnDdT
+9mAYvlUPuMG/OU4PBfQAn2+gnx5lPYoGbzqHZ+tB+tOEB9O0iEYEExECAAYFAkLg
+7JIACgkQybWm7OLXdN9WjgCffqBC9I2jg2Y7RzR6OsHneFbHDzQAoJwke8F2zfWd
+MJ871QuwAr1AIWitiEYEExECAAYFAkLiYa4ACgkQbZiNF4cxXDG1WwCeJ7ruGatR
+Fa1H49akQP1jQ05tMSUAoMd/v+N4r5G9GJLrvY3KrNWas1i2iEYEExECAAYFAkLj
+ZH8ACgkQdcqio/ObN1BMMACeM9dBoVGR2ayYXauJnMxHAsoiHGwAmgIlJ3CzGZ9j
+4/8FUetJlzd3hTaBiEYEExECAAYFAkLj6zgACgkQ8gLLgpYclGAUEQCguHgPduC0
+Hl+xQaC1RzJbbzi4SPwAniuFfYBUHxQyXNn1eRIPiebvnyIaiEYEExECAAYFAkLk
+nWIACgkQMoZOQZyFIisvBQCfQNu26oDdfydlcamtsGcNI3NCxLYAn3xBoUFBRGHx
+BsksFVW/Lu/zR+qPiEYEExECAAYFAkLlEHMACgkQUnkvr5l4r4ZVfQCeJyCO7KNA
+vS7Gskhxm5AXyoUBYqYAoOIucjFcefJ+XT7h7IVVZaXustgIiEYEExECAAYFAkLl
+EWoACgkQa3OhBipiP3K3wQCfT0qSslV61+vOKSLP2DBmFOypxggAoKItqEAlE3D4
+cfxrvwhcUPqYmMyciEYEExECAAYFAkLnYMQACgkQbpR1lMFSQxq3PACgqksqmWLv
+vb7R1a7Q8lWxjCaa8V8An0z0Fd9GJuNgydyNIW09EBBeqXwwiEkEMBECAAkFAj4N
+NagCHQAACgkQi9gubzC5S1yB4QCggBPo0gnQXYxXXovq++XB06XkyE8AnAr8Z8v8
+9p/xZ5f8Jhv3+arjeJQtiHMEEBECADMFAkH3aB8FgwHhM4AmGmh0dHA6Ly93d3cu
+Y2FjZXJ0Lm9yZy9pbmRleC5waHA/aWQ9MTAACgkQd65/Ep4r0fJ5pgCgiB3W1T4c
+GDppMkr/XOiSb+CGcz8AniCU4fX1rwfcmml+2UMHaD6YBifOiQCSAwUQO/csPD6P
+t/L4g0HZAQFcFQPiA4wlLRmaFhthlDezqGgAUmVqrUH3rQS+fBu9YVJr+iIU6tSA
+ZtxQ06LGXoeiDfr3EpVb6uuxfsvYEQ1nbLuUpdifZ/WVA1MpsrEBUPvgeuaGWiaU
+vKQ9PWlgLewpL7YZGA+1F6c0Gru36ufV0cWYjfmwwB7UgX+EzIvjpweJAJUDBRA2
+L2tnN+P0X+5l4yEBAXDUBADq7ePqNi8HAoiad2zhfVmTIq6TEECSEqlwZ6Jt69ng
+fpNWvyYoNbHj0sz8Z3lvRIFiK1oNBHyvrtpDIL0ycpw1Lsfcy+KpxApmU/hEhJvj
+/KzynnnDn1B4A40JU1Ap3jl+adE23byZvk3CVY146Fa2L+oWYCJ2MPo5bxeXv7HS
+PIkAlQMFEDYva54rAsNF4kSQGQEBt4YEAIozUUKZALnzf8gfLDusOW+exkp4q91t
+ssZKeYk1Y7J/hWCQLw4IqbA+dYs44grjj8mxPZiieKHav7TgUGpijpKwyAGo4Emo
+qS0e+3FsPdV9RSSRXyRniK255xZPHTOPU5rRCIKK/wce92yZAHSaPTuZZDPUJqZc
+9LAdif6358k4iQCVAwUQNkcAbUS43da7HZ9tAQFUnAP+PrKa3YFKp9XTrANmORex
+4kOyNpM1adS8zM6bTcHyIkH4WitBb2nRbqOdOuSwbh655aSStluIxY66CValeL+6
+E7MCEqQ/UlzzMVmdbMyWSYwDlkV3gQkg3vE6bgFlWlLLr2HnkllY3ISEChDWh8x7
+fRKLy+8ZBGW89ZXOGkhIb9mJAJUDBRA6ynbL7q1M/UmlY9kBAfqwBADWNsA9PkYR
+bJXuAo14CZY4GjkkDz4ROa28MAgaDfvCw2E0FAaQI/6k30K7NEQoJI4uBnkpdikt
+axgsnPVftnO1HaQWYEY8eylgF0myvOa/J3I4McJmA30GHT++ZZYo9jfTjumRk4iJ
+DmV1Kppe2ZgzPNJV7a8LpWopBSRlQI1AnYkAlQMFEDsaVtNoOT9M0gui7QEBGvAD
+/jHgMCvKihnRNm3k2+nDUTHFn6blxG6kjRtL1DJGQD5cATbhxMx9ioQtD1q87KCj
+54pcGQ8HbTL8frQbyPX6yFjpSPILIPCXWF6NVHZ9GCi3SOo39HNFxCQZUpVof49g
+mGOgL/qwfu7mi/sBEJP7c3FeyFnvewYE2a9SBhZUWdb2iQCVAwUTQt6sGzGmPZbs
+FAuBAQGa8QP+KvvPkpSYQQkswMyjywDO1D7WMO365hlf8yv+rm2pQuNcjohNAGKH
+x3gWiqTM6VU6uY4LIy5u7WHH1KRijvf6CLV6zi7Zoe8ahZtGJSwAJdejxaAsbV3J
+fFxhEqzC9HwZHSEUqwXEDQP3ULkYiUDAr6t/nUlKn8zmQ4yJklEM1jGJAJUDBRNC
+320oms08wKmfdd0BASX6A/0VZD5IG16JzDK6nq0GvG3ivVjtvl5Nx1CQZINxIzmF
+SKYHluG/HEltU05TLKsdR0bfltqSWAV5KGUhz7UFZrKgOwlKBKAZ4Qi9+jbR9dV8
+rB/Y1x1sz1r409L/Au5IpAXIjjx0fkYoJuW1J834ymZFIbSVrH6OO8JL6tk7nQzy
+rYkBHAQTAQIABgUCP7/IDwAKCRAG6s+qdtg8xuRuB/9vB0CVFffRizujxjDQqOfK
+6uRugXmOAa95hDrJxPtyo6etPr/rxC823H2/VWUCpcZuj69bc3S88qbKg6T69UNO
+OfM5wXnqL1/ORGDiRRPQ24N1hLXYO5yDtXZsegGgNgSsoI+EAykeUhcfzZjYqO2U
+njZBduMzY8eGniNFWtVoYQPOkowngny/L1GxA/pWEgGUiQl8RDIBxHOtA8xdMvje
+radJ6PjQ49/A+209WIXgBlwjRaNdKtJSXVMN6LXa/Fq0lVBT7mkCdeo2m1LnPQJH
+N7D0VuZS1EBJ/gjoptQUAQehpy0CZKo76dVDDJq3wHgXlxV0jrUZsAiW2fd0l5Jj
+iQIVAwUQPqRYgc1j0d2E+SZPAQJy5BAAiC9vzladIopS40igR8djtOq7qc0h9JqT
+XN1NXl6sbLPyh8VGTy6CLRTcqw3dvKoj7WSW1ICGOAw1N6UE8g6ErpEcJ7UhZ6JW
+YcHDAcS7rWNYnPqEg4Vz2yprdzJYo38ACD3o81O7ko+KvwFsT+PYquZsc2OsjWH9
+GNwBGiSugyEJA3nkN9S7YMmJ5LriOofMze1fl8wnaoCx55RliOIYe7T5l65hokvQ
+7u2AT/z/Wca/qSuIiz7bNznLyyUT15OILl/WgXtDyhh1dL0C3K0dDxkbCmUB1DoT
+5Qp03w7fleT8diOhSquk9181iUKI85teeGkIMzPff8Th7liyxFFiFse8Q+xbtvN+
+3/jtnK59ArCpc6uSxWBdwoE66Unf63mmiGvEbkgoZ+aAsxm5LkP3v7VHHUJOtESf
+ijLq3QSv2vrobxDHejLgtMU9MQbPLV8s2mcRWeLrMJuFNRRNSEfJLEhG7FtvX8Rz
+Ez5kkgaHgSdyJ6HtDHo5MNIl9WkY/oFrh+jo+8j8eh1i81pZyAxPd+FRsDiyGTBt
+NJLQoARJV+YAY3D0Xa5Gmd4p2X2M47koDgHeA5HOpmfB0wuQOYDNnILdhIuHVB1G
+RcicJGMVx7DOXVJa5hoTbHoIz0XlpLsafMIQym/K3x6sP3MybzefD83MaJ+Ilat+
+T3FxctedvHmJARwEEAECAAYFAkQtjiQACgkQKbiS0MhihQHy/Af7BegEEKu6b6Oi
+t6LjVHE7Da9e0Vdp4kcWN6z2fUaRLz5pU6qy2xC9M4fNHgC/QYr6Y4Oh21h1FSwT
+ThsvUPW/ww2Ya90yOnq2ATeQbWgFzd9G7nXd02/tX6ENy1r7bLV4q7V0YrHin2Qy
+lCA3TjWrQoJmk3XpbJklo3+/QYF4Ici1ZYxSZLFwQWkCMKb7PzWlyXbKp90SikfL
+LrqISBdHs29DKieYXgARakgyCOqhLlwVV25xYBgm8dqmKvunLhVywMtvGy34OAQB
++9oaLStOk9vpLnSkDLHLsAMtEFANxXb8eQ52EbdMXtKXAzpVwVXobzm35ZD3Ajrn
+PUPnsM6uSIhGBBARAgAGBQJDh599AAoJEGY1tsDeiF3TeL8AnR9M8PqJhFtgjsUV
+cGUPuqGvzHIwAJ9YtZTOPbgNFhAtzz5myUzEwATnUohGBBARAgAGBQJEotvOAAoJ
+EPXCYBZM7tdfzMIAn11ERE6xQes48Lxzofr3iJqSViEkAJ9mVR4Jzi5ELdbeRmna
+SmqWeSnahohGBBARAgAGBQJEowIuAAoJEJRQQ/U1wQDw6IgAoIFuERnMMQLeTUXQ
+l3pa+3qONYJaAKDP6zc0b8neb7FtegXWrmodKHzY+IhGBBARAgAGBQJEo41PAAoJ
+EC0hq2VlRht5rX8An3wHo8WtQIdS9maRS3kLouXQbwv7AJwNEPKHfj1duPlxTQcM
+wcOYqlA3k4hGBBARAgAGBQJEpEY6AAoJECt+gudD7yEfFNYAn2NIOA/HNFSh0nZI
+sgeJkooFB1OyAJ9KAfWADJ6jjqTeyiyCITcWGNgCOYhGBBARAgAGBQJEqSDSAAoJ
+EDLJ5M42QstLITkAoM03u+ZArGbrIOZ0eUsFpfeQOA/VAKC6ipKDBB+Widh0J8LI
+or1FFptaZYhGBBARAgAGBQJEqXkgAAoJEDWLqjNQ+WEWMtIAn1I+MUuw4Q2COj/5
+XhyLoG0gjq96AJ46ye16TpcQ3BJ/g05LBNJvonrd0IhGBBARAgAGBQJEqs2tAAoJ
+EPs32kAzLmPNne4AniQ1GQOAaUT0KZ6V65X5ZKULPuxWAJ9u33vCnx7sDd6FrVyo
+MbQaVMGwxohGBBARAgAGBQJEtH+LAAoJEIs3mDmc6QTcnH4AnA0vgXIlKw4jxKqT
+ARIztmzasOasAJsHlZgzr2rwg0uBk0rj1BWZuv6lUohGBBERAgAGBQJEoxpaAAoJ
+EFYNCGHufcdO2Q0Anj9O0fO3FFtkm/JzhhyDpJ4RGsmDAJ9yIzONSrjzDIn9LqQZ
+7jTDwA0Tu4hGBBERAgAGBQJEoxppAAoJEMpynWJgPU9U6VgAn2eJcIe9NBN0X3Bz
+81etoSby3sZGAJ9XpjrTwTsOjN6EdD3L+SQdoBu8J4hGBBMRAgAGBQJC4D0YAAoJ
+ENUzTnWxMT3iC5MAn0n2KJmuCCm7rI8sPQcOhM5piW19AJ9ayTVzGuii1Ar2s41a
+KGCsGxS34ohGBBMRAgAGBQJC4TWsAAoJEKIRWuFfa4tytRQAoNIs5JcTKV5ipr7o
+ALx7tvc1GeObAJsERXhHD/z9Tc8ayNyCdLvYacAj24hGBBMRAgAGBQJEo6GAAAoJ
+EEwEKBgxGj3ltywAmwQJlwMEVB478PRN2dmQWvERlPqJAKCHAki2GJh6DPH2/hsI
+mw0NAbuH/ZkBogRF5pBCEQQAl0V2Nzw661+0aKZ9wUIcmrN55Vzu/JQ9pxXz7BeW
+wD1hTjNeYxgkPoG7+XpTUKBx1Ft8UW/PmnNYF/lJp192Ub+2lG9xHxYCD0iuaXO2
+bBXFZ2VRD+RmrO3WlEp51Lt6ou7mBm+RxWxf2MEAjOr24V6ACrRL8WYVwtJumSon
+//MAoMQ/H3l0Hx5O3nG3ruUyLnyamqnrA/9pfnqC0LTI3RAau7hKIfTLXh/md93B
+7zcDS+CtBzcDZb5Dm/NEpRC4vtwIjrkceNB8JBXjfuUhAxxoQyUsahM70Ao2vk7j
+iEt4V4jY5DrIxKUG/+BPZhlbJ5RWbrhS/L56tejIBeIYdxTHt4xTKggKIlfSeoaL
+v8w7N2/x2mvPwwP/R9THBOKfRkSCQPWODdQZMl/PgAyiKQDLxzws098roEs/yuCH
+7twsAZLk4pXtRWRl7AFS90a233wIDdMQbKtRKT5HQnTWrCuRoRT0sSnMi+7rzfG2
+UdSYiElgWeBsYYgtq5Vz23NshZeFkcAkUVG++P6CWQMxZkvqTIimle0sqia0KEpl
+YW4tRnJlZGVyaWMgQ2xlcmUgPGpmY2xlcmVAYXBhY2hlLm9yZz6IYAQTEQIAIAUC
+ReaQQgIbAwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJEA9FCiZiEL/AxXcAniAU
+9ixyqn2KbN0LiL62LbUIP7dgAKCSJxnDnv1Ea0btS5dIT43eWIXcFrkCDQRF5pBM
+EAgA46vQ7lkEVSvB14lYCkc2AgZSd7hG4hfvUCbyvskbrkCQi7ajJvMiXC5cvnZ9
+OFEWRhrcIAWvasQFZp81PsKX5oJuxTXE1pJZeufIroXTKgAvop85+ac1m34iQADT
+hI9ZifsrkrdaJmbTGfJd4eLoJX9QXHVbGHplXQuKTby6kIZVkKj4F4fTMn7RoIMT
+527ihbaSDWWWFBx8RmWsyVVy+8SnjLBZmTUpJ3eb4uTnlWQg63KwWE8n6KgEdvHq
+USEPnBVEQlB+tEpZiOdKBDT+OeLg+0cg2HXaebROAp5JKEZnt0P/n/HBqo/cKla8
+fLzOiLFdsGhICGAGT/xVg6iX7wADBQgA2vkFmT3Ue5woe17uJpV2TRHhUELYPvfq
+ZsdE7DxtxKzCGRaUTQcVRfPmJjycyclAQek+PXR3nH0TrWZRNwHmXe2teH+NRl3b
+pj7MBAMMjrHT1aUmQQw/FNwOdDuIPz5xz2VpBR1cjmlyTY988y9Nb0yVs789D10e
+wIKZPw/b8iGtjPUFn6IQrSuQWSTr/ZhTuQLg3DMw1NEo7LlIJOOVsTpFd2LHcx9b
+Ujd/1bxDF/+WE8jQdyKAr5pp/P3JkEokzfVuoXhbiAdq5WWLUWUcNoHmivmUFEbx
+XFUioyIgqud/5nWRTViD5qBxeyzFqFNE8bVQOnkjkrSakxxNEpmCQYhJBBgRAgAJ
+BQJF5pBMAhsMAAoJEA9FCiZiEL/Ah5UAn2wOhfYkXBtOejoIJnl7/7DrTc9EAKCG
+dXioomESUJeZzxMYdyk8nOiIeQ==
+=UD4Y
+-----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..dff50f3
--- /dev/null
+++ b/connectors/README.txt
@@ -0,0 +1,11 @@
+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..f458ced
--- /dev/null
+++ b/connectors/ajp/ajplib/include/ajp.h
@@ -0,0 +1,454 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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..7c8fc80
--- /dev/null
+++ b/connectors/ajp/ajplib/include/ajp_header.h
@@ -0,0 +1,166 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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..2bf3a16
--- /dev/null
+++ b/connectors/ajp/ajplib/include/ajp_logon.h
@@ -0,0 +1,52 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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..77af2b9
--- /dev/null
+++ b/connectors/ajp/ajplib/src/ajp_header.c
@@ -0,0 +1,726 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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..f8b91d1
--- /dev/null
+++ b/connectors/ajp/ajplib/src/ajp_link.c
@@ -0,0 +1,133 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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..dbb0e32
--- /dev/null
+++ b/connectors/ajp/ajplib/src/ajp_logon.c
@@ -0,0 +1,215 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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..d117ffa
--- /dev/null
+++ b/connectors/ajp/ajplib/src/ajp_msg.c
@@ -0,0 +1,608 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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..1581abb
--- /dev/null
+++ b/connectors/ajp/ajplib/test/httpd_wrap.c
@@ -0,0 +1,672 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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..01bf1ba
--- /dev/null
+++ b/connectors/ajp/ajplib/test/httpd_wrap.h
@@ -0,0 +1,788 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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..5e8ac3a
--- /dev/null
+++ b/connectors/ajp/ajplib/test/testajp.c
@@ -0,0 +1,284 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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..11051a4
--- /dev/null
+++ b/connectors/ajp/proxy/mod_proxy.c
@@ -0,0 +1,1720 @@
+#define FIX_15207
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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..a7fef04
--- /dev/null
+++ b/connectors/ajp/proxy/mod_proxy.h
@@ -0,0 +1,415 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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..993a004
--- /dev/null
+++ b/connectors/ajp/proxy/proxy_ajp.c
@@ -0,0 +1,421 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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..94c07ba
--- /dev/null
+++ b/connectors/ajp/proxy/proxy_balancer.c
@@ -0,0 +1,388 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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..a277ab3
--- /dev/null
+++ b/connectors/ajp/proxy/proxy_connect.c
@@ -0,0 +1,387 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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..58fed01
--- /dev/null
+++ b/connectors/ajp/proxy/proxy_ftp.c
@@ -0,0 +1,1870 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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..970190f
--- /dev/null
+++ b/connectors/ajp/proxy/proxy_http.c
@@ -0,0 +1,1263 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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..02b6d07
--- /dev/null
+++ b/connectors/ajp/proxy/proxy_util.c
@@ -0,0 +1,1878 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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..3124440
--- /dev/null
+++ b/connectors/build.properties.default
@@ -0,0 +1,245 @@
+# -----------------------------------------------------------------------------
+# 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.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=http://www.apache.org/dist/jakarta/commons/logging/binaries/commons-logging-1.0.4.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.4
+regexp.lib=${regexp.home}
+regexp.jar=${regexp.lib}/jakarta-regexp-1.4.jar
+regexp.loc=http://www.apache.org/dist/jakarta/regexp/binaries/jakarta-regexp-1.4.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
+
+
+# ----- 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=3.0.1
+jmx.home=${base.path}/mx4j-${jmx.version}
+jmx.lib=${jmx.home}/lib
+jmx.jar=${jmx.lib}/mx4j-jmx.jar
+jmx.loc=http://download.sourceforge.net/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
+
+# ----- Tomcat5.x.x -----
+tomcat5.version=5.5.15
+tomcat5.home=${base.path}/apache-tomcat-${tomcat5.version}
+tomcat5.jar=${tomcat5.home}/server/lib/catalina.jar
+servlet-api.jar=${tomcat5.home}/common/lib/servlet-api.jar
+tomcat5.loc=http://www.apache.org/dist/tomcat/tomcat-5/v${tomcat5.version}/bin/apache-tomcat-${tomcat5.version}.tar.gz 
+
+# ----- Tomcat4.1.x -----
+tomcat41.version=4.1.31
+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/tomcat/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}/tomcat/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..5b2ee1e
--- /dev/null
+++ b/connectors/build.xml
@@ -0,0 +1,330 @@
+<?xml version="1.0"?>
+<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="${tomcat5.loc}"/>
+      <param name="destfile" value="${tomcat5.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/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..d7fe441
--- /dev/null
+++ b/connectors/coyote/build.xml
@@ -0,0 +1,376 @@
+<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"
+   depends="static,compile.shared"
+   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="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..c5e15a9
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/ActionCode.java
@@ -0,0 +1,154 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.
+ * Actions represent callbacks from the servlet container to the coyote
+ * connector.
+ *
+ * Actions are implemented by ProtocolHandler, using the ActionHook interface.
+ *
+ * @see ProtocolHandler
+ * @see ActionHook
+ * @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 ).
+     * 
+     * Argument is the Response.
+     */
+    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);
+
+
+    /**
+     * Callback for setting FORM auth body replay
+     */
+    public static final ActionCode ACTION_REQ_SET_BODY_REPLAY = new ActionCode(20);
+
+
+    // ----------------------------------------------------------- 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..7215494
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/ActionHook.java
@@ -0,0 +1,48 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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. Actions represent the callback mechanism used by
+ * coyote servlet containers to request operations on the coyote connectors.
+ * Some standard actions are defined in ActionCode, however custom
+ * actions are permitted.
+ *
+ * The param object can be used to pass and return informations related with the
+ * action.
+ * 
+ *
+ * This interface is typically implemented by ProtocolHandlers, and the param
+ * is usually a Request or Response object.
+ *
+ * @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..0b6f5f0
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/Adapter.java
@@ -0,0 +1,50 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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. This represents the entry point in a coyote-based servlet container.
+ *
+ *
+ * @author Remy Maucherat
+ * @see ProtocolHandler
+ */
+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..6f7e970
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/Constants.java
@@ -0,0 +1,56 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..56e1ecc
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/InputBuffer.java
@@ -0,0 +1,46 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.
+ *
+ * This class is used only in the protocol implementation. All reading from tomcat ( or adapter ) should be done
+ * using Request.doRead().
+ *
+ * 
+ * @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..4f6c647
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/OutputBuffer.java
@@ -0,0 +1,47 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.
+ *
+ * This class is used internally by the protocol implementation. All writes from higher level code should happen
+ * via Resonse.doWrite().
+ * 
+ * @author Remy Maucherat
+ */
+public interface OutputBuffer {
+
+
+    /** Write the response. The caller ( tomcat ) owns the chunks.
+     *
+     * @param chunk data to write
+     * @param response used to allow buffers that can be shared by multiple responses.
+     * @return
+     * @throws IOException
+     */
+    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..9258f57
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/Processor.java
@@ -0,0 +1,45 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.
+ *
+ * Not really used, should be deprecated. 
+ *
+ * @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..0e81135
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/ProtocolHandler.java
@@ -0,0 +1,85 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.Iterator;
+
+
+/**
+ * Abstract the protocol implementation, including threading, etc.
+ * Processor is single threaded and specific to stream-based protocols,
+ * will not fit Jk protocols like JNI.
+ *
+ * This is the main interface to be implemented by a coyoute connector.
+ * Adapter is the main interface to be impleneted by a coyote servlet container.
+ *
+ * @author Remy Maucherat
+ * @author Costin Manolache
+ * @see Adapter
+ */
+public interface ProtocolHandler {
+
+
+    /**
+     * Pass config info.
+     */
+    public void setAttribute(String name, Object value);
+
+
+    public Object getAttribute(String name);
+    public Iterator getAttributeNames();
+
+    /**
+     * 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..1c148af
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/Request.java
@@ -0,0 +1,503 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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);
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    private int serverPort = -1;
+    private MessageBytes serverNameMB = MessageBytes.newInstance();
+
+    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 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.getUniqueValue("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.
+     *
+     * The buffer is owned by the protocol implementation - it will be reused on the next read.
+     * The Adapter must either process the data in place or copy it to a separate buffer if it needs
+     * to hold it. In most cases this is done during byte->char conversions or via InputStream. Unlike
+     * InputStream, this interface allows the app to process data in place, without copy.
+     *
+     */
+    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();
+
+        schemeMB.recycle();
+
+        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..41fa3fe
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/RequestGroupInfo.java
@@ -0,0 +1,164 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..30559ca
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/RequestInfo.java
@@ -0,0 +1,223 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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;
+    String workerThreadName;
+
+    // -------------------- 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;
+    }
+
+    public String getWorkerThreadName() {
+        return workerThreadName;
+    }
+
+    public void setWorkerThreadName(String workerThreadName) {
+        this.workerThreadName = workerThreadName;
+    }
+}
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..dd3e3b9
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/Response.java
@@ -0,0 +1,592 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 long 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 --------------------
+    /**
+     * Warning: This method always returns <code>false<code> for Content-Type
+     * and Content-Length.
+     */
+    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 {
+                long cL=Long.parseLong( 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 void setContentLength(long contentLength) {
+        this.contentLength = contentLength;
+    }
+
+    public int getContentLength() {
+        long length = getContentLengthLong();
+        
+        if (length < Integer.MAX_VALUE) {
+            return (int) length;
+        }
+        return -1;
+    }
+    
+    public long getContentLengthLong() {
+        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..bbb8fe9
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/memory/MemoryProtocolHandler.java
@@ -0,0 +1,167 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 java.util.Iterator;
+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;
+    }
+
+    public Iterator getAttributeNames() { 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..3277adc
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat3/CoyoteInterceptor2.java
@@ -0,0 +1,304 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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
+{
+    public static final String REDIRECT_PORT_ATTR = 
+	"org.apache.tomcat.request.redirectPort";
+    private String processorClassName="org.apache.coyote.http11.Http11Protocol";
+    Tomcat3Adapter adapter;
+    ProtocolHandler proto;
+    int protocolNote;
+    int redirectPort = -1;
+    
+    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( translateAttributeName(prop), value );
+    }
+
+
+    public void setProperty( String prop, String value ) {
+        setAttribute( prop, value );
+    }
+
+    /**
+     * Set the redirect port.
+     */
+    public void setRedirectPort(int rp) {
+	redirectPort = rp;
+	setAttribute("redirectPort", new Integer(rp));
+    }
+
+    /**
+     * Get the redirect port.
+     */
+    public int getRedirectPort() {
+	return redirectPort;
+    }
+
+    /** 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, this);
+        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( httpReq == null || httpReq.getConnector() != this ) {
+	    return null;
+	}
+
+        if(key!=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);
+            } else if(key.equals(REDIRECT_PORT_ATTR)) {
+		return new Integer(redirectPort);
+	    }
+
+            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);
+    }
+
+    private String translateAttributeName(String name) {
+         if ("clientAuth".equals(name)) {
+             return "clientauth";
+         } else if ("keystoreFile".equals(name)) {
+             return "keystore";
+         } else if ("randomFile".equals(name)) {
+             return "randomfile";
+         } else if ("rootFile".equals(name)) {
+             return "rootfile";
+         } else if ("keystorePass".equals(name)) {
+             return "keypass";
+         } else if ("keystoreType".equals(name)) {
+             return "keytype";
+         } else if ("sslProtocol".equals(name)) {
+             return "protocol";
+         } else if ("sslProtocols".equals(name)) {
+             return "protocols";
+         }
+         return name;
+    }
+ 
+}
+
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..c90fc18
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat3/Tomcat3Adapter.java
@@ -0,0 +1,76 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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;
+import org.apache.tomcat.core.BaseInterceptor;
+
+/** 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;
+    BaseInterceptor connector;
+    
+    Tomcat3Adapter(ContextManager ctxman, CoyoteInterceptor2 conn) {
+	cm   = ctxman;
+	connector = conn;
+    }
+
+    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);
+	    reqA.setConnector(connector);
+            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..4673836
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat3/Tomcat3Request.java
@@ -0,0 +1,237 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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;
+import org.apache.tomcat.core.BaseInterceptor;
+
+/** 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;
+    BaseInterceptor   connector = 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();
+    }
+
+    /**
+     * Set the Connector that this request services
+     */
+    void setConnector(BaseInterceptor conn) {
+	connector = conn;
+    }
+
+    /**
+     * Get the Connector that this request services
+     */
+    BaseInterceptor getConnector() {
+	return connector;
+    }
+    
+    /** 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..23e081c
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat3/Tomcat3Response.java
@@ -0,0 +1,153 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..68bf332
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat4/Constants.java
@@ -0,0 +1,51 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..4f3a5f4
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteAdapter.java
@@ -0,0 +1,700 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 {
+
+    protected static final boolean ALLOW_BACKSLASH = Boolean.valueOf(
+            System.getProperty(
+                    "org.apache.coyote.tomcat4.CoyoteAdapter.ALLOW_BACKSLASH",
+                    "false")).booleanValue();
+
+    // -------------------------------------------------------------- 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) {
+            if ( ALLOW_BACKSLASH )
+                normalized = normalized.replace('\\', '/');
+            else 
+                return null;
+        }
+        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) '\\') {
+                if (ALLOW_BACKSLASH)
+                    b[pos] = (byte) '/';
+                else 
+                    return false;
+            }
+            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..0409b77
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteConnector.java
@@ -0,0 +1,1426 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.net.URLEncoder;
+import java.util.HashMap;
+
+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.core.StandardEngine;
+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.MalformedObjectNameException;
+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 {
+
+
+    // ------------------------------------------------------------ Constructor
+    
+    public CoyoteConnector() throws Exception {
+        this(null);
+    }
+    
+    public CoyoteConnector(String protocolHandlerClassName) throws Exception {
+        
+        if (protocolHandlerClassName != null) {
+            setProtocolHandlerClassName(protocolHandlerClassName);
+        }
+        
+        // Instantiate protocol handler
+        try {
+            Class clazz = Class.forName(this.protocolHandlerClassName);
+            protocolHandler = (ProtocolHandler) clazz.newInstance();
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new LifecycleException(sm.getString(
+                    "coyoteConnector.protocolHandlerInstantiationFailed", e));
+        }
+
+    }
+
+    // ----------------------------------------------------- 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 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 idle processors to have available. This is also
+     * the number of processors that are started at initialization.
+     */
+    protected int minProcessors = 4;
+
+
+    /**
+     * The maximum amount of spare processors.
+     */
+    protected int maxSpareProcessors = 50;
+
+
+    /**
+     * The maximum number of processors allowed, or <0 for unlimited.
+     */
+    private int maxProcessors = 200;
+
+    /**
+     * The maximum permitted size of the request and response HTTP headers.
+     */
+    private int maxHttpHeaderSize = 4 * 1024;
+
+    /**
+     * 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;
+
+
+    /**
+     * 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 compressableMimeTypes = "text/html,text/xml,text/plain";
+    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;
+
+
+     protected static HashMap replacements = new HashMap();
+     static {
+         replacements.put("maxProcessors", "maxThreads");
+         replacements.put("minProcessors", "minSpareThreads");
+         replacements.put("maxSpareProcessors", "maxSpareThreads");
+         replacements.put("acceptCount", "backlog");
+         replacements.put("connectionLinger", "soLinger");
+         replacements.put("connectionTimeout", "soTimeout");
+         replacements.put("connectionUploadTimeout", "timeout");
+         replacements.put("serverSocketTimeout", "serverSoTimeout");
+         replacements.put("clientAuth", "clientauth");
+         replacements.put("keystoreFile", "keystore");
+         replacements.put("randomFile", "randomfile");
+         replacements.put("rootFile", "rootfile");
+         replacements.put("keystorePass", "keypass");
+         replacements.put("keystoreType", "keytype");
+         replacements.put("sslProtocol", "protocol");
+         replacements.put("sslProtocols", "protocols");
+     }
+
+     
+     // ------------------------------------------------------------- Properties
+
+
+     /**
+      * Return a configured property.
+      */
+     public Object getProperty(String name) {
+         String repl = name;
+         if (replacements.get(name) != null) {
+             repl = (String) replacements.get(name);
+         }
+         return IntrospectionUtils.getProperty(protocolHandler, repl);
+     }
+
+     
+     /**
+      * Set a configured property.
+      */
+     public void setProperty(String name, String value) {
+         String repl = name;
+         if (replacements.get(name) != null) {
+             repl = (String) replacements.get(name);
+         }
+         IntrospectionUtils.setProperty(protocolHandler, repl, value);
+     }
+
+     
+    /**
+     * 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;
+        setProperty("connectionLinger", String.valueOf(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;
+        setProperty("connectionTimeout", String.valueOf(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;
+        setProperty("connectionUploadTimeout",
+                    String.valueOf(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;
+        setProperty("serverSocketTimeout", String.valueOf(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;
+        setProperty("acceptCount", String.valueOf(acceptCount));
+    }
+
+
+    /**
+     * 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;
+        setProperty("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 list of compressable MIME types.
+     */
+    public String getCompressableMimeType() {
+        return compressableMimeTypes;
+    }
+
+    /**
+     * Set the list of compressable MIME types.
+     * 
+     * @param compressableMimeTypes The new list of MIME types to enable for
+     * compression.
+     */
+    public void setCompressableMimeType(String compressableMimeTypes) {
+        this.compressableMimeTypes = compressableMimeTypes;
+        setProperty("compressableMimeTypes", compressableMimeTypes);
+    }
+
+    /**
+     * 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;
+        setProperty("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;
+        setProperty("minProcessors", String.valueOf(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;
+        setProperty("maxProcessors", String.valueOf(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;
+        setProperty("maxSpareProcessors", String.valueOf(maxSpareProcessors));
+
+    }
+
+    /**
+     * Return the maximum permitted size of the HTTP request and response
+     * headers.
+     */
+    public int getMaxHttpHeaderSize() {
+        return maxHttpHeaderSize;
+    }
+
+    /**
+     * Set the maximum permitted size of the HTTP request and response
+     * headers.
+     * 
+     * @param maxHttpHeaderSize The new maximum header size in bytes.
+     */
+    public void setMaxHttpHeaderSize(int maxHttpHeaderSize) {
+        this.maxHttpHeaderSize = maxHttpHeaderSize;
+        setProperty("maxHttpHeaderSize", String.valueOf(maxHttpHeaderSize));
+    }
+
+    /**
+     * 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;
+        setProperty("port", String.valueOf(port));
+        
+    }
+
+
+    /**
+     * Return the protocol handler associated with the connector.
+     */
+    public ProtocolHandler getProtocolHandler() {
+
+        return (this.protocolHandler);
+
+    }
+    /**
+     * 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;
+        setProperty("disableUploadTimeout",
+                    String.valueOf(disableUploadTimeout));
+    }
+
+    /**
+     * 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;
+        setProperty("maxKeepAliveRequests",
+                    String.valueOf(maxKeepAliveRequests));
+    }
+
+    /**
+     * 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;
+        setProperty("secure",
+                String.valueOf(secure));
+
+    }
+
+    public boolean getTomcatAuthentication() {
+        return tomcatAuthentication;
+    }
+
+    public void setTomcatAuthentication(boolean tomcatAuthentication) {
+        this.tomcatAuthentication = tomcatAuthentication;
+        setProperty("tomcatAuthentication",
+                    String.valueOf(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;
+        setProperty("tcpNoDelay", String.valueOf(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);
+
+    }
+
+
+    // ------------------------------------------------------ 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);
+
+    }
+
+    protected ObjectName createObjectName(String domain, String type)
+            throws MalformedObjectNameException {
+        String encodedAddr = null;
+        if (getProperty("address") != null) {
+            encodedAddr = URLEncoder.encode(getProperty("address").toString());
+        }
+        String addSuffix = (getProperty("address") == null) ? "" : ",address="
+                + encodedAddr;
+        ObjectName _oname = new ObjectName(domain + ":type=" + type + ",port="
+                + getPort() + addSuffix);
+        return _oname;
+}
+
+    
+    /**
+     * Initialize this connector (create ServerSocket here!)
+     */
+    public void initialize()
+        throws LifecycleException {
+
+        if (initialized)
+            throw new LifecycleException
+                (sm.getString("coyoteConnector.alreadyInitialized"));
+
+        this.initialized = true;
+
+        if( oname == null && (container instanceof StandardEngine)) {
+            try {
+                // we are loaded directly, via API - and no name was given to us
+                StandardEngine cb=(StandardEngine)container;
+                oname = createObjectName(cb.getName(), "Connector");
+                Registry.getRegistry(null, null)
+                    .registerComponent(this, oname, null);
+            } catch (Exception e) {
+                log ("Error registering connector " + e.toString());
+            }
+            if(debug > 0)
+                log("Creating name for connector " + oname);
+        }
+
+        // Initialize adapter
+        adapter = new CoyoteAdapter(this);
+
+        protocolHandler.setAdapter(adapter);
+
+        IntrospectionUtils.setProperty(protocolHandler, "jkHome",
+                                       System.getProperty("catalina.base"));
+
+        // 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, "ciphers",
+                                           ssf.getCiphers());
+	        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());
+        }
+
+        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(null, null).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..6c2aa11
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteInputStream.java
@@ -0,0 +1,165 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..33be5bd
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteOutputStream.java
@@ -0,0 +1,94 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..ee90d41
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyotePrincipal.java
@@ -0,0 +1,74 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..9fed204
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteRequest.java
@@ -0,0 +1,2120 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 == null || 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 = "";
+                }
+            }
+
+            if (!isAlpha(language) || !isAlpha(country) || !isAlpha(variant)) {
+                continue;
+            }
+
+            // 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);
+            }
+        }
+
+    }
+
+    protected static final boolean isAlpha(String value) {
+        for (int i = 0; i < value.length(); i++) {
+            char c = value.charAt(i);
+            if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))) {
+                return false;
+            }
+        }
+        return true;
+    }
+}
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..86a2440
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteRequestFacade.java
@@ -0,0 +1,343 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..2b01a28
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteResponse.java
@@ -0,0 +1,1325 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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) {
+        // Need special handling for Content-Type and Content-Length due to
+        // special handling of these in coyoteResponse
+        char cc=name.charAt(0);
+        if(cc=='C' || cc=='c') {
+            if(name.equalsIgnoreCase("Content-Type")) {
+                // Will return null if this has not been set
+                return (coyoteResponse.getContentType() != null);
+            }
+            if(name.equalsIgnoreCase("Content-Length")) {
+                // Can't use null test since this header is an int
+                return (coyoteResponse.getContentLengthLong() != -1);
+            }
+        }
+
+        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) {
+            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..f4dafcf
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteResponseFacade.java
@@ -0,0 +1,381 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..4b1e707
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteServerSocketFactory.java
@@ -0,0 +1,237 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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;
+    }
+
+
+    /**
+     * Ciphers to be used.
+     */
+    private String ciphers;
+
+    public String getCiphers() {
+        return (this.ciphers);
+    }
+
+    public void setCiphers(String ciphers) {
+        this.ciphers = ciphers;
+    }
+
+
+    /**
+     * 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..fa7ec4e
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteWriter.java
@@ -0,0 +1,268 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..ec281e5
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat4/OutputBuffer.java
@@ -0,0 +1,676 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 {
+
+
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( OutputBuffer.class );
+
+    // -------------------------------------------------------------- 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.getContentLengthLong() == -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 ) {
+        if (log.isDebugEnabled()) 
+            log.debug("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..3bcb77d
--- /dev/null
+++ b/connectors/coyote/src/test/org/apache/coyote/SimpleAdapter.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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..3fa6b56
--- /dev/null
+++ b/connectors/http11/build.xml
@@ -0,0 +1,243 @@
+<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-jni.jar" value="../jni/dist/tomcat-native-1.0.0.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}"/>
+    <pathelement location="${tomcat-jni.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">
+    <available property="jdk.1.4.present"   classname="java.lang.CharSequence" />
+
+    <javac  srcdir="${source.home}"
+           destdir="${build.home}/classes"
+             debug="${compile.debug}"
+       deprecation="${compile.deprecation}"
+          optimize="${compile.optimize}">
+      <exclude name="org\apache\coyote\http11\*Apr*" unless="jdk.1.4.present" />
+      <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>
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..4e909a6
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/Constants.java
@@ -0,0 +1,209 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.ByteChunk;
+
+
+/**
+ * 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;
+    
+    
+    /**
+     * CRLF.
+     */
+    public static final String CRLF = "\r\n";
+
+    
+    /**
+     * Server string.
+     */
+    public static final byte[] SERVER_BYTES = 
+        ByteChunk.convertToBytes("Server: Apache-Coyote/1.1" + CRLF);
+
+    
+    /**
+     * 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;
+
+
+    /* Various constant "strings" */
+    public static final byte[] CRLF_BYTES = ByteChunk.convertToBytes(CRLF);
+    public static final byte[] COLON_BYTES = ByteChunk.convertToBytes(": ");
+    public static final String CONNECTION = "Connection";
+    public static final String CLOSE = "close";
+    public static final byte[] CLOSE_BYTES = 
+        ByteChunk.convertToBytes(CLOSE);
+    public static final String KEEPALIVE = "keep-alive";
+    public static final byte[] KEEPALIVE_BYTES = 
+        ByteChunk.convertToBytes(KEEPALIVE);
+    public static final String CHUNKED = "chunked";
+    public static final byte[] ACK_BYTES = 
+        ByteChunk.convertToBytes("HTTP/1.1 100 Continue" + CRLF + CRLF);
+    public static final String TRANSFERENCODING = "Transfer-Encoding";
+    public static final byte[] _200_BYTES = 
+        ByteChunk.convertToBytes("200");
+    public static final byte[] _400_BYTES = 
+        ByteChunk.convertToBytes("400");
+    public static final byte[] _404_BYTES = 
+        ByteChunk.convertToBytes("404");
+    
+
+    /**
+     * 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";
+    public static final byte[] HTTP_11_BYTES = 
+        ByteChunk.convertToBytes(HTTP_11);
+
+
+    /**
+     * GET.
+     */
+    public static final String GET = "GET";
+
+
+    /**
+     * HEAD.
+     */
+    public static final String HEAD = "HEAD";
+
+
+    /**
+     * POST.
+     */
+    public static final String POST = "POST";
+
+
+}
diff --git a/connectors/http11/src/java/org/apache/coyote/http11/Http11AprProcessor.java b/connectors/http11/src/java/org/apache/coyote/http11/Http11AprProcessor.java
new file mode 100644
index 0000000..cae738d
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/Http11AprProcessor.java
@@ -0,0 +1,1771 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.util.StringTokenizer;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.ActionHook;
+import org.apache.coyote.Adapter;
+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.SavedRequestInputFilter;
+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.jni.Address;
+import org.apache.tomcat.jni.SSL;
+import org.apache.tomcat.jni.SSLSocket;
+import org.apache.tomcat.jni.Sockaddr;
+import org.apache.tomcat.jni.Socket;
+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.AprEndpoint;
+import org.apache.tomcat.util.res.StringManager;
+import org.apache.tomcat.util.threads.ThreadWithAttributes;
+
+
+/**
+ * Processes HTTP requests.
+ *
+ * @author Remy Maucherat
+ */
+public class Http11AprProcessor implements ActionHook {
+
+
+    /**
+     * Logger.
+     */
+    protected static org.apache.commons.logging.Log log
+        = org.apache.commons.logging.LogFactory.getLog(Http11AprProcessor.class);
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    public Http11AprProcessor(int headerBufferSize, AprEndpoint endpoint) {
+
+        this.endpoint = endpoint;
+        
+        request = new Request();
+        int readTimeout = endpoint.getFirstReadTimeout();
+        if (readTimeout == 0) {
+            readTimeout = 100;
+        } else if (readTimeout < 0) {
+            readTimeout = -1;
+        }
+        inputBuffer = new InternalAprInputBuffer(request, headerBufferSize,
+                readTimeout);
+        request.setInputBuffer(inputBuffer);
+
+        response = new Response();
+        response.setHook(this);
+        outputBuffer = new InternalAprOutputBuffer(response, headerBufferSize);
+        response.setOutputBuffer(outputBuffer);
+        request.setResponse(response);
+        
+        ssl = !"off".equalsIgnoreCase(endpoint.getSSLEngine());
+
+        initializeFilters();
+
+        // Cause loading of HexUtils
+        int foo = HexUtils.DEC[0];
+
+        // Cause loading of FastHttpDateFormat
+        FastHttpDateFormat.getCurrentDate();
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Associated adapter.
+     */
+    protected Adapter adapter = null;
+
+
+    /**
+     * Request object.
+     */
+    protected Request request = null;
+
+
+    /**
+     * Response object.
+     */
+    protected Response response = null;
+
+
+    /**
+     * Input.
+     */
+    protected InternalAprInputBuffer inputBuffer = null;
+
+
+    /**
+     * Output.
+     */
+    protected InternalAprOutputBuffer 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;
+
+
+    /**
+     * Sendfile data.
+     */
+    protected AprEndpoint.SendfileData sendfileData = null;
+
+
+    /**
+     * 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 enabled ?
+     */
+    protected boolean ssl = false;
+    
+
+    /**
+     * Socket associated with the current connection.
+     */
+    protected long socket = 0;
+
+
+    /**
+     * 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 save post size.
+     */
+    protected int maxSavePostSize = 4 * 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 endpoint.
+     */
+    protected AprEndpoint endpoint;
+
+
+    /**
+     * Allow a customized the server header for the tin-foil hat folks.
+     */
+    protected String server = null;
+
+
+    // ------------------------------------------------------------- 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;
+    }
+
+
+    /**
+     * 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(sm.getString("http11processor.regexp.error", 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(sm.getString("http11processor.filter.unknown", className));
+            }
+        } catch (Exception e) {
+            log.error(sm.getString("http11processor.filter.error", 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(sm.getString("http11processor.regexp.error", 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 setMaxSavePostSize(int msps) {
+        maxSavePostSize = msps;
+    }
+
+
+    /**
+     * Return the maximum size of a POST which will be buffered in SSL mode.
+     */
+    public int getMaxSavePostSize() {
+        return maxSavePostSize;
+    }
+
+
+    /**
+     * 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;
+    }
+
+
+    /**
+     * Set the server header name.
+     */
+    public void setServer( String server ) {
+        if (server==null || server.equals("")) {
+            this.server = null;
+        } else {
+            this.server = server;
+        }
+    }
+
+    /**
+     * Get the server header name.
+     */
+    public String getServer() {
+        return server;
+    }
+
+
+    /** 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.
+     *
+     * @throws IOException error during an I/O operation
+     */
+    public boolean process(long socket)
+        throws IOException {
+        ThreadWithAttributes thrA=
+                (ThreadWithAttributes)Thread.currentThread();
+        RequestInfo rp = request.getRequestProcessor();
+        thrA.setCurrentStage(endpoint, "parsing http request");
+        rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
+
+        // Set the remote address
+        remoteAddr = null;
+        remoteHost = null;
+        localAddr = null;
+        localName = null;
+        remotePort = -1;
+        localPort = -1;
+
+        // Setting up the socket
+        this.socket = socket;
+        inputBuffer.setSocket(socket);
+        outputBuffer.setSocket(socket);
+
+        // Error flag
+        error = false;
+        keepAlive = true;
+
+        int keepAliveLeft = maxKeepAliveRequests;
+        long soTimeout = endpoint.getSoTimeout();
+        
+        int limit = 0;
+        if (endpoint.getFirstReadTimeout() > 0 || endpoint.getFirstReadTimeout() < -1) {
+            limit = endpoint.getMaxThreads() / 2;
+        }
+
+        boolean keptAlive = false;
+        boolean openSocket = false;
+
+        while (started && !error && keepAlive) {
+
+            // Parsing the request header
+            try {
+                if( !disableUploadTimeout && keptAlive && soTimeout > 0 ) {
+                    Socket.timeoutSet(socket, soTimeout * 1000);
+                }
+                if (!inputBuffer.parseRequestLine
+                        (keptAlive && (endpoint.getCurrentThreadsBusy() > limit))) {
+                    // This means that no data is available right now
+                    // (long keepalive), so that the processor should be recycled
+                    // and the method should return true
+                    openSocket = true;
+                    // Add the socket to the poller
+                    endpoint.getPoller().add(socket);
+                    break;
+                }
+                request.setStartTime(System.currentTimeMillis());
+                thrA.setParam(endpoint, request.requestURI());
+                keptAlive = true;
+                if (!disableUploadTimeout) {
+                    Socket.timeoutSet(socket, timeout * 1000);
+                }
+                inputBuffer.parseHeaders();
+            } catch (IOException e) {
+                error = true;
+                break;
+            } catch (Throwable t) {
+                if (log.isDebugEnabled()) {
+                    log.debug(sm.getString("http11processor.header.parse"), t);
+                }
+                // 400 - Bad Request
+                response.setStatus(400);
+                error = true;
+            }
+
+            // Setting up filters, and parse some request headers
+            thrA.setCurrentStage(endpoint, "prepareRequest");
+            rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
+            try {
+                prepareRequest();
+            } catch (Throwable t) {
+                if (log.isDebugEnabled()) {
+                    log.debug(sm.getString("http11processor.request.prepare"), 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(endpoint, "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(sm.getString("http11processor.request.process"), t);
+                    // 500 - Internal Server Error
+                    response.setStatus(500);
+                    error = true;
+                }
+            }
+
+            // Finish the handling of the request
+            try {
+                thrA.setCurrentStage(endpoint, "endRequestIB");
+                rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);
+                inputBuffer.endRequest();
+            } catch (IOException e) {
+                error = true;
+            } catch (Throwable t) {
+                log.error(sm.getString("http11processor.request.finish"), t);
+                // 500 - Internal Server Error
+                response.setStatus(500);
+                error = true;
+            }
+            try {
+                thrA.setCurrentStage(endpoint, "endRequestOB");
+                rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);
+                outputBuffer.endRequest();
+            } catch (IOException e) {
+                error = true;
+            } catch (Throwable t) {
+                log.error(sm.getString("http11processor.response.finish"), 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(endpoint, "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();
+
+            // Do sendfile as needed: add socket to sendfile and end
+            if (sendfileData != null) {
+                sendfileData.socket = socket;
+                sendfileData.keepAlive = keepAlive;
+                if (!endpoint.getSendfile().add(sendfileData)) {
+                    openSocket = true;
+                    break;
+                }
+            }
+            
+        }
+
+        rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
+
+        // Recycle
+        inputBuffer.recycle();
+        outputBuffer.recycle();
+        this.socket = 0;
+
+        return openSocket;
+        
+    }
+
+
+    // ----------------------------------------------------- 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_HOST_ADDR_ATTRIBUTE) {
+
+            // Get remote host address
+            if (remoteAddr == null && (socket != 0)) {
+                try {
+                    long sa = Address.get(Socket.APR_REMOTE, socket);
+                    remoteAddr = Address.getip(sa);
+                } catch (Exception e) {
+                    log.warn(sm.getString("http11processor.socket.info"), e);
+                }
+            }
+            request.remoteAddr().setString(remoteAddr);
+
+        } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_NAME_ATTRIBUTE) {
+
+            // Get local host name
+            if (localName == null && (socket != 0)) {
+                try {
+                    long sa = Address.get(Socket.APR_LOCAL, socket);
+                    localName = Address.getnameinfo(sa, 0);
+                } catch (Exception e) {
+                    log.warn(sm.getString("http11processor.socket.info"), e);
+                }
+            }
+            request.localName().setString(localName);
+
+        } else if (actionCode == ActionCode.ACTION_REQ_HOST_ATTRIBUTE) {
+
+            // Get remote host name
+            if (remoteHost == null && (socket != 0)) {
+                try {
+                    long sa = Address.get(Socket.APR_REMOTE, socket);
+                    remoteHost = Address.getnameinfo(sa, 0);
+                } catch (Exception e) {
+                    log.warn(sm.getString("http11processor.socket.info"), e);
+                }
+            }
+            request.remoteHost().setString(remoteHost);
+
+        } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_ADDR_ATTRIBUTE) {
+
+            // Get local host address
+            if (localAddr == null && (socket != 0)) {
+                try {
+                    long sa = Address.get(Socket.APR_LOCAL, socket);
+                    localAddr = Address.getip(sa);
+                } catch (Exception e) {
+                    log.warn(sm.getString("http11processor.socket.info"), e);
+                }
+            }
+
+            request.localAddr().setString(localAddr);
+
+        } else if (actionCode == ActionCode.ACTION_REQ_REMOTEPORT_ATTRIBUTE) {
+
+            // Get remote port
+            if (remotePort == -1 && (socket != 0)) {
+                try {
+                    long sa = Address.get(Socket.APR_REMOTE, socket);
+                    Sockaddr addr = Address.getInfo(sa);
+                    remotePort = addr.port;
+                } catch (Exception e) {
+                    log.warn(sm.getString("http11processor.socket.info"), e);
+                }
+            }
+            request.setRemotePort(remotePort);
+
+        } else if (actionCode == ActionCode.ACTION_REQ_LOCALPORT_ATTRIBUTE) {
+
+            // Get local port
+            if (localPort == -1 && (socket != 0)) {
+                try {
+                    long sa = Address.get(Socket.APR_LOCAL, socket);
+                    Sockaddr addr = Address.getInfo(sa);
+                    localPort = addr.port;
+                } catch (Exception e) {
+                    log.warn(sm.getString("http11processor.socket.info"), e);
+                }
+            }
+            request.setLocalPort(localPort);
+
+        } else if (actionCode == ActionCode.ACTION_REQ_SSL_ATTRIBUTE ) {
+
+            if (ssl && (socket != 0)) {
+                try {
+                    // Cipher suite
+                    Object sslO = SSLSocket.getInfoS(socket, SSL.SSL_INFO_CIPHER);
+                    if (sslO != null) {
+                        request.setAttribute
+                            (AprEndpoint.CIPHER_SUITE_KEY, sslO);
+                    }
+                    // Client certificate chain if present
+                    int certLength = SSLSocket.getInfoI(socket, SSL.SSL_INFO_CLIENT_CERT_CHAIN);
+                    X509Certificate[] certs = null;
+                    if (certLength > 0) {
+                        certs = new X509Certificate[certLength];
+                        for (int i = 0; i < certLength; i++) {
+                            byte[] data = SSLSocket.getInfoB(socket, SSL.SSL_INFO_CLIENT_CERT_CHAIN + i);
+                            CertificateFactory cf =
+                                CertificateFactory.getInstance("X.509");
+                            ByteArrayInputStream stream = new ByteArrayInputStream(data);
+                            certs[i] = (X509Certificate) cf.generateCertificate(stream);
+                        }
+                    }
+                    if (certs != null) {
+                        request.setAttribute
+                            (AprEndpoint.CERTIFICATE_KEY, certs);
+                    }
+                    // User key size
+                    sslO = new Integer(SSLSocket.getInfoI(socket, SSL.SSL_INFO_CIPHER_USEKEYSIZE));
+                    if (sslO != null) {
+                        request.setAttribute
+                            (AprEndpoint.KEY_SIZE_KEY, sslO);
+                    }
+                    // SSL session ID
+                    sslO = SSLSocket.getInfoS(socket, SSL.SSL_INFO_SESSION_ID);
+                    if (sslO != null) {
+                        request.setAttribute
+                            (AprEndpoint.SESSION_ID_KEY, sslO);
+                    }
+                } catch (Exception e) {
+                    log.warn(sm.getString("http11processor.socket.ssl"), e);
+                }
+            }
+
+        } else if (actionCode == ActionCode.ACTION_REQ_SSL_CERTIFICATE) {
+
+            if (ssl && (socket != 0)) {
+                 // 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(maxSavePostSize);
+                inputBuffer.addActiveFilter
+                    (inputFilters[Constants.BUFFERED_FILTER]);
+                try {
+                    // Renegociate certificates
+                    SSLSocket.renegotiate(socket);
+                    // Client certificate chain if present
+                    int certLength = SSLSocket.getInfoI(socket, SSL.SSL_INFO_CLIENT_CERT_CHAIN);
+                    X509Certificate[] certs = null;
+                    if (certLength > 0) {
+                        certs = new X509Certificate[certLength];
+                        for (int i = 0; i < certLength; i++) {
+                            byte[] data = SSLSocket.getInfoB(socket, SSL.SSL_INFO_CLIENT_CERT_CHAIN + i);
+                            CertificateFactory cf =
+                                CertificateFactory.getInstance("X.509");
+                            ByteArrayInputStream stream = new ByteArrayInputStream(data);
+                            certs[i] = (X509Certificate) cf.generateCertificate(stream);
+                        }
+                    }
+                    if (certs != null) {
+                        request.setAttribute
+                            (AprEndpoint.CERTIFICATE_KEY, certs);
+                    }
+                } catch (Exception e) {
+                    log.warn(sm.getString("http11processor.socket.ssl"), e);
+                }
+            }
+
+        } else if (actionCode == ActionCode.ACTION_REQ_SET_BODY_REPLAY) {
+            ByteChunk body = (ByteChunk) param;
+            
+            InputFilter savedBody = new SavedRequestInputFilter(body);
+            savedBody.setRequest(request);
+            
+            InternalAprInputBuffer internalBuffer = (InternalAprInputBuffer)
+                request.getInputBuffer();
+            internalBuffer.addActiveFilter(savedBody);
+        }
+
+    }
+
+
+    // ------------------------------------------------------ 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;
+        sendfileData = null;
+        if (ssl) {
+            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);
+        }
+
+        MimeHeaders headers = request.getMimeHeaders();
+
+        // Check connection header
+        MessageBytes connectionValueMB = headers.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 = headers.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 = headers.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 = headers.setValue("host");
+                hostMB.setBytes(uriB, uriBCStart + pos + 3,
+                                slashPos - pos - 3);
+            }
+
+        }
+
+        // Input filter setup
+        InputFilter[] inputFilters = inputBuffer.getFilters();
+
+        // Parse transfer-encoding header
+        MessageBytes transferEncodingValueMB = null;
+        if (http11)
+            transferEncodingValueMB = headers.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);
+            }
+        }
+
+        // Parse content-length header
+        long contentLength = request.getContentLengthLong();
+        if (contentLength >= 0 && !contentDelimitation) {
+            inputBuffer.addActiveFilter
+                (inputFilters[Constants.IDENTITY_FILTER]);
+            contentDelimitation = true;
+        }
+
+        MessageBytes valueMB = headers.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 
+            // (broken HTTP/1.0 or HTTP/1.1), assume
+            // the client is not broken and didn't send a body
+            inputBuffer.addActiveFilter
+                    (inputFilters[Constants.VOID_FILTER]);
+            contentDelimitation = true;
+        }
+
+        // Advertise sendfile support through a request attribute
+        if (endpoint.getUseSendfile()) {
+            request.setAttribute("org.apache.tomcat.sendfile.support", Boolean.TRUE);
+        }
+        
+    }
+
+
+    /**
+     * 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(endpoint.getPort());
+            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 (!ssl) {
+                // 80 - Default HTTP 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
+        long contentLength = response.getContentLengthLong();
+        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;
+        }
+
+        // Sendfile support
+        if (endpoint.getUseSendfile()) {
+            String fileName = (String) request.getAttribute("org.apache.tomcat.sendfile.filename");
+            if (fileName != null) {
+                // No entity body sent here
+                outputBuffer.addActiveFilter
+                    (outputFilters[Constants.VOID_FILTER]);
+                contentDelimitation = true;
+                sendfileData = new AprEndpoint.SendfileData();
+                sendfileData.fileName = fileName;
+                sendfileData.start = 
+                    ((Long) request.getAttribute("org.apache.tomcat.sendfile.start")).longValue();
+                sendfileData.end = 
+                    ((Long) request.getAttribute("org.apache.tomcat.sendfile.end")).longValue();
+            }
+        }
+        
+        // Check for compression
+        boolean useCompression = false;
+        if (entityBody && (compressionLevel > 0) && (sendfileData == null)) {
+            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);
+            }
+        }
+
+        long contentLength = response.getContentLengthLong();
+        if (contentLength != -1) {
+            headers.setValue("Content-Length").setLong(contentLength);
+            outputBuffer.addActiveFilter
+                (outputFilters[Constants.IDENTITY_FILTER]);
+            contentDelimitation = true;
+        } else {
+            if (entityBody && http11 && keepAlive) {
+                outputBuffer.addActiveFilter
+                    (outputFilters[Constants.CHUNKED_FILTER]);
+                contentDelimitation = true;
+                headers.addValue(Constants.TRANSFERENCODING).setString(Constants.CHUNKED);
+            } else {
+                outputBuffer.addActiveFilter
+                    (outputFilters[Constants.IDENTITY_FILTER]);
+            }
+        }
+
+        if (useCompression) {
+            outputBuffer.addActiveFilter(outputFilters[Constants.GZIP_FILTER]);
+            headers.setValue("Content-Encoding").setString("gzip");
+            // Make Proxies happy via Vary (from mod_deflate)
+            // Bugzilla 39402: http://issues.apache.org/bugzilla/show_bug.cgi?id=39402
+            // Either add a "Vary: Accept-Encoding" header or add Accept-Encoding to
+            // the existing header.
+            String varyHeaderValue = "";
+            MessageBytes varyHeaderValueBytes = headers.getValue("Vary");
+            if (varyHeaderValueBytes!=null && varyHeaderValueBytes.getString()!=null) {
+                varyHeaderValue = varyHeaderValueBytes.getString();
+            }
+            if ("".equals(varyHeaderValue)) {
+                varyHeaderValue="Accept-Encoding";
+            } else {
+                varyHeaderValue=varyHeaderValue+","+"Accept-Encoding";
+            }
+            headers.setValue("Vary").setString(varyHeaderValue);
+        }
+
+        // Add date header
+        headers.setValue("Date").setString(FastHttpDateFormat.getCurrentDate());
+
+        // FIXME: Add transfer encoding header
+
+        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) {
+            headers.addValue(Constants.CONNECTION).setString(Constants.CLOSE);
+        } else if (!http11 && !error) {
+            headers.addValue(Constants.CONNECTION).setString(Constants.KEEPALIVE);
+        }
+
+        // Build the response header
+        outputBuffer.sendStatus();
+
+        // Add server header
+        if (server != null) {
+            headers.setValue("Server").setString(server);
+        } else {
+            outputBuffer.write(Constants.SERVER_BYTES);
+        }
+
+        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/Http11AprProtocol.java b/connectors/http11/src/java/org/apache/coyote/http11/Http11AprProtocol.java
new file mode 100644
index 0000000..e21d184
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/Http11AprProtocol.java
@@ -0,0 +1,707 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.net.InetAddress;
+import java.net.URLEncoder;
+import java.util.Hashtable;
+import java.util.Iterator;
+
+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.AprEndpoint;
+import org.apache.tomcat.util.net.AprEndpoint.Handler;
+import org.apache.tomcat.util.res.StringManager;
+
+
+/**
+ * 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 Http11AprProtocol implements ProtocolHandler, MBeanRegistration
+{
+    public Http11AprProtocol() {
+        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);
+    }
+
+    public Iterator getAttributeNames() {
+        return attributes.keySet().iterator();
+    }
+
+    /**
+     * 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.setName(getName());
+        ep.setHandler(cHandler);
+
+        try {
+            ep.init();
+        } catch (Exception ex) {
+            log.error(sm.getString("http11protocol.endpoint.initerror"), ex);
+            throw ex;
+        }
+        if(log.isInfoEnabled())
+            log.info(sm.getString("http11protocol.init", getName()));
+
+    }
+
+    ObjectName tpOname;
+    ObjectName rgOname;
+
+    public void start() throws Exception {
+        if( this.domain != null ) {
+            try {
+                tpOname=new ObjectName
+                    (domain + ":" + "type=ThreadPool,name=" + getName());
+                Registry.getRegistry(null, null)
+                .registerComponent(ep, tpOname, null );
+            } 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.start();
+        } catch (Exception ex) {
+            log.error(sm.getString("http11protocol.endpoint.starterror"), ex);
+            throw ex;
+        }
+        if(log.isInfoEnabled())
+            log.info(sm.getString("http11protocol.start", getName()));
+    }
+
+    public void pause() throws Exception {
+        try {
+            ep.pause();
+        } catch (Exception ex) {
+            log.error(sm.getString("http11protocol.endpoint.pauseerror"), ex);
+            throw ex;
+        }
+        if(log.isInfoEnabled())
+            log.info(sm.getString("http11protocol.pause", getName()));
+    }
+
+    public void resume() throws Exception {
+        try {
+            ep.resume();
+        } catch (Exception ex) {
+            log.error(sm.getString("http11protocol.endpoint.resumeerror"), ex);
+            throw ex;
+        }
+        if(log.isInfoEnabled())
+            log.info(sm.getString("http11protocol.resume", getName()));
+    }
+
+    public void destroy() throws Exception {
+        if(log.isInfoEnabled())
+            log.info(sm.getString("http11protocol.stop", getName()));
+        ep.destroy();
+        if( tpOname!=null )
+            Registry.getRegistry(null, null).unregisterComponent(tpOname);
+        if( rgOname != null )
+            Registry.getRegistry(null, null).unregisterComponent(rgOname);
+    }
+
+    // -------------------- Properties--------------------
+    protected AprEndpoint ep=new AprEndpoint();
+    protected boolean secure;
+
+    protected Hashtable attributes = new Hashtable();
+
+    private int maxKeepAliveRequests=100; // as in Apache HTTPD server
+    private int timeout = 300000;   // 5 minutes as in Apache HTTPD server
+    private int maxSavePostSize = 4 * 1024;
+    private int maxHttpHeaderSize = 8 * 1024;
+    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;
+
+    private String server;
+
+    // -------------------- Pool setup --------------------
+
+    public int getMaxThreads() {
+        return ep.getMaxThreads();
+    }
+
+    public void setMaxThreads( int maxThreads ) {
+        ep.setMaxThreads(maxThreads);
+        setAttribute("maxThreads", "" + maxThreads);
+    }
+
+    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);
+    }
+
+    public int getFirstReadTimeout() {
+        return ep.getFirstReadTimeout();
+    }
+
+    public void setFirstReadTimeout( int i ) {
+        ep.setFirstReadTimeout(i);
+        setAttribute("firstReadTimeout", "" + i);
+    }
+
+    public int getPollTime() {
+        return ep.getPollTime();
+    }
+
+    public void setPollTime( int i ) {
+        ep.setPollTime(i);
+        setAttribute("pollTime", "" + i);
+    }
+
+    public void setPollerSize(int i) {
+        ep.setPollerSize(i); 
+        setAttribute("pollerSize", "" + i);
+    }
+    
+    public int getPollerSize() {
+        return ep.getPollerSize();
+    }
+    
+    public void setSendfileSize(int i) {
+        ep.setSendfileSize(i); 
+        setAttribute("sendfileSize", "" + i);
+    }
+    
+    public int getSendfileSize() {
+        return ep.getSendfileSize();
+    }
+    
+    public boolean getUseSendfile() {
+        return ep.getUseSendfile();
+    }
+
+    public void setUseSendfile(boolean useSendfile) {
+        ep.setUseSendfile(useSendfile);
+    }
+
+    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());
+    }
+
+    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 getMaxSavePostSize() {
+        return maxSavePostSize;
+    }
+
+    public void setMaxSavePostSize(int valueI) {
+        maxSavePostSize = valueI;
+        setAttribute("maxSavePostSize", "" + 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 String getProtocol() {
+        return getProperty("protocol");
+    }
+
+    public void setProtocol( String k ) {
+        setSecure(true);
+        setAttribute("protocol", k);
+    }
+
+    public boolean getSecure() {
+        return secure;
+    }
+
+    public void setSecure( boolean b ) {
+        secure=b;
+        setAttribute("secure", "" + b);
+    }
+
+    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);
+    }
+
+    /**
+     * Return the Keep-Alive policy for the connection.
+     */
+    public boolean getKeepAlive() {
+        return ((maxKeepAliveRequests != 0) && (maxKeepAliveRequests != 1));
+    }
+
+    /**
+     * Set the keep-alive policy for this connection.
+     */
+    public void setKeepAlive(boolean keepAlive) {
+        if (!keepAlive) {
+            setMaxKeepAliveRequests(1);
+        }
+    }
+
+    public int getSocketCloseDelay() {
+        return socketCloseDelay;
+    }
+
+    public void setSocketCloseDelay( int d ) {
+        socketCloseDelay=d;
+        setAttribute("socketCloseDelay", "" + d);
+    }
+
+    public void setServer( String server ) {
+        this.server = server;
+    }
+
+    public String getServer() {
+        return server;
+    }
+
+    public int getTimeout() {
+        return timeout;
+    }
+
+    public void setTimeout( int timeouts ) {
+        timeout = timeouts;
+        setAttribute("timeout", "" + timeouts);
+    }
+
+    // --------------------  SSL related properties --------------------
+
+    /**
+     * SSL engine.
+     */
+    public String getSSLEngine() { return ep.getSSLEngine(); }
+    public void setSSLEngine(String SSLEngine) { ep.setSSLEngine(SSLEngine); }
+
+
+    /**
+     * SSL protocol.
+     */
+    public String getSSLProtocol() { return ep.getSSLProtocol(); }
+    public void setSSLProtocol(String SSLProtocol) { ep.setSSLProtocol(SSLProtocol); }
+
+
+    /**
+     * SSL password (if a cert is encrypted, and no password has been provided, a callback
+     * will ask for a password).
+     */
+    public String getSSLPassword() { return ep.getSSLPassword(); }
+    public void setSSLPassword(String SSLPassword) { ep.setSSLPassword(SSLPassword); }
+
+
+    /**
+     * SSL cipher suite.
+     */
+    public String getSSLCipherSuite() { return ep.getSSLCipherSuite(); }
+    public void setSSLCipherSuite(String SSLCipherSuite) { ep.setSSLCipherSuite(SSLCipherSuite); }
+
+
+    /**
+     * SSL certificate file.
+     */
+    public String getSSLCertificateFile() { return ep.getSSLCertificateFile(); }
+    public void setSSLCertificateFile(String SSLCertificateFile) { ep.setSSLCertificateFile(SSLCertificateFile); }
+
+
+    /**
+     * SSL certificate key file.
+     */
+    public String getSSLCertificateKeyFile() { return ep.getSSLCertificateKeyFile(); }
+    public void setSSLCertificateKeyFile(String SSLCertificateKeyFile) { ep.setSSLCertificateKeyFile(SSLCertificateKeyFile); }
+
+
+    /**
+     * SSL certificate chain file.
+     */
+    public String getSSLCertificateChainFile() { return ep.getSSLCertificateChainFile(); }
+    public void setSSLCertificateChainFile(String SSLCertificateChainFile) { ep.setSSLCertificateChainFile(SSLCertificateChainFile); }
+
+
+    /**
+     * SSL CA certificate path.
+     */
+    public String getSSLCACertificatePath() { return ep.getSSLCACertificatePath(); }
+    public void setSSLCACertificatePath(String SSLCACertificatePath) { ep.setSSLCACertificatePath(SSLCACertificatePath); }
+
+
+    /**
+     * SSL CA certificate file.
+     */
+    public String getSSLCACertificateFile() { return ep.getSSLCACertificateFile(); }
+    public void setSSLCACertificateFile(String SSLCACertificateFile) { ep.setSSLCACertificateFile(SSLCACertificateFile); }
+
+
+    /**
+     * SSL CA revocation path.
+     */
+    public String getSSLCARevocationPath() { return ep.getSSLCARevocationPath(); }
+    public void setSSLCARevocationPath(String SSLCARevocationPath) { ep.setSSLCARevocationPath(SSLCARevocationPath); }
+
+
+    /**
+     * SSL CA revocation file.
+     */
+    public String getSSLCARevocationFile() { return ep.getSSLCARevocationFile(); }
+    public void setSSLCARevocationFile(String SSLCARevocationFile) { ep.setSSLCARevocationFile(SSLCARevocationFile); }
+
+
+    /**
+     * SSL verify client.
+     */
+    public String getSSLVerifyClient() { return ep.getSSLVerifyClient(); }
+    public void setSSLVerifyClient(String SSLVerifyClient) { ep.setSSLVerifyClient(SSLVerifyClient); }
+
+
+    /**
+     * SSL verify depth.
+     */
+    public int getSSLVerifyDepth() { return ep.getSSLVerifyDepth(); }
+    public void setSSLVerifyDepth(int SSLVerifyDepth) { ep.setSSLVerifyDepth(SSLVerifyDepth); }
+
+    // --------------------  Connection handler --------------------
+
+    static class Http11ConnectionHandler implements Handler {
+        Http11AprProtocol proto;
+        static int count=0;
+        RequestGroupInfo global=new RequestGroupInfo();
+        ThreadLocal localProcessor = new ThreadLocal();
+
+        Http11ConnectionHandler( Http11AprProtocol proto ) {
+            this.proto=proto;
+        }
+
+        public boolean process(long socket) {
+            Http11AprProcessor processor = null;
+            try {
+                processor = (Http11AprProcessor) localProcessor.get();
+                if (processor == null) {
+                    processor =
+                        new Http11AprProcessor(proto.maxHttpHeaderSize, proto.ep);
+                    processor.setAdapter(proto.adapter);
+                    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.setMaxSavePostSize(proto.maxSavePostSize);
+                    processor.setServer(proto.server);
+                    localProcessor.set(processor);
+                    if (proto.getDomain() != null) {
+                        synchronized (this) {
+                            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);
+                            } catch (Exception e) {
+                                log.warn("Error registering request");
+                            }
+                        }
+                    }
+                }
+
+                if (processor instanceof ActionHook) {
+                    ((ActionHook) processor).action(ActionCode.ACTION_START, null);
+                }
+
+                return processor.process(socket);
+
+            } catch(java.net.SocketException e) {
+                // SocketExceptions are normal
+                Http11AprProtocol.log.debug
+                    (sm.getString
+                     ("http11protocol.proto.socketexception.debug"), e);
+            } catch (java.io.IOException e) {
+                // IOExceptions are normal
+                Http11AprProtocol.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.
+                Http11AprProtocol.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);
+                }
+            }
+            return false;
+        }
+    }
+
+    protected static org.apache.commons.logging.Log log
+        = org.apache.commons.logging.LogFactory.getLog(Http11AprProtocol.class);
+
+    // -------------------- Various implementation classes --------------------
+
+    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/Http11BaseProtocol.java b/connectors/http11/src/java/org/apache/coyote/http11/Http11BaseProtocol.java
new file mode 100644
index 0000000..a7add88
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/Http11BaseProtocol.java
@@ -0,0 +1,747 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 java.util.Iterator;
+
+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.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;
+
+
+/**
+ * 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 Http11BaseProtocol implements ProtocolHandler
+{
+    public Http11BaseProtocol() {
+        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);
+    }
+
+    public Iterator getAttributeNames() {
+        return attributes.keySet().iterator();
+    }
+
+    /**
+     * 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;
+    }
+    
+    protected Http11ConnectionHandler createConnectionHandler() {
+        return new Http11ConnectionHandler( this );
+    }
+
+    /** Start the protocol
+     */
+    public void init() throws Exception {
+        cHandler = createConnectionHandler() ;
+        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;
+        }
+        if(log.isInfoEnabled())
+            log.info(sm.getString("http11protocol.init", getName()));
+
+    }
+
+    public void start() throws Exception {
+        try {
+            ep.startEndpoint();
+        } catch (Exception ex) {
+            log.error(sm.getString("http11protocol.endpoint.starterror"), ex);
+            throw ex;
+        }
+        if(log.isInfoEnabled())
+            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;
+        }
+        if(log.isInfoEnabled())
+            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;
+        }
+        if(log.isInfoEnabled())
+            log.info(sm.getString("http11protocol.resume", getName()));
+    }
+
+    public void destroy() throws Exception {
+        if(log.isInfoEnabled())
+            log.info(sm.getString("http11protocol.stop", getName()));
+        ep.stopEndpoint();
+    }
+
+    // -------------------- 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 maxSavePostSize = 4 * 1024;
+    private int maxHttpHeaderSize = 8 * 1024;
+    private String reportedname;
+    private int socketCloseDelay=-1;
+    private boolean disableUploadTimeout = true;
+    private int socketBuffer = 9000;
+    private Adapter adapter;
+    protected 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;
+
+    private String server;
+
+    // -------------------- Pool setup --------------------
+
+    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();
+    }
+
+    public void setStrategy(String strategy) {
+        ep.setStrategy(strategy);
+        setAttribute("strategy", strategy);
+      }
+
+      public String getStrategy() {
+        return ep.getStrategy();
+      }
+
+    // -------------------- 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);
+    }
+
+    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());
+    }
+
+    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;
+        setSecure(true);
+        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 getMaxSavePostSize() {
+        return maxSavePostSize;
+    }
+
+    public void setMaxSavePostSize(int valueI) {
+        maxSavePostSize = valueI;
+        setAttribute("maxSavePostSize", "" + 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 ) {
+        setSecure(true);
+        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);
+    }
+
+    /**
+     * Return the Keep-Alive policy for the connection.
+     */
+    public boolean getKeepAlive() {
+        return ((maxKeepAliveRequests != 0) && (maxKeepAliveRequests != 1));
+    }
+
+    /**
+     * Set the keep-alive policy for this connection.
+     */
+    public void setKeepAlive(boolean keepAlive) {
+        if (!keepAlive) {
+            setMaxKeepAliveRequests(1);
+        }
+    }
+
+    public int getSocketCloseDelay() {
+        return socketCloseDelay;
+    }
+
+    public void setSocketCloseDelay( int d ) {
+        socketCloseDelay=d;
+        setAttribute("socketCloseDelay", "" + d);
+    }
+
+    public void setServer( String server ) {
+        this.server = server;
+    }
+
+    public String getServer() {
+        return server;
+    }
+
+
+    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;
+        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 Http11ConnectionHandler implements TcpConnectionHandler {
+        Http11BaseProtocol proto;
+        static int count=0;
+        RequestGroupInfo global=new RequestGroupInfo();
+
+        Http11ConnectionHandler( Http11BaseProtocol 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.setEndpoint( proto.ep );
+            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.setMaxSavePostSize( proto.maxSavePostSize );
+            processor.setServer( proto.server );
+
+            thData[Http11BaseProtocol.THREAD_DATA_PROCESSOR]=processor;
+
+            return  thData;
+        }
+
+        public void processConnection(TcpConnection connection,
+                      Object thData[]) {
+            Socket socket=null;
+            Http11Processor  processor=null;
+            try {
+                processor=(Http11Processor)thData[Http11BaseProtocol.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
+                Http11BaseProtocol.log.debug
+                    (sm.getString
+                     ("http11protocol.proto.socketexception.debug"), e);
+            } catch (IOException e) {
+                // IOExceptions are normal
+                Http11BaseProtocol.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.
+                Http11BaseProtocol.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(Http11BaseProtocol.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;
+            }
+        }
+    }
+
+}
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..eb30acc
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/Http11Processor.java
@@ -0,0 +1,1733 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.SavedRequestInputFilter;
+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.PoolTcpEndpoint;
+import org.apache.tomcat.util.net.SSLSupport;
+import org.apache.tomcat.util.res.StringManager;
+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);
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // ----------------------------------------------------------- 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 saved post size.
+     */
+    protected int maxSavePostSize = 4 * 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;
+
+
+    /**
+     * Associated endpoint.
+     */
+    protected PoolTcpEndpoint endpoint;
+
+
+    /**
+     * Allow a customized the server header for the tin-foil hat folks.
+     */
+    protected String server = null;
+
+
+    // ------------------------------------------------------------- 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;
+    }
+
+    
+    public void setEndpoint(PoolTcpEndpoint endpoint) {
+        this.endpoint = endpoint;
+    }
+
+
+    /**
+     * 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(sm.getString("http11processor.regexp.error", 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(sm.getString("http11processor.filter.unknown", className));
+            }
+        } catch (Exception e) {
+            log.error(sm.getString("http11processor.filter.error", 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(sm.getString("http11processor.regexp.error", 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 setMaxSavePostSize(int msps) {
+        maxSavePostSize = msps;
+    }
+
+
+    /**
+     * Return the maximum size of a POST which will be buffered in SSL mode.
+     */
+    public int getMaxSavePostSize() {
+        return maxSavePostSize;
+    }
+
+
+    /**
+     * 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;
+    }
+
+
+    /**
+     * Set the server header name.
+     */
+    public void setServer( String server ) {
+        if (server==null || server.equals("")) {
+            this.server = null;
+        } else {
+            this.server = server;
+        }
+    }
+
+    /**
+     * Get the server header name.
+     */
+    public String getServer() {
+        return server;
+    }
+
+
+    /** 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;
+        localName = 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();
+        int oldSoTimeout = soTimeout;
+
+        int threadRatio = 0;
+        if (threadPool.getCurrentThreadsBusy() > 0) {
+            threadRatio = (threadPool.getCurrentThreadsBusy() * 100)
+                / threadPool.getMaxThreads();
+        } else {
+            threadRatio = (endpoint.getCurrentThreadsBusy() * 100)
+                / endpoint.getMaxThreads();
+        }
+        if ((threadRatio > 33) && (threadRatio <= 66)) {
+            soTimeout = soTimeout / 2;
+        } else if ((threadRatio > 66) && (threadRatio <= 90)) {
+            soTimeout = soTimeout / 3;
+            keepAliveLeft = 1;
+        } else if (threadRatio > 90) {
+            soTimeout = soTimeout / 20;
+            keepAliveLeft = 1;
+        }
+        
+        if (soTimeout != oldSoTimeout) {
+            try {
+                socket.setSoTimeout(soTimeout);
+            } catch (Throwable t) {
+                log.debug(sm.getString("http11processor.socket.timeout"), t);
+                error = true;
+            }
+        }
+
+        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) {
+                if (log.isDebugEnabled()) {
+                    log.debug(sm.getString("http11processor.header.parse"), t);
+                }
+                // 400 - Bad Request
+                if (log.isDebugEnabled()) {
+                    log.debug(sm.getString("http11processor.request.prepare")+
+                              " host header missing");
+                }
+                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) {
+                if (log.isDebugEnabled()) {
+                    log.debug(sm.getString("http11processor.request.prepare"), 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(sm.getString("http11processor.request.process"), 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(sm.getString("http11processor.request.finish"), 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(sm.getString("http11processor.response.finish"), 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(sm.getString("http11processor.socket.ssl"), 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();
+                }
+                if(remoteHost == null) {
+                    if(remoteAddr != null) {
+                        remoteHost = remoteAddr;
+                    } else { // all we can do is punt
+                        request.remoteHost().recycle();
+                    }
+                }
+            }
+            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(maxSavePostSize);
+                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(sm.getString("http11processor.socket.ssl"), e);
+                }
+            }
+        } else if (actionCode == ActionCode.ACTION_REQ_SET_BODY_REPLAY) {
+            ByteChunk body = (ByteChunk) param;
+            
+            InputFilter savedBody = new SavedRequestInputFilter(body);
+            savedBody.setRequest(request);
+
+            InternalInputBuffer internalBuffer = (InternalInputBuffer)
+                request.getInputBuffer();
+            internalBuffer.addActiveFilter(savedBody);
+        }
+
+    }
+
+
+    // ------------------------------------------------------ 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
+            if (log.isDebugEnabled()) {
+                log.debug(sm.getString("http11processor.request.prepare")+
+                          " Unsupported HTTP version \""+protocolMB+"\"");
+            }
+            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);
+        }
+
+        MimeHeaders headers = request.getMimeHeaders();
+
+        // Check connection header
+        MessageBytes connectionValueMB = headers.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 = headers.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 = headers.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 = headers.setValue("host");
+                hostMB.setBytes(uriB, uriBCStart + pos + 3,
+                                slashPos - pos - 3);
+            }
+
+        }
+
+        // Input filter setup
+        InputFilter[] inputFilters = inputBuffer.getFilters();
+
+        // Parse transfer-encoding header
+        MessageBytes transferEncodingValueMB = null;
+        if (http11)
+            transferEncodingValueMB = headers.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
+                if (log.isDebugEnabled()) {
+                    log.debug(sm.getString("http11processor.request.prepare")+
+                              " Unsupported transfer encoding \""+encodingName+"\"");
+                }
+                response.setStatus(501);
+            }
+        }
+
+        // Parse content-length header
+        long contentLength = request.getContentLengthLong();
+        if (contentLength >= 0 && !contentDelimitation) {
+            inputBuffer.addActiveFilter
+                (inputFilters[Constants.IDENTITY_FILTER]);
+            contentDelimitation = true;
+        }
+
+        MessageBytes valueMB = headers.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 
+            // (broken HTTP/1.0 or HTTP/1.1), assume
+            // the client is not broken and didn't send a body
+            inputBuffer.addActiveFilter
+                    (inputFilters[Constants.VOID_FILTER]);
+            contentDelimitation = true;
+        }
+
+    }
+
+
+    /**
+     * 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.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 HTTP 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
+        long contentLength = response.getContentLengthLong();
+        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);
+            }
+        }
+
+        long contentLength = response.getContentLengthLong();
+        if (contentLength != -1) {
+            headers.setValue("Content-Length").setLong(contentLength);
+            outputBuffer.addActiveFilter
+                (outputFilters[Constants.IDENTITY_FILTER]);
+            contentDelimitation = true;
+        } else {
+            if (entityBody && http11 && keepAlive) {
+                outputBuffer.addActiveFilter
+                    (outputFilters[Constants.CHUNKED_FILTER]);
+                contentDelimitation = true;
+                headers.addValue(Constants.TRANSFERENCODING).setString(Constants.CHUNKED);
+            } else {
+                outputBuffer.addActiveFilter
+                    (outputFilters[Constants.IDENTITY_FILTER]);
+            }
+        }
+
+        if (useCompression) {
+            outputBuffer.addActiveFilter(outputFilters[Constants.GZIP_FILTER]);
+            headers.setValue("Content-Encoding").setString("gzip");
+            // Make Proxies happy via Vary (from mod_deflate)
+            headers.setValue("Vary").setString("Accept-Encoding");
+        }
+
+        // Add date header
+        String date = null;
+        if (System.getSecurityManager() != null){
+            date = (String)AccessController.doPrivileged(
+                    new PrivilegedAction() {
+                        public Object run(){
+                            return FastHttpDateFormat.getCurrentDate();
+                        }
+                    }
+            );
+        } else {
+            date = FastHttpDateFormat.getCurrentDate();
+        }
+        headers.setValue("Date").setString(date);
+
+        // FIXME: Add transfer encoding header
+
+        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) {
+            headers.addValue(Constants.CONNECTION).setString(Constants.CLOSE);
+        } else if (!http11 && !error) {
+            headers.addValue(Constants.CONNECTION).setString(Constants.KEEPALIVE);
+        }
+
+        // Build the response header
+        outputBuffer.sendStatus();
+
+        // Add server header
+        if (server != null) {
+            headers.setValue("Server").setString(server);
+        } else {
+            outputBuffer.write(Constants.SERVER_BYTES);
+        }
+
+        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..51b30bb
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/Http11Protocol.java
@@ -0,0 +1,191 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 javax.management.MBeanRegistration;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.apache.commons.modeler.Registry;
+import org.apache.coyote.RequestInfo;
+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 extends Http11BaseProtocol implements MBeanRegistration
+{
+    public Http11Protocol() {
+    }
+    
+    protected Http11ConnectionHandler createConnectionHandler() {
+        return new JmxHttp11ConnectionHandler( this ) ;
+    }
+
+    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());
+                if ("ms".equals(getStrategy())) {
+                    Registry.getRegistry(null, null)
+                        .registerComponent(ep, tpOname, null );
+                } else {
+                    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 );
+        }
+
+        super.start();
+    }
+
+    public void destroy() throws Exception {
+        super.destroy();
+        if( tpOname!=null )
+            Registry.getRegistry(null, null).unregisterComponent(tpOname);
+        if( rgOname != null )
+            Registry.getRegistry(null, null).unregisterComponent(rgOname);
+    }
+
+    // --------------------  Connection handler --------------------
+
+    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[Http11BaseProtocol.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 JmxHttp11ConnectionHandler extends Http11ConnectionHandler  {
+        Http11Protocol proto;
+        static int count=0;
+
+        JmxHttp11ConnectionHandler( Http11Protocol proto ) {
+            super(proto);
+            this.proto = proto ;
+        }
+
+        public void setAttribute( String name, Object value ) {
+        }
+
+        public void setServer( Object o ) {
+        }
+
+        public Object[] init() {
+
+            Object thData[]=super.init();
+
+            // was set up by supper
+            Http11Processor  processor = (Http11Processor)
+                    thData[ Http11BaseProtocol.THREAD_DATA_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[Http11BaseProtocol.THREAD_DATA_OBJECT_NAME]=rpName;
+                } catch( Exception ex ) {
+                    log.warn("Error registering request");
+                }
+            }
+
+            return  thData;
+        }
+    }
+
+    // -------------------- Various implementation classes --------------------
+
+
+    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..661290f
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/InputFilter.java
@@ -0,0 +1,82 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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/InternalAprInputBuffer.java b/connectors/http11/src/java/org/apache/coyote/http11/InternalAprInputBuffer.java
new file mode 100644
index 0000000..c98731a
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/InternalAprInputBuffer.java
@@ -0,0 +1,874 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.EOFException;
+import java.net.SocketTimeoutException;
+import java.nio.ByteBuffer;
+
+import org.apache.tomcat.jni.Socket;
+import org.apache.tomcat.jni.Status;
+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 InternalAprInputBuffer implements InputBuffer {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Alternate constructor.
+     */
+    public InternalAprInputBuffer(Request request, int headerBufferSize, 
+                                  long readTimeout) {
+
+        this.request = request;
+        headers = request.getMimeHeaders();
+
+        headerBuffer1 = new byte[headerBufferSize];
+        headerBuffer2 = new byte[headerBufferSize];
+        bodyBuffer = new byte[headerBufferSize];
+        buf = headerBuffer1;
+        bbuf = ByteBuffer.allocateDirect(headerBufferSize);
+
+        headerBuffer = new char[headerBufferSize];
+        ascbuf = headerBuffer;
+
+        inputStreamInputBuffer = new SocketInputBuffer();
+
+        filterLibrary = new InputFilter[0];
+        activeFilters = new InputFilter[0];
+        lastActiveFilter = -1;
+
+        parsingHeader = true;
+        swallowInput = true;
+        
+        if (readTimeout < 0) {
+            this.readTimeout = -1;
+        } else {
+            this.readTimeout = readTimeout * 1000;
+        }
+
+    }
+
+
+    // -------------------------------------------------------------- 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;
+
+
+    /**
+     * Direct byte buffer used to perform actual reading.
+     */
+    protected ByteBuffer bbuf;
+
+
+    /**
+     * Underlying socket.
+     */
+    protected long socket;
+
+
+    /**
+     * 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;
+
+
+    /**
+     * The socket timeout used when reading the first block of the request
+     * header.
+     */
+    protected long readTimeout;
+    
+    
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Set the underlying socket.
+     */
+    public void setSocket(long socket) {
+        this.socket = socket;
+        Socket.setrbb(this.socket, bbuf);
+    }
+
+
+    /**
+     * Get the underlying socket input stream.
+     */
+    public long getSocket() {
+        return socket;
+    }
+
+
+    /**
+     * Add an input filter to the filter library.
+     */
+    public void addFilter(InputFilter filter) {
+
+        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();
+
+        socket = 0;
+        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.
+     * @return true if data is properly fed; false if no data is available 
+     * immediately and thread should be freed
+     */
+    public boolean parseRequestLine(boolean useAvailableData)
+        throws IOException {
+
+        int start = 0;
+
+        //
+        // Skipping blank lines
+        //
+
+        byte chr = 0;
+        do {
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (useAvailableData) {
+                    return false;
+                }
+                if (readTimeout == -1) {
+                    if (!fill())
+                        throw new EOFException(sm.getString("iib.eof.error"));
+                } else {
+                    // Do a simple read with a short timeout
+                    bbuf.clear();
+                    int nRead = Socket.recvbbt
+                    (socket, 0, buf.length - lastValid, readTimeout);
+                    if (nRead > 0) {
+                        bbuf.limit(nRead);
+                        bbuf.get(buf, pos, nRead);
+                        lastValid = pos + nRead;
+                    } else {
+                        if ((-nRead) == Status.ETIMEDOUT || (-nRead) == Status.TIMEUP) {
+                            return false;
+                        } else {
+                            throw new IOException(sm.getString("iib.failedread"));
+                        }
+                    }
+                }
+            }
+
+            chr = buf[pos++];
+
+        } while ((chr == Constants.CR) || (chr == Constants.LF));
+
+        pos--;
+
+        // Mark the current buffer position
+        start = pos;
+
+        if (pos >= lastValid) {
+            if (useAvailableData) {
+                return false;
+            }
+            if (readTimeout == -1) {
+                if (!fill())
+                    throw new EOFException(sm.getString("iib.eof.error"));
+            } else {
+                // Do a simple read with a short timeout
+                bbuf.clear();
+                int nRead = Socket.recvbbt
+                    (socket, 0, buf.length - lastValid, readTimeout);
+                if (nRead > 0) {
+                    bbuf.limit(nRead);
+                    bbuf.get(buf, pos, nRead);
+                    lastValid = pos + nRead;
+                } else {
+                    if ((-nRead) == Status.ETIMEDOUT || (-nRead) == Status.TIMEUP) {
+                        return false;
+                    } else {
+                        throw new IOException(sm.getString("iib.failedread"));
+                    }
+                }
+            }
+        }
+
+        //
+        // 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("");
+        }
+        
+        return true;
+
+    }
+
+
+    /**
+     * 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"));
+            }
+
+            bbuf.clear();
+            nRead = Socket.recvbb
+                (socket, 0, buf.length - lastValid);
+            if (nRead > 0) {
+                bbuf.limit(nRead);
+                bbuf.get(buf, pos, nRead);
+                lastValid = pos + nRead;
+            } else {
+                if ((-nRead) == Status.EAGAIN) {
+                    return false;
+                } else {
+                    throw new IOException(sm.getString("iib.failedread"));
+                }
+            }
+
+        } else {
+
+            buf = bodyBuffer;
+            pos = 0;
+            lastValid = 0;
+            bbuf.clear();
+            nRead = Socket.recvbb
+                (socket, 0, buf.length);
+            if (nRead > 0) {
+                bbuf.limit(nRead);
+                bbuf.get(buf, 0, nRead);
+                lastValid = nRead;
+            } else {
+                if ((-nRead) == Status.ETIMEDOUT || (-nRead) == Status.TIMEUP) {
+                    throw new SocketTimeoutException(sm.getString("iib.failedread"));
+                } else {
+                    throw new IOException(sm.getString("iib.failedread"));
+                }
+            }
+
+        }
+
+        return (nRead > 0);
+
+    }
+
+
+    // ------------------------------------- InputStreamInputBuffer Inner Class
+
+
+    /**
+     * This class is an input buffer which will read its data from an input
+     * stream.
+     */
+    protected class SocketInputBuffer 
+        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/InternalAprOutputBuffer.java b/connectors/http11/src/java/org/apache/coyote/http11/InternalAprOutputBuffer.java
new file mode 100644
index 0000000..949c36e
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/InternalAprOutputBuffer.java
@@ -0,0 +1,747 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.nio.ByteBuffer;
+
+import org.apache.tomcat.jni.Socket;
+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 InternalAprOutputBuffer 
+    implements OutputBuffer {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Default constructor.
+     */
+    public InternalAprOutputBuffer(Response response) {
+        this(response, Constants.DEFAULT_HTTP_HEADER_BUFFER_SIZE);
+    }
+
+
+    /**
+     * Alternate constructor.
+     */
+    public InternalAprOutputBuffer(Response response, int headerBufferSize) {
+
+        this.response = response;
+        headers = response.getMimeHeaders();
+
+        headerBuffer = new byte[headerBufferSize];
+        buf = headerBuffer;
+
+        bbuf = ByteBuffer.allocateDirect((headerBufferSize / 1500 + 1) * 1500);
+
+        outputStreamOutputBuffer = new SocketOutputBuffer();
+
+        filterLibrary = new OutputFilter[0];
+        activeFilters = new OutputFilter[0];
+        lastActiveFilter = -1;
+
+        committed = false;
+        finished = false;
+
+        // Cause loading of HttpMessages
+        HttpMessages.getMessage(200);
+
+    }
+
+
+    // -------------------------------------------------------------- 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 socket.
+     */
+    protected long socket;
+
+
+    /**
+     * 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;
+
+
+    /**
+     * Direct byte buffer used for writing.
+     */
+    protected ByteBuffer bbuf = null;
+
+    
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Set the underlying socket.
+     */
+    public void setSocket(long socket) {
+        this.socket = socket;
+        Socket.setsbb(this.socket, bbuf);
+    }
+
+
+    /**
+     * Get the underlying socket input stream.
+     */
+    public long getSocket() {
+        return socket;
+    }
+
+
+    /**
+     * Set the socket buffer size.
+     */
+    public void setSocketBuffer(int socketBufferSize) {
+        // FIXME: Remove
+    }
+
+
+    /**
+     * 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
+        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();
+        bbuf.clear();
+
+        socket = 0;
+        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();
+
+        // 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();
+
+        flushBuffer();
+
+        finished = true;
+
+    }
+
+
+    // ------------------------------------------------ HTTP/1.1 Output Methods
+
+
+    /**
+     * Send an acknoledgement.
+     */
+    public void sendAck()
+        throws IOException {
+
+        if (!committed) {
+            if (Socket.send(socket, Constants.ACK_BYTES, 0, Constants.ACK_BYTES.length) < 0)
+                throw new IOException(sm.getString("iib.failedwrite"));
+        }
+
+    }
+
+
+    /**
+     * Send the response status line.
+     */
+    public void sendStatus() {
+
+        // Write protocol name
+        write(Constants.HTTP_11_BYTES);
+        buf[pos++] = Constants.SP;
+
+        // Write status code
+        int status = response.getStatus();
+        switch (status) {
+        case 200:
+            write(Constants._200_BYTES);
+            break;
+        case 400:
+            write(Constants._400_BYTES);
+            break;
+        case 404:
+            write(Constants._404_BYTES);
+            break;
+        default:
+            write(status);
+        }
+
+        buf[pos++] = Constants.SP;
+
+        // Write message
+        String message = response.getMessage();
+        if (message == null) {
+            write(HttpMessages.getMessage(status));
+        } else {
+            write(message);
+        }
+
+        // End the response status line
+        buf[pos++] = Constants.CR;
+        buf[pos++] = Constants.LF;
+
+    }
+
+
+    /**
+     * Send a header.
+     * 
+     * @param name Header name
+     * @param value Header value
+     */
+    public void sendHeader(MessageBytes name, MessageBytes value) {
+
+        write(name);
+        buf[pos++] = Constants.COLON;
+        buf[pos++] = Constants.SP;
+        write(value);
+        buf[pos++] = Constants.CR;
+        buf[pos++] = Constants.LF;
+
+    }
+
+
+    /**
+     * Send a header.
+     * 
+     * @param name Header name
+     * @param value Header value
+     */
+    public void sendHeader(ByteChunk name, ByteChunk value) {
+
+        write(name);
+        buf[pos++] = Constants.COLON;
+        buf[pos++] = Constants.SP;
+        write(value);
+        buf[pos++] = Constants.CR;
+        buf[pos++] = Constants.LF;
+
+    }
+
+
+    /**
+     * Send a header.
+     * 
+     * @param name Header name
+     * @param value Header value
+     */
+    public void sendHeader(String name, String value) {
+
+        write(name);
+        buf[pos++] = Constants.COLON;
+        buf[pos++] = Constants.SP;
+        write(value);
+        buf[pos++] = Constants.CR;
+        buf[pos++] = Constants.LF;
+
+    }
+
+
+    /**
+     * End the header block.
+     */
+    public void endHeaders() {
+
+        buf[pos++] = Constants.CR;
+        buf[pos++] = Constants.LF;
+
+    }
+
+
+    // --------------------------------------------------- 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
+            bbuf.put(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 <= 31) && (c != 9)) {
+                c = ' ';
+            } else 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
+     */
+    public 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 <= 31) && (c != 9)) {
+                c = ' ';
+            } else 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.
+     */
+    protected void flushBuffer()
+        throws IOException {
+        if (bbuf.position() > 0) {
+            if (Socket.sendbb(socket, 0, bbuf.position()) < 0) {
+                throw new IOException();
+            }
+            bbuf.clear();
+        }
+    }
+
+
+    // ----------------------------------- OutputStreamOutputBuffer Inner Class
+
+
+    /**
+     * This class is an output buffer which will write data to an output
+     * stream.
+     */
+    protected class SocketOutputBuffer 
+        implements OutputBuffer {
+
+
+        /**
+         * Write chunk.
+         */
+        public int doWrite(ByteChunk chunk, Response res) 
+            throws IOException {
+
+            int len = chunk.getLength();
+            int start = chunk.getStart();
+            byte[] b = chunk.getBuffer();
+            while (len > 0) {
+                int thisTime = len;
+                if (bbuf.position() == bbuf.capacity()) {
+                    flushBuffer();
+                }
+                if (thisTime > bbuf.capacity() - bbuf.position()) {
+                    thisTime = bbuf.capacity() - bbuf.position();
+                }
+                bbuf.put(b, start, thisTime);
+                len = len - thisTime;
+                start = start + thisTime;
+            }
+            return chunk.getLength();
+
+        }
+
+
+    }
+
+
+}
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..67a9ca7
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/InternalInputBuffer.java
@@ -0,0 +1,794 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..4acce12
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/InternalOutputBuffer.java
@@ -0,0 +1,784 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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(Constants.HTTP_11_BYTES);
+        buf[pos++] = Constants.SP;
+
+        // Write status code
+        int status = response.getStatus();
+        switch (status) {
+        case 200:
+            write(Constants._200_BYTES);
+            break;
+        case 400:
+            write(Constants._400_BYTES);
+            break;
+        case 404:
+            write(Constants._404_BYTES);
+            break;
+        default:
+            write(status);
+        }
+
+        buf[pos++] = Constants.SP;
+
+        // 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(){
+                        buf[pos++] = Constants.CR;
+                        buf[pos++] = Constants.LF;
+                        return null;
+                    }
+                }
+           );
+        } else {
+            buf[pos++] = Constants.CR;
+            buf[pos++] = Constants.LF;
+        }
+
+    }
+
+    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);
+        buf[pos++] = Constants.COLON;
+        buf[pos++] = Constants.SP;
+        write(value);
+        buf[pos++] = Constants.CR;
+        buf[pos++] = Constants.LF;
+
+    }
+
+
+    /**
+     * Send a header.
+     * 
+     * @param name Header name
+     * @param value Header value
+     */
+    public void sendHeader(ByteChunk name, ByteChunk value) {
+
+        write(name);
+        buf[pos++] = Constants.COLON;
+        buf[pos++] = Constants.SP;
+        write(value);
+        buf[pos++] = Constants.CR;
+        buf[pos++] = Constants.LF;
+
+    }
+
+
+    /**
+     * Send a header.
+     * 
+     * @param name Header name
+     * @param value Header value
+     */
+    public void sendHeader(String name, String value) {
+
+        write(name);
+        buf[pos++] = Constants.COLON;
+        buf[pos++] = Constants.SP;
+        write(value);
+        buf[pos++] = Constants.CR;
+        buf[pos++] = Constants.LF;
+
+    }
+
+
+    /**
+     * End the header block.
+     */
+    public void endHeaders() {
+
+        buf[pos++] = Constants.CR;
+        buf[pos++] = Constants.LF;
+
+    }
+
+
+    // --------------------------------------------------- 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 <= 31) && (c != 9)) {
+                c = ' ';
+            } else 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
+     */
+    public 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 <= 31) && (c != 9)) {
+                c = ' ';
+            } else 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..75a4537
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/LocalStrings.properties
@@ -0,0 +1,52 @@
+# $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
+#
+
+http11processor.regexp.error=Error parsing regular expression {0}
+http11processor.filter.unknown=Unknown filter {0}
+http11processor.filter.error=Error intializing filter {0}
+http11processor.header.parse=Error parsing HTTP request header
+http11processor.request.prepare=Error preparing request
+http11processor.request.process=Error processing request
+http11processor.request.finish=Error finishing request
+http11processor.response.finish=Error finishing response
+http11processor.socket.info=Exception getting socket information
+http11processor.socket.ssl=Exception getting SSL attributes
+http11processor.socket.timeout=Error setting socket timeout
+
+#
+# InternalInputBuffer
+#
+
+iib.eof.error=Unexpected EOF read on the socket
+iib.failedread=Exception filling buffer with data from underlying input stream: not an EAGAIN status, so perhaps disconnected client?
+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..c4efb61
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/OutputFilter.java
@@ -0,0 +1,83 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..768eebb
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/filters/BufferedInputFilter.java
@@ -0,0 +1,123 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..947b4e5
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
@@ -0,0 +1,326 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..526c5a7
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/filters/ChunkedOutputFilter.java
@@ -0,0 +1,187 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..f430b08
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/filters/GzipOutputFilter.java
@@ -0,0 +1,171 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..332ac15
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/filters/IdentityInputFilter.java
@@ -0,0 +1,202 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..6366201
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/filters/IdentityOutputFilter.java
@@ -0,0 +1,190 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.getContentLengthLong();
+        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/SavedRequestInputFilter.java b/connectors/http11/src/java/org/apache/coyote/http11/filters/SavedRequestInputFilter.java
new file mode 100644
index 0000000..b49fbe5
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/filters/SavedRequestInputFilter.java
@@ -0,0 +1,103 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.InputBuffer;
+import org.apache.coyote.http11.InputFilter;
+import org.apache.tomcat.util.buf.ByteChunk;
+
+/**
+ * Input filter responsible for replaying the request body when restoring the
+ * saved request after FORM authentication.
+ */
+public class SavedRequestInputFilter implements InputFilter {
+
+	/**
+     * The original request body.
+	 */
+    protected ByteChunk input = null;
+
+    /**
+     * Create a new SavedRequestInputFilter.
+     * 
+     * @param input The saved request body to be replayed.
+     */
+    public SavedRequestInputFilter(ByteChunk input) {
+        this.input = input;
+    }
+
+    /**
+     * Read bytes.
+     */
+    public int doRead(ByteChunk chunk, org.apache.coyote.Request request)
+            throws IOException {
+        int writeLength = 0;
+        
+        if (chunk.getLimit() > 0 && chunk.getLimit() < input.getLength()) {
+            writeLength = chunk.getLimit();
+        } else {
+        	writeLength = input.getLength();
+        }
+        
+        if(input.getOffset()>= input.getEnd())
+            return -1;
+        
+        input.substract(chunk.getBuffer(), 0, writeLength);
+        chunk.setOffset(0);
+        chunk.setEnd(writeLength);
+        
+        return writeLength;
+    }
+
+    /**
+     * Set the content length on the request.
+     */
+    public void setRequest(org.apache.coyote.Request request) {
+        request.setContentLength(input.getLength());
+    }
+
+    /**
+     * Make the filter ready to process the next request.
+     */
+    public void recycle() {
+        input = null;
+    }
+
+    /**
+     * Return the name of the associated encoding; here, the value is null.
+     */
+    public ByteChunk getEncodingName() {
+        return null;
+    }
+
+    /**
+     * Set the next buffer in the filter pipeline (has no effect).
+     */
+    public void setBuffer(InputBuffer buffer) {
+    }
+
+    /**
+     * End the current request (has no effect).
+     */
+    public long end() throws IOException {
+        return 0;
+    }
+
+}
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..1b527e4
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/filters/VoidInputFilter.java
@@ -0,0 +1,119 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..72569c5
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/filters/VoidOutputFilter.java
@@ -0,0 +1,128 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..348bb0c
--- /dev/null
+++ b/connectors/http11/src/test/java/org/apache/coyote/http11/FileTester.java
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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..de33155
--- /dev/null
+++ b/connectors/http11/src/test/java/org/apache/coyote/http11/RandomAdapter.java
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 {
+
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( RandomAdapter.class );
+
+    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
+            if (log.isDebugEnabled())
+                log.debug("Response 0");
+            break;
+
+        case 1:
+
+            // 1) Set content length, and write the appropriate content
+            if (log.isDebugEnabled())
+                log.debug("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
+            if (log.isDebugEnabled())
+                log.debug("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
+            if (log.isDebugEnabled())
+                log.debug("Response 3");
+            res.setStatus(204);
+            nRead = req.doRead(bc);
+            res.setHeader("Info", "Read " + nRead + " bytes");
+            break;
+
+        case 4:
+
+            // 4) Do a request dump
+            if (log.isDebugEnabled())
+                log.debug("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
+            if (log.isDebugEnabled())
+                log.debug("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..75a3880
--- /dev/null
+++ b/connectors/http11/src/test/java/org/apache/coyote/http11/TestAdapter.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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..86785be
--- /dev/null
+++ b/connectors/jk/BUILD.txt
@@ -0,0 +1,19 @@
+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 native/BUILDING file in this
+source distribution.
+
+For the impatient Apache admins:
+$> cd native
+$> ./configure --with-apxs=/usr/sbin/apxs (or where ever the apxs/apxs2 is)
+$> make
+$> su -c 'make install'
+
+For the impatient SunONE admins:
+$> cd native
+$> ./configure --enable-netscape
+$> cd netscape
+$> make -f Makefile.solaris
diff --git a/connectors/jk/HOWTO-RELEASE b/connectors/jk/HOWTO-RELEASE
new file mode 100644
index 0000000..2f803ec
--- /dev/null
+++ b/connectors/jk/HOWTO-RELEASE
@@ -0,0 +1,165 @@
+		How to do a mod_jk 1.2 release
+
+Check out a clean copy of tomcat/connectors from subversion to
+make sure you don't have any lingering configure or build files.
+This will make sure that the source distribution created is clean.
+
+We assume, that you check out to a directory tomcat-connectors.
+
+If you haven't already, add your public PGP key to
+tomcat-connectors/KEYS.
+
+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/index.xml
+xdocs/news/<THISYEAR0101>.xml
+native/STATUS.txt
+
+Update the JK_VERISRELEASE define in jk/native/common/jk_version.h, here is
+a svn diff that shows what I changed:
+
+Index: native/common/jk_version.h
+===================================================================
+--- native/common/jk_version.h  (revision 417260)
++++ native/common/jk_version.h  (working copy)
+@@ -32,7 +32,7 @@
+ #define JK_VERBETA      0
+ #define JK_BETASTRING   "0"
+ /* set JK_VERISRELEASE to 1 when release (do not forget to commit!) */
+-#define JK_VERISRELEASE 0
++#define JK_VERISRELEASE 1
+ #define JK_VERRC        0
+ #define JK_RCSTRING     "0"
+
+After updating revision numbers, commit your changes to subversion.
+
+Tag and branch tomcat-connectors in subversion
+----------------------------------------------
+
+Use the pattern below for branching and tagging the tomcat-connectors directory.
+
+TAG=JK_{MAJOR_REVISION}_{MINOR_REVISION}_{RELEASE}
+
+svn copy \
+   https://svn.apache.org/repos/asf/tomcat/connectors/trunk/ \
+   https://svn.apache.org/repos/asf/tomcat/connectors/tags/jk1.2.x/TAG/
+
+Here is an example for mod_jk 1.2.16
+
+svn copy \
+   https://svn.apache.org/repos/asf/tomcat/connectors/trunk/ \
+   https://svn.apache.org/repos/asf/tomcat/connectors/tags/jk1.2.x/JK_1_2_16/
+
+Build the mod_jk 1.2 documentation
+----------------------------------
+
+cd tomcat-connectors/jk/xdocs
+ant
+
+
+Check the documentation carefully (produced in tomcat-connectors/jk/build/docs)
+and copy it to people.apache.org:/x1/www/tomcat.apache.org/connectors-doc
+
+Create the new source distribution
+----------------------------------
+
+A tool named jkrelease.sh in tomcat-connectors/jk/tools creates
+a release tarball and a zip including signature files.
+
+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 tomcat-connectors/KEYS to the
+/www/www.apache.org/dist/tomcat/tomcat-connectors
+directory on the www.apache.org server.
+
+scp tomcat-connectors-jk-1.2.16-src.tar.gz* to 
+/www/www.apache.org/dist/tomcat/tomcat-connectors/jk/source
+scp tomcat-connectors-jk-1.2.16-src.zip* to   
+/www/www.apache.org/dist/tomcat/tomcat-connectors/jk/source
+
+ssh to www.apache.org and cd to the
+/www/www.apache.org/dist/tomcat/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/tomcat-connectors-jk-1.2.16-src.tar.gz tomcat-connectors-jk-src-current.tar.gz
+ln -s source/tomcat-connectors-jk-1.2.16-src.tar.gz.asc tomcat-connectors-jk-src-current.tar.gz.asc
+ln -s source/tomcat-connectors-jk-1.2.16-src.tar.zip tomcat-connectors-jk-src-current.zip
+ln -s source/tomcat-connectors-jk-1.2.16-src.zip.asc 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/tomcat/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:
+
+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
+------------------------------
+
+native/STATUS.txt: Add status "in development" for new version.
+native/configure.in: Update variable VERSION.
+native/common/jk_version.h: Update variables JK_VERMAJOR, JK_VERMINOR, JK_VERFIX,
+                            JK_VERSTRING, and JK_VERISRELEASE.
+native/common/portable.h.sample: Update VERSION define.
+native/iis/isapi_redirect.rc: Update JK_VERSION_STR, FILEVERSION, and PRODUCTVERSION
+native/iis/installer/isapi-redirector-win32-msi.ism: Update ProductVersion.
+xdocs/miscellaneous/changelog.xml: Start a new section for the new version.
+
+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/tomcat/tomcat-connector/jk .
+Copy old source distributions and binaries as needed, then remove the
+old source and binary distributions.
+
+Arrange the downloads_tomcat-connectors.cgi
+-------------------------------------------
+
+Check tomcat-site out:
+svn co https://svn.apache.org/repos/asf/tomcat/site site-tomcat
+
+
+Arrange the file: xdocs/downloads/downloads.xml
+
+Use ant to regenerate the corresponding html file:
+docs/site/downloads/downloads_tomcat-connectors.html
+
+Commit it after checking carefully the changes.
+
+Connect to people.apache.org and update the tomcat.apache.org site image,
+the site tomcat.apache.org should reflect the change after a while.
+cd /x2/www/tomcat.apache.org/site/downloads
+svn update downloads_tomcat-connectors.html
+
+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 users@tomcat.apache.org,
+dev@tomcat.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..eb98af5
--- /dev/null
+++ b/connectors/jk/build.xml
@@ -0,0 +1,448 @@
+<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" />
+
+    <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" />
+    <property name="tomcat-apr.jar" value="../jni/dist/tomcat-native-1.0.0.jar" />
+
+    <!-- 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-util.home" location="../util/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="${tomcat-util.home}/lib/tomcat-util.jar" />
+
+    <property name="tc4-catalina.jar" location="${tomcat41.home}/server/lib/catalina.jar" />
+    <property name="tc5-catalina.jar" location="${tomcat5.home}/server/lib/catalina.jar" />
+    <property name="tc3-core.jar" location="${tomcat33.home}/lib/common/tomcat_core.jar" />
+    <property name="tc3-core_util.jar" location="${tomcat33.home}/lib/common/core_util.jar" />
+    <property name="tc3-util.jar" location="${tomcat33.home}/lib/container/tomcat_util.jar" />
+    <property name="tc3-modules.jar" location="${tomcat33.home}/lib/container/tomcat_modules.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="${servlet-api.jar}"/>
+        <pathelement location="${tomcat-util.jar}" />
+        <pathelement location="${commons-logging.jar}"/>
+        <pathelement location="${commons-modeler.jar}"/>
+        <pathelement location="${jmx.jar}"/>
+        <pathelement location="${tomcat-coyote.jar}"/>
+    </path>
+
+    <path id="build-tc4.classpath">
+        <pathelement location="${tc4-catalina.jar}" />
+        <pathelement location="${servlet-api.jar}" />
+    </path>    
+    <path id="build-tc5.classpath">
+        <pathelement location="${tc5-catalina.jar}" />
+        <pathelement location="${servlet-api.jar}" />
+    </path>    
+    <path id="build-tc3.classpath">
+       <pathelement location="${tc3-core.jar}" />
+       <pathelement location="${tc3-core_util.jar}" />
+       <pathelement location="${tc3-util.jar}" />
+       <pathelement location="${tc3-modules.jar}" />
+       <pathelement location="${servlet-api.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="-------- tomcat-connectors --------" /> 
+        <available property="tomcat33.detect" 
+                   file="${tc3-core.jar}" />
+        <available property="tomcat40.detect" 
+                   file="${tc4-catalina.jar}" />
+        <available property="tomcat41.detect" 
+                   file="${tomcat41.home}/server/webapps" />
+        <condition property="tomcat5.detect">
+          <and>
+            <available
+              classname="javax.servlet.ServletRequestEvent"
+              classpath="${servlet-api.jar}" />
+            <available file="${tc5-catalina.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-static" depends="prepare,report" >
+	<!-- Copy static resource files -->
+	<copy todir="${jk.build}/classes">
+	    <fileset dir="java">
+	    	<include name="**/*.properties"/>
+	    </fileset>
+        </copy>
+        <copy todir="${jk.build}/classes" >
+          <fileset dir="java" includes="**/*.xml" />
+        </copy>
+    </target>
+    <target name="jkjava-shared" depends="jkjava-static" 
+             description="Build shared java side of the connector" >
+        <javac srcdir="java"
+               destdir="${jk.build}/classes"
+               deprecation="${compile.deprecation}"
+               debug="${compile.debug}"
+               optimize="${optimize}"
+               verbose="off" >
+            <include name="org/apache/jk/**"/>
+            <include name="org/apache/coyote/ajp/**" />
+            <exclude name="org/apache/coyote/ajp/*Apr*" unless="jdk.1.4.present" />
+	    <exclude name="org/apache/jk/common/ChannelNioSocket.java" unless="jdk.1.4.present"/>
+	    <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"  />
+            <exclude name="org/apache/jk/ant/**" />
+	    <classpath>
+	       <pathelement location="${tomcat-apr.jar}" />
+	       <path refid="xml-apis.classpath"/>
+	       <path refid="build-main.classpath"/>
+	    </classpath>
+	</javac>
+	<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-jk2.jar}"
+             index="true"
+             manifest="conf/tomcat-jk2.manifest"
+	     basedir="${jk.build}/classes" >
+            <include name="org/apache/jk/**" />
+            <include name="org/apache/coyote/ajp/**" />
+            <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="jkjava-tc5" depends="jkjava-shared" if="tomcat5.detect"
+           description="Build TC5 java side of the connector" >
+        <javac srcdir="java"
+               destdir="${jk.build}/classes"
+               deprecation="${compile.deprecation}"
+               debug="${compile.debug}"
+               optimize="${optimize}"
+               verbose="off" >
+            <include name="org/apache/coyote/ajp/**"/>
+            <include name="org/apache/jk/config/**"/>
+	    <classpath>
+	       <path refid="xml-apis.classpath"/>
+	       <path refid="build-main.classpath"/>
+               <path refid="build-tc5.classpath"/>
+	    </classpath>
+	</javac>
+	<jar jarfile="${tomcat-jkconfig.jar}"
+             index="true"
+	     basedir="${jk.build}/classes" 
+             manifest="conf/jkconfig.manifest">
+            <include name="org/apache/jk/config/**" />
+        </jar>
+    </target>
+    <target name="jkjava-tc4" depends="jkjava-shared" if="tomcat40.detect"
+           description="Build the TC4 java side of the connector">
+        <javac srcdir="java"
+               destdir="${jk.build}/classes"
+               deprecation="${compile.deprecation}"
+               debug="${compile.debug}"
+               optimize="${optimize}"
+               verbose="off" >
+            <include name="org/apache/ajp/**" />
+            <exclude name="org/apache/ajp/tomcat33/**" />
+	    <classpath>
+	       <path refid="xml-apis.classpath"/>
+	       <path refid="build-main.classpath"/>
+               <path refid="build-tc4.classpath"/>
+	    </classpath>
+
+	</javac>
+
+	<jar jarfile="${tomcat-jk.jar}"
+             index="true"
+	     basedir="${jk.build}/classes">
+            <include name="org/apache/ajp/**" />
+        </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>
+    </target>
+    <target name="jkjava-tc3" depends="jkjava-shared" if="tomcat33.detect"
+           description="Build the TC3 java side of the connector">
+        <javac srcdir="java"
+               destdir="${jk.build}/classes"
+               deprecation="${compile.deprecation}"
+               debug="${compile.debug}"
+               optimize="${optimize}"
+               verbose="off" >
+            <include name="org/apache/ajp/**" />
+            <exclude name="org/apache/ajp/tomcat4/**" />
+	    <classpath>
+	       <path refid="xml-apis.classpath"/>
+	       <path refid="build-main.classpath"/>
+               <path refid="build-tc3.classpath"/>
+	    </classpath>
+
+	</javac>
+
+	<jar jarfile="${tomcat-jk.jar}"
+             index="true"
+	     basedir="${jk.build}/classes">
+            <include name="org/apache/ajp/**" />
+        </jar>
+	
+    </target>
+    <target name="jkjava" depends="jkjava-tc3,jkjava-tc4,jkjava-tc5"
+            description="Build java side of the connector" >
+    </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-2005 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}"/>
+    </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>
+
+
+</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/uriworkermap.properties b/connectors/jk/conf/uriworkermap.properties
new file mode 100644
index 0000000..5214cd4
--- /dev/null
+++ b/connectors/jk/conf/uriworkermap.properties
@@ -0,0 +1,24 @@
+# uriworkermap.properties - IIS
+#
+# This file provides sample mappings for example wlb
+# worker defined in workermap.properties.minimal
+# The general syntax for this file is:
+# [URL]=[Worker name]
+
+/admin/*=wlb
+/manager/*=wlb
+/jsp-examples/*=wlb
+/servlets-examples/*=wlb
+/examples/*=wlb
+
+# Optionally filter out all .jpeg files inside that context
+# For no mapping the url has to start with exclamation (!)
+
+!/servlets-examples/*.jpeg=wlb
+
+#
+# Mount jkstatus to /jkmanager
+# For production servers you will need to
+# secure the access to the /jkmanager url
+#
+/jkmanager=jkstatus
diff --git a/connectors/jk/conf/workers.properties b/connectors/jk/conf/workers.properties
new file mode 100644
index 0000000..9688ba3
--- /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 worker 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 pool.
+#worker.ajp13.connection_pool_size
+
+#
+#------ 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.balance_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/workers.properties.minimal b/connectors/jk/conf/workers.properties.minimal
new file mode 100644
index 0000000..14dbb6e
--- /dev/null
+++ b/connectors/jk/conf/workers.properties.minimal
@@ -0,0 +1,30 @@
+# workers.properties.minimal -
+#
+# This file provides minimal jk configuration properties needed to
+# connect to Tomcat.
+#
+# The workers that jk should create and work with
+#
+
+worker.list=wlb,jkstatus
+
+#
+# Defining a worker named ajp13w and of type ajp13
+# Note that the name and the type do not have to match.
+#
+worker.ajp13w.type=ajp13
+worker.ajp13w.host=localhost
+worker.ajp13w.port=8009
+
+#
+# Defining a load balancer
+# 
+
+worker.wlb.type=lb
+worker.wlb.balance_workers=ajp13w
+
+#
+# Define status worker
+#
+
+worker.jkstatus.type=status
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..c746414
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/Ajp13.java
@@ -0,0 +1,513 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..213591c
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/Ajp13Packet.java
@@ -0,0 +1,521 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 {
+    
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( Ajp13Packet.class );
+    
+    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 ) {
+            if (log.isDebugEnabled())
+                log.debug("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 ) {
+            if (log.isDebugEnabled())
+                log.debug("Buffer overflow " + buff.length + " " + pos + " " + numBytes );
+        }
+        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 ) {
+            if (log.isDebugEnabled())
+                log.debug("appendXBytes - Buffer overflow " + buff.length + " " + pos + " " + numBytes );
+        }
+        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) ) {
+            if (log.isDebugEnabled())
+                log.debug("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)?
+            if (log.isDebugEnabled())
+                log.debug("XXX Assert failed, buff too small ");
+        }
+	
+        if( (length == 0xFFFF) || (length == -1) ) {
+            if (log.isDebugEnabled())
+                log.debug("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)?
+            if (log.isDebugEnabled())
+                log.debug("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) {
+                if (log.isDebugEnabled())
+                    log.debug( hex( buff[i] ) + " ");
+            } else {
+                if (log.isDebugEnabled()) 
+                    log.debug( "   " );
+            }
+        }
+        if (log.isDebugEnabled()) 
+            log.debug(" | ");
+        for( int i=start; i < start+16 && i < pkgEnd; i++ ) {
+            if( Character.isLetterOrDigit( (char)buff[i] )) {
+                if (log.isDebugEnabled()) 
+                    log.debug( new Character((char)buff[i]) );
+            } else {
+                if (log.isDebugEnabled()) 
+                    log.debug( "." );
+            }
+        }
+    }
+    
+    public void dump(String msg) {
+        if (log.isDebugEnabled())
+            log.debug( msg + ": " + buff + " " + pos +"/" + (len + 4));
+
+        for( int j=0; j < len + 4; j+=16 )
+            hexLine( j );
+
+    }
+}
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..1e69231
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/AjpHandler.java
@@ -0,0 +1,54 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..7cedc75
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/Logger.java
@@ -0,0 +1,52 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 {
+    
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog(Logger.class );
+    
+    /**
+     * Log the given message.
+     * @param msg The message to log.
+     */
+    public void log(String msg) {
+        if (log.isDebugEnabled())
+            log.debug("[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) {
+        if (log.isDebugEnabled())
+            log.debug("[Ajp13] " + msg, t);
+    }
+}
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..13ed95f
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/NegociationHandler.java
@@ -0,0 +1,534 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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
+{
+    
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog(NegociationHandler.class );
+    
+    // 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
+    {
+        if (log.isDebugEnabled())
+            log.debug("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) {
+        if (log.isDebugEnabled())
+	    log.debug("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..66fc067
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/RequestHandler.java
@@ -0,0 +1,812 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 bytesRead = ch.receive(ch.inBuf);
+            if(bytesRead < 0) {
+                return 500;
+            }
+
+            // First two bytes are length, rest is real data
+            if (bytesRead < 2) {
+                ch.blen = 0;
+            } else {
+                ch.blen = bytesRead - 2;
+            }
+            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..e3c7c80
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat33/Ajp14Interceptor.java
@@ -0,0 +1,461 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..baa796b
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat4/Ajp13Connector.java
@@ -0,0 +1,1109 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..2bf241d
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat4/Ajp13InputStream.java
@@ -0,0 +1,67 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..d883485
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat4/Ajp13Logger.java
@@ -0,0 +1,88 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..e33fe8c
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat4/Ajp13OutputStream.java
@@ -0,0 +1,47 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..32e2ed4
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat4/Ajp13Processor.java
@@ -0,0 +1,667 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..f14cf8f
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat4/Ajp13Request.java
@@ -0,0 +1,321 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..acdaa1c
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat4/Ajp13Response.java
@@ -0,0 +1,166 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..3de84af
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat4/Constants.java
@@ -0,0 +1,22 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..3b02e79
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat4/JkServlet.java
@@ -0,0 +1,96 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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  void d(String s ) {
+        log( "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..f901615
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat4/config/ApacheConfig.java
@@ -0,0 +1,571 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..55fbbb3
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat4/config/BaseJkConfig.java
@@ -0,0 +1,491 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 {
+
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( BaseJkConfig.class );
+    
+    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.warn("Unable to open config file", iex);
+	    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) {
+        log.info(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..6703af6
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat4/config/IISConfig.java
@@ -0,0 +1,304 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..802fb4b
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat4/config/NSConfig.java
@@ -0,0 +1,327 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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/coyote/ajp/AjpAprProcessor.java b/connectors/jk/java/org/apache/coyote/ajp/AjpAprProcessor.java
new file mode 100644
index 0000000..5c6c736
--- /dev/null
+++ b/connectors/jk/java/org/apache/coyote/ajp/AjpAprProcessor.java
@@ -0,0 +1,1304 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.ajp;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.ActionHook;
+import org.apache.coyote.Adapter;
+import org.apache.coyote.InputBuffer;
+import org.apache.coyote.OutputBuffer;
+import org.apache.coyote.Request;
+import org.apache.coyote.RequestInfo;
+import org.apache.coyote.Response;
+import org.apache.tomcat.jni.Socket;
+import org.apache.tomcat.jni.Status;
+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.HttpMessages;
+import org.apache.tomcat.util.http.MimeHeaders;
+import org.apache.tomcat.util.net.AprEndpoint;
+import org.apache.tomcat.util.res.StringManager;
+import org.apache.tomcat.util.threads.ThreadWithAttributes;
+
+
+/**
+ * Processes HTTP requests.
+ *
+ * @author Remy Maucherat
+ * @author Henri Gomez
+ * @author Dan Milstein
+ * @author Keith Wannamaker
+ * @author Kevin Seguin
+ * @author Costin Manolache
+ * @author Bill Barker
+ */
+public class AjpAprProcessor implements ActionHook {
+
+
+    /**
+     * Logger.
+     */
+    protected static org.apache.commons.logging.Log log
+        = org.apache.commons.logging.LogFactory.getLog(AjpAprProcessor.class);
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    public AjpAprProcessor(int packetSize, AprEndpoint endpoint) {
+
+        this.endpoint = endpoint;
+
+        request = new Request();
+        request.setInputBuffer(new SocketInputBuffer());
+
+        response = new Response();
+        response.setHook(this);
+        response.setOutputBuffer(new SocketOutputBuffer());
+        request.setResponse(response);
+
+        requestHeaderMessage = new AjpMessage(packetSize);
+        responseHeaderMessage = new AjpMessage(packetSize);
+        bodyMessage = new AjpMessage(packetSize);
+
+        if (endpoint.getFirstReadTimeout() > 0) {
+            readTimeout = endpoint.getFirstReadTimeout() * 1000;
+        } else {
+            readTimeout = 100 * 1000;
+        }
+
+        // Allocate input and output buffers
+        inputBuffer = ByteBuffer.allocateDirect(packetSize * 2);
+        inputBuffer.limit(0);
+        outputBuffer = ByteBuffer.allocateDirect(packetSize * 2);
+
+        // Cause loading of HexUtils
+        int foo = HexUtils.DEC[0];
+
+        // Cause loading of HttpMessages
+        HttpMessages.getMessage(200);
+
+     }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Associated adapter.
+     */
+    protected Adapter adapter = null;
+
+
+    /**
+     * Request object.
+     */
+    protected Request request = null;
+
+
+    /**
+     * Response object.
+     */
+    protected Response response = null;
+
+
+    /**
+     * Header message. Note that this header is merely the one used during the
+     * processing of the first message of a "request", so it might not be a request
+     * header. It will stay unchanged during the processing of the whole request.
+     */
+    protected AjpMessage requestHeaderMessage ;
+
+
+    /**
+     * Message used for response header composition.
+     */
+    protected AjpMessage responseHeaderMessage ;
+
+
+    /**
+     * Body message.
+     */
+    protected AjpMessage bodyMessage ;
+
+
+    /**
+     * Body message.
+     */
+    protected MessageBytes bodyBytes = MessageBytes.newInstance();
+
+
+    /**
+     * State flag.
+     */
+    protected boolean started = false;
+
+
+    /**
+     * Error flag.
+     */
+    protected boolean error = false;
+
+
+    /**
+     * Socket associated with the current connection.
+     */
+    protected long socket;
+
+
+    /**
+     * Host name (used to avoid useless B2C conversion on the host name).
+     */
+    protected char[] hostNameC = new char[0];
+
+
+    /**
+     * Associated endpoint.
+     */
+    protected AprEndpoint endpoint;
+
+
+    /**
+     * The socket timeout used when reading the first block of the request
+     * header.
+     */
+    protected long readTimeout;
+
+
+    /**
+     * Temp message bytes used for processing.
+     */
+    protected MessageBytes tmpMB = MessageBytes.newInstance();
+
+
+    /**
+     * Byte chunk for certs.
+     */
+    protected MessageBytes certificates = MessageBytes.newInstance();
+
+
+    /**
+     * End of stream flag.
+     */
+    protected boolean endOfStream = false;
+
+
+    /**
+     * Body empty flag.
+     */
+    protected boolean empty = true;
+
+
+    /**
+     * First read.
+     */
+    protected boolean first = true;
+
+
+    /**
+     * Replay read.
+     */
+    protected boolean replay = false;
+
+
+    /**
+     * Finished response.
+     */
+    protected boolean finished = false;
+
+
+    /**
+     * Direct buffer used for output.
+     */
+    protected ByteBuffer outputBuffer = null;
+
+
+    /**
+     * Direct buffer used for input.
+     */
+    protected ByteBuffer inputBuffer = null;
+
+
+    /**
+     * Direct buffer used for sending right away a get body message.
+     */
+    protected static final ByteBuffer getBodyMessageBuffer;
+
+
+    /**
+     * Direct buffer used for sending right away a pong message.
+     */
+    protected static final ByteBuffer pongMessageBuffer;
+
+
+    /**
+     * End message array.
+     */
+    protected static final byte[] endMessageArray;
+
+    /**
+     * Direct buffer used for sending explicit flush message.
+     */
+    protected static final ByteBuffer flushMessageBuffer;
+
+    // ----------------------------------------------------- Static Initializer
+
+
+    static {
+
+        // Set the get body message buffer
+        AjpMessage getBodyMessage = new AjpMessage(128);
+        getBodyMessage.reset();
+        getBodyMessage.appendByte(Constants.JK_AJP13_GET_BODY_CHUNK);
+        getBodyMessage.appendInt(Constants.MAX_READ_SIZE);
+        getBodyMessage.end();
+        getBodyMessageBuffer = ByteBuffer.allocateDirect(getBodyMessage
+                .getLen());
+        getBodyMessageBuffer.put(getBodyMessage.getBuffer(), 0, getBodyMessage
+                .getLen());
+
+        // Set the read body message buffer
+        AjpMessage pongMessage = new AjpMessage(128);
+        pongMessage.reset();
+        pongMessage.appendByte(Constants.JK_AJP13_CPONG_REPLY);
+        pongMessage.end();
+        pongMessageBuffer = ByteBuffer.allocateDirect(pongMessage.getLen());
+        pongMessageBuffer.put(pongMessage.getBuffer(), 0,
+                pongMessage.getLen());
+
+        // Allocate the end message array
+        AjpMessage endMessage = new AjpMessage(128);
+        endMessage.reset();
+        endMessage.appendByte(Constants.JK_AJP13_END_RESPONSE);
+        endMessage.appendByte(1);
+        endMessage.end();
+        endMessageArray = new byte[endMessage.getLen()];
+        System.arraycopy(endMessage.getBuffer(), 0, endMessageArray, 0,
+                endMessage.getLen());
+
+        // Set the flush message buffer
+        AjpMessage flushMessage = new AjpMessage(128);
+        flushMessage.reset();
+        flushMessage.appendByte(Constants.JK_AJP13_SEND_BODY_CHUNK);
+        flushMessage.appendInt(0);
+        flushMessage.appendByte(0);
+        flushMessage.end();
+        flushMessageBuffer =
+            ByteBuffer.allocateDirect(flushMessage.getLen());
+        flushMessageBuffer.put(flushMessage.getBuffer(), 0,
+                flushMessage.getLen());
+
+    }
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Use Tomcat authentication ?
+     */
+    protected boolean tomcatAuthentication = true;
+    public boolean getTomcatAuthentication() { return tomcatAuthentication; }
+    public void setTomcatAuthentication(boolean tomcatAuthentication) { this.tomcatAuthentication = tomcatAuthentication; }
+
+
+    /**
+     * Required secret.
+     */
+    protected String requiredSecret = null;
+    public void setRequiredSecret(String requiredSecret) { this.requiredSecret = requiredSecret; }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /** 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.
+     *
+     * @throws IOException error during an I/O operation
+     */
+    public boolean process(long socket)
+        throws IOException {
+        ThreadWithAttributes thrA=
+                (ThreadWithAttributes)Thread.currentThread();
+        RequestInfo rp = request.getRequestProcessor();
+        thrA.setCurrentStage(endpoint, "parsing http request");
+        rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
+
+        // Setting up the socket
+        this.socket = socket;
+        Socket.setrbb(this.socket, inputBuffer);
+        Socket.setsbb(this.socket, outputBuffer);
+
+        // Error flag
+        error = false;
+
+        int limit = 0;
+        if (endpoint.getFirstReadTimeout() > 0) {
+            limit = endpoint.getMaxThreads() / 2;
+        }
+
+        boolean openSocket = true;
+        boolean keptAlive = false;
+
+        while (started && !error) {
+
+            // Parsing the request header
+            try {
+                // Get first message of the request
+                if (!readMessage(requestHeaderMessage, true,
+                        keptAlive && (endpoint.getCurrentThreadsBusy() > limit))) {
+                    // This means that no data is available right now
+                    // (long keepalive), so that the processor should be recycled
+                    // and the method should return true
+                    rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
+                    break;
+                }
+                // Check message type, process right away and break if
+                // not regular request processing
+                int type = requestHeaderMessage.getByte();
+                if (type == Constants.JK_AJP13_CPING_REQUEST) {
+                    if (Socket.sendb(socket, pongMessageBuffer, 0,
+                            pongMessageBuffer.position()) < 0) {
+                        error = true;
+                    }
+                    continue;
+                } else if(type != Constants.JK_AJP13_FORWARD_REQUEST) {
+                    // Usually the servlet didn't read the previous request body
+                    if(log.isDebugEnabled()) {
+                        log.debug("Unexpected message: "+type);
+                    }
+                    continue;
+                }
+
+                keptAlive = true;
+                request.setStartTime(System.currentTimeMillis());
+            } catch (IOException e) {
+                error = true;
+                break;
+            } catch (Throwable t) {
+                log.debug(sm.getString("ajpprocessor.header.error"), t);
+                // 400 - Bad Request
+                response.setStatus(400);
+                error = true;
+            }
+
+            // Setting up filters, and parse some request headers
+            thrA.setCurrentStage(endpoint, "prepareRequest");
+            rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
+            try {
+                prepareRequest();
+                thrA.setParam(endpoint, request.requestURI());
+            } catch (Throwable t) {
+                log.debug(sm.getString("ajpprocessor.request.prepare"), t);
+                // 400 - Internal Server Error
+                response.setStatus(400);
+                error = true;
+            }
+
+            // Process the request in the adapter
+            if (!error) {
+                try {
+                    thrA.setCurrentStage(endpoint, "service");
+                    rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
+                    adapter.service(request, response);
+                } catch (InterruptedIOException e) {
+                    error = true;
+                } catch (Throwable t) {
+                    log.error(sm.getString("ajpprocessor.request.process"), t);
+                    // 500 - Internal Server Error
+                    response.setStatus(500);
+                    error = true;
+                }
+            }
+
+            // Finish the response if not done yet
+            if (!finished) {
+                try {
+                    finish();
+                } catch (Throwable 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(endpoint, "ended");
+            rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
+            recycle();
+
+        }
+
+        // Add the socket to the poller
+        if (!error) {
+            endpoint.getPoller().add(socket);
+        } else {
+            openSocket = false;
+        }
+
+        rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
+        recycle();
+
+        return openSocket;
+
+    }
+
+
+    // ----------------------------------------------------- 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) {
+
+            if (response.isCommitted())
+                return;
+
+            // Validate and write response headers
+            try {
+                prepareResponse();
+            } catch (IOException e) {
+                // Set error flag
+                error = true;
+            }
+
+        } else if (actionCode == ActionCode.ACTION_CLIENT_FLUSH) {
+
+            if (!response.isCommitted()) {
+                // Validate and write response headers
+                try {
+                    prepareResponse();
+                } catch (IOException e) {
+                    // Set error flag
+                    error = true;
+                    return;
+                }
+            }
+
+            try {
+                flush();
+                // Send explicit flush message
+                if (Socket.sendb(socket, flushMessageBuffer, 0,
+                                 flushMessageBuffer.position()) < 0) {
+                    error = true;                    
+                }
+            } catch (IOException e) {
+                // Set error flag
+                error = true;
+            }
+
+        } else if (actionCode == ActionCode.ACTION_CLOSE) {
+            // Close
+
+            // End the processing of the current request, and stop any further
+            // transactions with the client
+
+            try {
+                finish();
+            } catch (IOException e) {
+                // Set error flag
+                error = true;
+            }
+
+        } else if (actionCode == ActionCode.ACTION_START) {
+
+            started = true;
+
+        } else if (actionCode == ActionCode.ACTION_STOP) {
+
+            started = false;
+
+        } else if (actionCode == ActionCode.ACTION_REQ_SSL_ATTRIBUTE ) {
+
+            if (!certificates.isNull()) {
+                ByteChunk certData = certificates.getByteChunk();
+                X509Certificate jsseCerts[] = null;
+                ByteArrayInputStream bais =
+                    new ByteArrayInputStream(certData.getBytes(),
+                            certData.getStart(),
+                            certData.getLength());
+                // Fill the first element.
+                try {
+                    CertificateFactory cf =
+                        CertificateFactory.getInstance("X.509");
+                    X509Certificate cert = (X509Certificate)
+                    cf.generateCertificate(bais);
+                    jsseCerts = new X509Certificate[1];
+                    jsseCerts[0] = cert;
+                    request.setAttribute(AprEndpoint.CERTIFICATE_KEY, jsseCerts);
+                } catch (java.security.cert.CertificateException e) {
+                    log.error(sm.getString("ajpprocessor.certs.fail"), e);
+                    return;
+                }
+            }
+
+        } else if (actionCode == ActionCode.ACTION_REQ_HOST_ATTRIBUTE) {
+
+            // Get remote host name using a DNS resolution
+            if (request.remoteHost().isNull()) {
+                try {
+                    request.remoteHost().setString(InetAddress.getByName
+                            (request.remoteAddr().toString()).getHostName());
+                } catch (IOException iex) {
+                    // Ignore
+                }
+            }
+
+        } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_ADDR_ATTRIBUTE) {
+
+            // Copy from local name for now, which should simply be an address
+            request.localAddr().setString(request.localName().toString());
+
+        } else if (actionCode == ActionCode.ACTION_REQ_SET_BODY_REPLAY) {
+
+            request.setContentLength(-1); // reset content length
+            // Set the given bytes as the content
+            ByteChunk bc = (ByteChunk) param;
+            bodyBytes.setBytes(bc.getBytes(), bc.getStart(), bc.getLength());
+            first = false;
+            empty = false;
+            replay = true;
+
+        }
+
+
+    }
+
+
+    // ------------------------------------------------------ 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() {
+
+        // Translate the HTTP method code to a String.
+        byte methodCode = requestHeaderMessage.getByte();
+        if (methodCode != Constants.SC_M_JK_STORED) {
+            String methodName = Constants.methodTransArray[(int)methodCode - 1];
+            request.method().setString(methodName);
+        }
+
+        requestHeaderMessage.getBytes(request.protocol());
+        requestHeaderMessage.getBytes(request.requestURI());
+
+        requestHeaderMessage.getBytes(request.remoteAddr());
+        requestHeaderMessage.getBytes(request.remoteHost());
+        requestHeaderMessage.getBytes(request.localName());
+        request.setLocalPort(requestHeaderMessage.getInt());
+
+        boolean isSSL = requestHeaderMessage.getByte() != 0;
+        if (isSSL) {
+            request.scheme().setString("https");
+        }
+
+        // Decode headers
+        MimeHeaders headers = request.getMimeHeaders();
+
+        int hCount = requestHeaderMessage.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 = requestHeaderMessage.peekInt();
+            int hId = isc & 0xFF;
+
+            MessageBytes vMB = null;
+            isc &= 0xFF00;
+            if(0xA000 == isc) {
+                requestHeaderMessage.getInt(); // To advance the read position
+                hName = Constants.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;
+                requestHeaderMessage.getBytes(tmpMB);
+                ByteChunk bc = tmpMB.getByteChunk();
+                vMB = headers.addValue(bc.getBuffer(),
+                        bc.getStart(), bc.getLength());
+            }
+
+            requestHeaderMessage.getBytes(vMB);
+
+            if (hId == Constants.SC_REQ_CONTENT_LENGTH ||
+                    (hId == -1 && tmpMB.equalsIgnoreCase("Content-Length"))) {
+                // just read the content-length header, so set it
+                long cl = vMB.getLong();
+                if(cl < Integer.MAX_VALUE)
+                    request.setContentLength( (int)cl );
+            } else if (hId == Constants.SC_REQ_CONTENT_TYPE ||
+                    (hId == -1 && tmpMB.equalsIgnoreCase("Content-Type"))) {
+                // just read the content-type header, so set it
+                ByteChunk bchunk = vMB.getByteChunk();
+                request.contentType().setBytes(bchunk.getBytes(),
+                        bchunk.getOffset(),
+                        bchunk.getLength());
+            }
+        }
+
+        // Decode extra attributes
+        boolean secret = false;
+        byte attributeCode;
+        while ((attributeCode = requestHeaderMessage.getByte())
+                != Constants.SC_A_ARE_DONE) {
+
+            switch (attributeCode) {
+
+            case Constants.SC_A_REQ_ATTRIBUTE :
+                requestHeaderMessage.getBytes(tmpMB);
+                String n = tmpMB.toString();
+                requestHeaderMessage.getBytes(tmpMB);
+                String v = tmpMB.toString();
+                request.setAttribute(n, v);
+                break;
+
+            case Constants.SC_A_CONTEXT :
+                requestHeaderMessage.getBytes(tmpMB);
+                // nothing
+                break;
+
+            case Constants.SC_A_SERVLET_PATH :
+                requestHeaderMessage.getBytes(tmpMB);
+                // nothing
+                break;
+
+            case Constants.SC_A_REMOTE_USER :
+                if (tomcatAuthentication) {
+                    // ignore server
+                    requestHeaderMessage.getBytes(tmpMB);
+                } else {
+                    requestHeaderMessage.getBytes(request.getRemoteUser());
+                }
+                break;
+
+            case Constants.SC_A_AUTH_TYPE :
+                if (tomcatAuthentication) {
+                    // ignore server
+                    requestHeaderMessage.getBytes(tmpMB);
+                } else {
+                    requestHeaderMessage.getBytes(request.getAuthType());
+                }
+                break;
+
+            case Constants.SC_A_QUERY_STRING :
+                requestHeaderMessage.getBytes(request.queryString());
+                break;
+
+            case Constants.SC_A_JVM_ROUTE :
+                requestHeaderMessage.getBytes(request.instanceId());
+                break;
+
+            case Constants.SC_A_SSL_CERT :
+                request.scheme().setString("https");
+                // SSL certificate extraction is lazy, moved to JkCoyoteHandler
+                requestHeaderMessage.getBytes(certificates);
+                break;
+
+            case Constants.SC_A_SSL_CIPHER :
+                request.scheme().setString("https");
+                requestHeaderMessage.getBytes(tmpMB);
+                request.setAttribute(AprEndpoint.CIPHER_SUITE_KEY,
+                                     tmpMB.toString());
+                break;
+
+            case Constants.SC_A_SSL_SESSION :
+                request.scheme().setString("https");
+                requestHeaderMessage.getBytes(tmpMB);
+                request.setAttribute(AprEndpoint.SESSION_ID_KEY,
+                                     tmpMB.toString());
+                break;
+
+            case Constants.SC_A_SSL_KEY_SIZE :
+                request.setAttribute(AprEndpoint.KEY_SIZE_KEY,
+                                     new Integer(requestHeaderMessage.getInt()));
+                break;
+
+            case Constants.SC_A_STORED_METHOD:
+                requestHeaderMessage.getBytes(request.method());
+                break;
+
+            case Constants.SC_A_SECRET:
+                requestHeaderMessage.getBytes(tmpMB);
+                if (requiredSecret != null) {
+                    secret = true;
+                    if (!tmpMB.equals(requiredSecret)) {
+                        response.setStatus(403);
+                        error = true;
+                    }
+                }
+                break;
+
+            default:
+                // Ignore unknown attribute for backward compatibility
+                break;
+
+            }
+
+        }
+
+        // Check if secret was submitted if required
+        if ((requiredSecret != null) && !secret) {
+            response.setStatus(403);
+            error = true;
+        }
+
+        // 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 = headers.setValue("host");
+                hostMB.setBytes(uriB, uriBCStart + pos + 3,
+                                slashPos - pos - 3);
+            }
+
+        }
+
+        MessageBytes valueMB = request.getMimeHeaders().getValue("host");
+        parseHost(valueMB);
+
+    }
+
+
+    /**
+     * Parse host.
+     */
+    public void parseHost(MessageBytes valueMB) {
+
+        if (valueMB == null || (valueMB != null && valueMB.isNull()) ) {
+            // HTTP/1.0
+            // Default is what the socket tells us. Overriden if a host is
+            // found/parsed
+            request.setServerPort(endpoint.getPort());
+            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 (request.scheme().equalsIgnoreCase("https")) {
+                // 443 - Default HTTPS port
+                request.setServerPort(443);
+            } else {
+                // 80 - Default HTTTP port
+                request.setServerPort(80);
+            }
+            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);
+
+        }
+
+    }
+
+
+    /**
+     * When committing the response, we have to validate the set of headers, as
+     * well as setup the response filters.
+     */
+    protected void prepareResponse()
+        throws IOException {
+
+        response.setCommitted(true);
+
+        responseHeaderMessage.reset();
+        responseHeaderMessage.appendByte(Constants.JK_AJP13_SEND_HEADERS);
+
+        // HTTP header contents
+        responseHeaderMessage.appendInt(response.getStatus());
+        String message = response.getMessage();
+        if (message == null){
+            message = HttpMessages.getMessage(response.getStatus());
+        } else {
+            message = message.replace('\n', ' ').replace('\r', ' ');
+        }
+        tmpMB.setString(message);
+        responseHeaderMessage.appendBytes(tmpMB);
+
+        // Special headers
+        MimeHeaders headers = response.getMimeHeaders();
+        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 >= 0) {
+            headers.setValue("Content-Length").setInt(contentLength);
+        }
+
+        // Other headers
+        int numHeaders = headers.size();
+        responseHeaderMessage.appendInt(numHeaders);
+        for (int i = 0; i < numHeaders; i++) {
+            MessageBytes hN = headers.getName(i);
+            responseHeaderMessage.appendBytes(hN);
+            MessageBytes hV=headers.getValue(i);
+            responseHeaderMessage.appendBytes(hV);
+        }
+
+        // Write to buffer
+        responseHeaderMessage.end();
+        outputBuffer.put(responseHeaderMessage.getBuffer(), 0, responseHeaderMessage.getLen());
+
+    }
+
+
+    /**
+     * Finish AJP response.
+     */
+    protected void finish()
+        throws IOException {
+
+        if (!response.isCommitted()) {
+            // Validate and write response headers
+            try {
+                prepareResponse();
+            } catch (IOException e) {
+                // Set error flag
+                error = true;
+            }
+        }
+
+        if (finished)
+            return;
+
+        finished = true;
+
+        // Add the end message
+        if (outputBuffer.position() + endMessageArray.length > outputBuffer.capacity()) {
+            flush();
+        }
+        outputBuffer.put(endMessageArray);
+        flush();
+
+    }
+
+
+    /**
+     * Read at least the specified amount of bytes, and place them
+     * in the input buffer.
+     */
+    protected boolean read(int n)
+        throws IOException {
+
+        if (inputBuffer.capacity() - inputBuffer.limit() <=
+                n - inputBuffer.remaining()) {
+            inputBuffer.compact();
+            inputBuffer.limit(inputBuffer.position());
+            inputBuffer.position(0);
+        }
+        while (inputBuffer.remaining() < n) {
+            int nRead = Socket.recvbb
+                (socket, inputBuffer.limit(),
+                        inputBuffer.capacity() - inputBuffer.limit());
+            if (nRead > 0) {
+                inputBuffer.limit(inputBuffer.limit() + nRead);
+            } else {
+                throw new IOException(sm.getString("ajpprotocol.failedread"));
+            }
+        }
+
+        return true;
+
+    }
+
+
+    /**
+     * Read at least the specified amount of bytes, and place them
+     * in the input buffer.
+     */
+    protected boolean readt(int n, boolean useAvailableData)
+        throws IOException {
+
+        if (useAvailableData && inputBuffer.remaining() == 0) {
+            return false;
+        }
+        if (inputBuffer.capacity() - inputBuffer.limit() <=
+                n - inputBuffer.remaining()) {
+            inputBuffer.compact();
+            inputBuffer.limit(inputBuffer.position());
+            inputBuffer.position(0);
+        }
+        while (inputBuffer.remaining() < n) {
+            int nRead = Socket.recvbbt
+                (socket, inputBuffer.limit(),
+                        inputBuffer.capacity() - inputBuffer.limit(), readTimeout);
+            if (nRead > 0) {
+                inputBuffer.limit(inputBuffer.limit() + nRead);
+            } else {
+                if ((-nRead) == Status.ETIMEDOUT || (-nRead) == Status.TIMEUP) {
+                    return false;
+                } else {
+                    throw new IOException(sm.getString("ajpprotocol.failedread"));
+                }
+            }
+        }
+
+        return true;
+
+    }
+
+
+    /** 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 {
+
+        first = false;
+        bodyMessage.reset();
+        readMessage(bodyMessage, false, false);
+
+        // No data received.
+        if (bodyMessage.getLen() == 0) {
+            // just the header
+            // Don't mark 'end of stream' for the first chunk.
+            return false;
+        }
+        int blen = bodyMessage.peekInt();
+        if (blen == 0) {
+            return false;
+        }
+
+        bodyMessage.getBytes(bodyBytes);
+        empty = false;
+        return true;
+    }
+
+    /**
+     * 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??).
+        // FORM support
+        if (replay) {
+            endOfStream = true; // we've read everything there is
+        }
+        if (endOfStream) {
+            return false;
+        }
+
+        // Request more data immediately
+        Socket.sendb(socket, getBodyMessageBuffer, 0,
+                getBodyMessageBuffer.position());
+
+        boolean moreData = receive();
+        if( !moreData ) {
+            endOfStream = true;
+        }
+        return moreData;
+    }
+
+
+    /**
+     * Read an AJP message.
+     *
+     * @param first is true if the message is the first in the request, which
+     *        will cause a short duration blocking read
+     * @return true if the message has been read, false if the short read
+     *         didn't return anything
+     * @throws IOException any other failure, including incomplete reads
+     */
+    protected boolean readMessage(AjpMessage message, boolean first,
+            boolean useAvailableData)
+        throws IOException {
+
+        byte[] buf = message.getBuffer();
+        int headerLength = message.getHeaderLength();
+
+        if (first) {
+            if (!readt(headerLength, useAvailableData)) {
+                return false;
+            }
+        } else {
+            read(headerLength);
+        }
+        inputBuffer.get(message.getBuffer(), 0, headerLength);
+        message.processHeader();
+        read(message.getLen());
+        inputBuffer.get(message.getBuffer(), headerLength, message.getLen());
+
+        return true;
+
+    }
+
+
+    /**
+     * Recycle the processor.
+     */
+    public void recycle() {
+
+        // Recycle Request object
+        first = true;
+        endOfStream = false;
+        empty = true;
+        replay = false;
+        finished = false;
+        request.recycle();
+        response.recycle();
+        certificates.recycle();
+
+        inputBuffer.clear();
+        inputBuffer.limit(0);
+        outputBuffer.clear();
+
+    }
+
+
+    /**
+     * Callback to write data from the buffer.
+     */
+    protected void flush()
+        throws IOException {
+        if (outputBuffer.position() > 0) {
+            if (Socket.sendbb(socket, 0, outputBuffer.position()) < 0) {
+                throw new IOException();
+            }
+            outputBuffer.clear();
+        }
+    }
+
+
+    // ------------------------------------- InputStreamInputBuffer Inner Class
+
+
+    /**
+     * This class is an input buffer which will read its data from an input
+     * stream.
+     */
+    protected class SocketInputBuffer
+        implements InputBuffer {
+
+
+        /**
+         * Read bytes into the specified chunk.
+         */
+        public int doRead(ByteChunk chunk, Request req )
+            throws IOException {
+
+            if (endOfStream) {
+                return -1;
+            }
+            if (first && req.getContentLengthLong() > 0) {
+                // Handle special first-body-chunk
+                if (!receive()) {
+                    return 0;
+                }
+            } else if (empty) {
+                if (!refillReadBuffer()) {
+                    return -1;
+                }
+            }
+            ByteChunk bc = bodyBytes.getByteChunk();
+            chunk.setBytes(bc.getBuffer(), bc.getStart(), bc.getLength());
+            empty = true;
+            return chunk.getLength();
+
+        }
+
+    }
+
+
+    // ----------------------------------- OutputStreamOutputBuffer Inner Class
+
+
+    /**
+     * This class is an output buffer which will write data to an output
+     * stream.
+     */
+    protected class SocketOutputBuffer
+        implements OutputBuffer {
+        
+        /**
+         * Write chunk.
+         */
+        public int doWrite(ByteChunk chunk, Response res)
+            throws IOException {
+
+            if (!response.isCommitted()) {
+                // Validate and write response headers
+                try {
+                    prepareResponse();
+                } catch (IOException e) {
+                    // Set error flag
+                    error = true;
+                }
+            }
+
+            int len = chunk.getLength();
+            // 4 - hardcoded, byte[] marshalling overhead
+            int chunkSize =  Constants.MAX_SEND_SIZE;
+            int off = 0;
+            while (len > 0) {
+                int thisTime = len;
+                if (thisTime > chunkSize) {
+                    thisTime = chunkSize;
+                }
+                len -= thisTime;
+                if (outputBuffer.position() + thisTime +
+                    Constants.H_SIZE + 4 > outputBuffer.capacity()) {
+                    flush();
+                }
+                outputBuffer.put((byte) 0x41);
+                outputBuffer.put((byte) 0x42);
+                outputBuffer.putShort((short) (thisTime + 4));
+                outputBuffer.put(Constants.JK_AJP13_SEND_BODY_CHUNK);
+                outputBuffer.putShort((short) thisTime);
+                outputBuffer.put(chunk.getBytes(), chunk.getOffset() + off, thisTime);
+                outputBuffer.put((byte) 0x00);
+                off += thisTime;
+            }
+
+            return chunk.getLength();
+
+        }
+
+
+    }
+
+
+}
diff --git a/connectors/jk/java/org/apache/coyote/ajp/AjpAprProtocol.java b/connectors/jk/java/org/apache/coyote/ajp/AjpAprProtocol.java
new file mode 100644
index 0000000..d61f856
--- /dev/null
+++ b/connectors/jk/java/org/apache/coyote/ajp/AjpAprProtocol.java
@@ -0,0 +1,537 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.ajp;
+
+import java.net.InetAddress;
+import java.net.URLEncoder;
+import java.util.Hashtable;
+import java.util.Iterator;
+
+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.AprEndpoint;
+import org.apache.tomcat.util.net.AprEndpoint.Handler;
+import org.apache.tomcat.util.res.StringManager;
+
+
+/**
+ * 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 AjpAprProtocol 
+    implements ProtocolHandler, MBeanRegistration {
+    
+    
+    protected static org.apache.commons.logging.Log log =
+        org.apache.commons.logging.LogFactory.getLog(AjpAprProtocol.class);
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // ------------------------------------------------------------ Constructor
+
+
+    public AjpAprProtocol() {
+        cHandler = new AjpConnectionHandler(this);
+        setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
+        setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
+        //setServerSoTimeout(Constants.DEFAULT_SERVER_SOCKET_TIMEOUT);
+        setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
+    }
+
+    
+    // ----------------------------------------------------- Instance Variables
+
+
+    protected ObjectName tpOname;
+    
+    
+    protected ObjectName rgOname;
+
+
+    /**
+     * Associated APR endpoint.
+     */
+    protected AprEndpoint ep = new AprEndpoint();
+
+
+    /**
+     * Configuration attributes.
+     */
+    protected Hashtable attributes = new Hashtable();
+
+
+    /**
+     * Should authentication be done in the native webserver layer, 
+     * or in the Servlet container ?
+     */
+    protected boolean tomcatAuthentication = true;
+
+
+    /**
+     * Required secret.
+     */
+    protected String requiredSecret = null;
+
+
+    /**
+     * AJP packet size.
+     */
+    protected int packetSize = Constants.MAX_PACKET_SIZE;
+
+    
+    /**
+     * Adapter which will process the requests recieved by this endpoint.
+     */
+    private Adapter adapter;
+    
+    
+    /**
+     * Connection handler for AJP.
+     */
+    private AjpConnectionHandler cHandler;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /** 
+     * Pass config info
+     */
+    public void setAttribute(String name, Object value) {
+        if (log.isTraceEnabled()) {
+            log.trace(sm.getString("ajpprotocol.setattribute", name, value));
+        }
+        attributes.put(name, value);
+    }
+
+    public Object getAttribute(String key) {
+        if (log.isTraceEnabled()) {
+            log.trace(sm.getString("ajpprotocol.getattribute", key));
+        }
+        return attributes.get(key);
+    }
+
+
+    public Iterator getAttributeNames() {
+        return attributes.keySet().iterator();
+    }
+
+
+    /**
+     * 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.setName(getName());
+        ep.setHandler(cHandler);
+        ep.setUseSendfile(false);
+
+        try {
+            ep.init();
+        } catch (Exception ex) {
+            log.error(sm.getString("ajpprotocol.endpoint.initerror"), ex);
+            throw ex;
+        }
+        if (log.isInfoEnabled()) {
+            log.info(sm.getString("ajpprotocol.init", getName()));
+        }
+    }
+
+
+    public void start() throws Exception {
+        if (this.domain != null ) {
+            try {
+                tpOname = new ObjectName
+                    (domain + ":" + "type=ThreadPool,name=" + getName());
+                Registry.getRegistry(null, null)
+                    .registerComponent(ep, tpOname, null );
+            } 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.start();
+        } catch (Exception ex) {
+            log.error(sm.getString("ajpprotocol.endpoint.starterror"), ex);
+            throw ex;
+        }
+        if (log.isInfoEnabled())
+            log.info(sm.getString("ajpprotocol.start", getName()));
+    }
+
+    public void pause() throws Exception {
+        try {
+            ep.pause();
+        } catch (Exception ex) {
+            log.error(sm.getString("ajpprotocol.endpoint.pauseerror"), ex);
+            throw ex;
+        }
+        if (log.isInfoEnabled())
+            log.info(sm.getString("ajpprotocol.pause", getName()));
+    }
+
+    public void resume() throws Exception {
+        try {
+            ep.resume();
+        } catch (Exception ex) {
+            log.error(sm.getString("ajpprotocol.endpoint.resumeerror"), ex);
+            throw ex;
+        }
+        if (log.isInfoEnabled())
+            log.info(sm.getString("ajpprotocol.resume", getName()));
+    }
+
+    public void destroy() throws Exception {
+        if (log.isInfoEnabled())
+            log.info(sm.getString("ajpprotocol.stop", getName()));
+        ep.destroy();
+        if (tpOname!=null)
+            Registry.getRegistry(null, null).unregisterComponent(tpOname);
+        if (rgOname != null)
+            Registry.getRegistry(null, null).unregisterComponent(rgOname);
+    }
+
+
+    public int getMaxThreads() {
+        return ep.getMaxThreads();
+    }
+
+    public void setMaxThreads(int maxThreads) {
+        ep.setMaxThreads(maxThreads);
+        setAttribute("maxThreads", "" + maxThreads);
+    }
+
+    public void setThreadPriority(int threadPriority) {
+        ep.setThreadPriority(threadPriority);
+        setAttribute("threadPriority", "" + threadPriority);
+    }
+    
+    public int getThreadPriority() {
+        return ep.getThreadPriority();
+    }
+    
+
+    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);
+    }
+
+
+    public boolean getUseSendfile() {
+        return ep.getUseSendfile();
+    }
+
+
+    public void setUseSendfile(boolean useSendfile) {
+        // No sendfile for AJP
+    }
+
+
+    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 ("ajp-" + encodedAddr + ep.getPort());
+    }
+
+
+    public boolean getTcpNoDelay() {
+        return ep.getTcpNoDelay();
+    }
+
+
+    public void setTcpNoDelay(boolean b) {
+        ep.setTcpNoDelay(b);
+        setAttribute("tcpNoDelay", "" + b);
+    }
+
+
+    public boolean getTomcatAuthentication() {
+        return tomcatAuthentication;
+    }
+
+
+    public void setTomcatAuthentication(boolean tomcatAuthentication) {
+        this.tomcatAuthentication = tomcatAuthentication;
+    }
+
+
+    public int getFirstReadTimeout() {
+        return ep.getFirstReadTimeout();
+    }
+
+
+    public void setFirstReadTimeout(int i) {
+        ep.setFirstReadTimeout(i);
+        setAttribute("firstReadTimeout", "" + i);
+    }
+
+
+    public int getPollTime() {
+        return ep.getPollTime();
+    }
+
+
+    public void setPollTime(int i) {
+        ep.setPollTime(i);
+        setAttribute("pollTime", "" + i);
+    }
+
+
+    public void setPollerSize(int i) {
+        ep.setPollerSize(i); 
+        setAttribute("pollerSize", "" + i);
+    }
+
+
+    public int getPollerSize() {
+        return ep.getPollerSize();
+    }
+
+
+    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 void setRequiredSecret(String requiredSecret) {
+        this.requiredSecret = requiredSecret;
+    }
+    
+
+    public int getPacketSize() {
+        return packetSize;
+    }
+
+
+    public void setPacketSize(int packetSize) {
+        if(packetSize <Constants.MAX_PACKET_SIZE) {
+            packetSize = Constants.MAX_PACKET_SIZE;
+        }
+        this.packetSize = packetSize;
+    }
+    
+    
+    // --------------------------------------  AjpConnectionHandler Inner Class
+
+
+    protected static class AjpConnectionHandler implements Handler {
+        protected AjpAprProtocol proto;
+        protected static int count = 0;
+        protected RequestGroupInfo global=new RequestGroupInfo();
+        protected ThreadLocal localProcessor = new ThreadLocal();
+
+        public AjpConnectionHandler(AjpAprProtocol proto) {
+            this.proto = proto;
+        }
+
+        public boolean process(long socket) {
+            AjpAprProcessor processor = null;
+            try {
+                processor = (AjpAprProcessor) localProcessor.get();
+                if (processor == null) {
+                    processor = new AjpAprProcessor(proto.packetSize,proto.ep);
+                    processor.setAdapter(proto.adapter);
+                    processor.setTomcatAuthentication(proto.tomcatAuthentication);
+                    processor.setRequiredSecret(proto.requiredSecret);
+                    localProcessor.set(processor);
+                    if (proto.getDomain() != null) {
+                        synchronized (this) {
+                            try {
+                                RequestInfo rp = processor.getRequest().getRequestProcessor();
+                                rp.setGlobalProcessor(global);
+                                ObjectName rpName = new ObjectName
+                                    (proto.getDomain() + ":type=RequestProcessor,worker="
+                                        + proto.getName() + ",name=AjpRequest" + count++ );
+                                Registry.getRegistry(null, null)
+                                    .registerComponent(rp, rpName, null);
+                            } catch (Exception ex) {
+                                log.warn(sm.getString("ajpprotocol.request.register"));
+                            }
+                        }
+                    }
+                }
+
+                if (processor instanceof ActionHook) {
+                    ((ActionHook) processor).action(ActionCode.ACTION_START, null);
+                }
+
+                return processor.process(socket);
+
+            } catch(java.net.SocketException e) {
+                // SocketExceptions are normal
+                AjpAprProtocol.log.debug
+                    (sm.getString
+                     ("ajpprotocol.proto.socketexception.debug"), e);
+            } catch (java.io.IOException e) {
+                // IOExceptions are normal
+                AjpAprProtocol.log.debug
+                    (sm.getString
+                     ("ajpprotocol.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.
+                AjpAprProtocol.log.error
+                    (sm.getString("ajpprotocol.proto.error"), e);
+            } finally {
+                if (processor instanceof ActionHook) {
+                    ((ActionHook) processor).action(ActionCode.ACTION_STOP, null);
+                }
+            }
+            return false;
+        }
+    }
+
+
+    // -------------------- Various implementation classes --------------------
+
+
+    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/jk/java/org/apache/coyote/ajp/AjpMessage.java b/connectors/jk/java/org/apache/coyote/ajp/AjpMessage.java
new file mode 100644
index 0000000..38656ff
--- /dev/null
+++ b/connectors/jk/java/org/apache/coyote/ajp/AjpMessage.java
@@ -0,0 +1,455 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.ajp;
+
+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.res.StringManager;
+
+/**
+ * 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.  
+ *
+ * @author Henri Gomez
+ * @author Dan Milstein
+ * @author Keith Wannamaker
+ * @author Kevin Seguin
+ * @author Costin Manolache
+ */
+public class AjpMessage {
+
+
+    protected static org.apache.commons.logging.Log log =
+        org.apache.commons.logging.LogFactory.getLog(AjpMessage.class);
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+    // ------------------------------------------------------------ Constructor
+
+    public AjpMessage(int packetSize) {
+        buf = new byte[packetSize];
+    }
+   
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Fixed size buffer.
+     */
+    protected byte buf[] = null ;
+
+
+    /**
+     * The current read or write position in the buffer.
+     */
+    protected 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.
+     */
+    protected int len; 
+
+     
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * 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);
+    }
+
+
+    /**
+     * Return the underlying byte buffer.
+     */
+    public byte[] getBuffer() {
+        return buf;
+    }
+
+
+    /**
+     * Return the current message length. 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).
+     */
+    public int getLen() {
+        return len;
+    }
+    
+
+    /**
+     * Add a short integer (2 bytes) to the message.
+     */
+    public void appendInt(int val) {
+        buf[pos++] = (byte) ((val >>> 8) & 0xFF);
+        buf[pos++] = (byte) (val & 0xFF);
+    }
+
+
+    /**
+     * Append a byte (1 byte) to the message.
+     */
+    public void appendByte(int val) {
+        buf[pos++] = (byte) val;
+    }
+
+    
+    /**
+     * Append an int (4 bytes) to the message.
+     */
+    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 MessageBytes out at the current write position.
+     * A null MessageBytes is encoded as a string with length 0.  
+     */
+    public void appendBytes(MessageBytes mb) {
+        if (mb == null) {
+            log.error(sm.getString("ajpmessage.null"), 
+                    new NullPointerException());
+            appendInt(0);
+            appendByte(0);
+            return;
+        }
+        if (mb.getType() == MessageBytes.T_BYTES) {
+            ByteChunk bc = mb.getByteChunk();
+            appendByteChunk(bc);
+        } else if (mb.getType() == MessageBytes.T_CHARS) {
+            CharChunk cc = mb.getCharChunk();
+            appendCharChunk(cc);
+        } else {
+            appendString(mb.toString());
+        }
+    }
+
+    
+    /**
+     * Write a ByteChunk out at the current write position.
+     * A null ByteChunk is encoded as a string with length 0.  
+     */
+    public void appendByteChunk(ByteChunk bc) {
+        if (bc == null) {
+            log.error(sm.getString("ajpmessage.null"), 
+                    new NullPointerException());
+            appendInt(0);
+            appendByte(0);
+            return;
+        }
+        appendBytes(bc.getBytes(), bc.getStart(), bc.getLength());
+    }
+
+    
+    /**
+     * Write a CharChunk out at the current write position.
+     * A null CharChunk is encoded as a string with length 0.  
+     */
+    public void appendCharChunk(CharChunk cc) {
+        if (cc == null) {
+            log.error(sm.getString("ajpmessage.null"), 
+                    new NullPointerException());
+            appendInt(0);
+            appendByte(0);
+            return;
+        }
+        int start = cc.getStart();
+        int end = cc.getEnd();
+        appendInt(end - start);
+        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 <= 31) && (c != 9)) {
+                c = ' ';
+            } else if (c == 127) {
+                c = ' ';
+            }
+            appendByte(c);
+        }
+        appendByte(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) {
+        if (str == null) {
+            log.error(sm.getString("ajpmessage.null"), 
+                    new NullPointerException());
+            appendInt(0);
+            appendByte(0);
+            return;
+        }
+        int len = str.length();
+        appendInt(len);
+        for (int i = 0; i < len; i++) {
+            char c = str.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 <= 31) && (c != 9)) {
+                c = ' ';
+            } else if (c == 127) {
+                c = ' ';
+            }
+            appendByte(c);
+        }
+        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) {
+        if (pos + numBytes + 3 >= buf.length) {
+            log.error(sm.getString("ajpmessage.overflow", "" + numBytes, "" + pos),
+                    new ArrayIndexOutOfBoundsException());
+            if (log.isDebugEnabled()) {
+                dump("Overflow/coBytes");
+            }
+            return;
+        }
+        appendInt(numBytes);
+        System.arraycopy(b, off, buf, pos, numBytes);
+        pos += numBytes;
+        appendByte(0);
+    }
+
+    
+    /**
+     * 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;
+        int b2 = buf[pos++] & 0xFF;
+        return (b1<<8) + b2;
+    }
+
+
+    public int peekInt() {
+        int b1 = buf[pos] & 0xFF;
+        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.recycle();
+            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 (pos + length > buf.length) {
+            log.error(sm.getString("ajpmessage.read", "" + length));
+            return 0;
+        }
+
+        if ((length == 0xFFFF) || (length == -1)) {
+            return 0;
+        }
+
+        System.arraycopy(buf, pos, dest, 0, length);
+        pos += length;
+        pos++; // Skip terminating \0
+        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();
+        // Verify message signature
+        if ((mark != 0x1234) && (mark != 0x4142)) {
+            log.error(sm.getString("ajpmessage.invalid", "" + mark));
+            if (log.isDebugEnabled()) {
+                dump("In: ");
+            }
+            return -1;
+        }
+        if (log.isDebugEnabled())  {
+            log.debug("Received " + len + " " + buf[0]);
+        }
+        return len;
+    }
+    
+
+    /**
+     * Dump the contents of the message, prefixed with the given String.
+     */
+    public void dump(String msg) {
+        if (log.isDebugEnabled()) {
+            log.debug(msg + ": " + buf + " " + pos +"/" + (len + 4));
+        }
+        int max = pos;
+        if (len + 4 > pos)
+            max = len+4;
+        if (max > 1000)
+            max = 1000;
+        if (log.isDebugEnabled()) {
+            for (int j = 0; j < max; j += 16) { 
+                log.debug(hexLine(buf, j, len));
+            }
+        }
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    protected 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();
+    }
+
+
+    protected static String hex(int 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/coyote/ajp/Constants.java b/connectors/jk/java/org/apache/coyote/ajp/Constants.java
new file mode 100644
index 0000000..73489b2
--- /dev/null
+++ b/connectors/jk/java/org/apache/coyote/ajp/Constants.java
@@ -0,0 +1,338 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.ajp;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+
+
+/**
+ * Constants.
+ *
+ * @author Remy Maucherat
+ */
+public final class Constants {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    /**
+     * Package name.
+     */
+    public static final String Package = "org.apache.coyote.ajp";
+
+    public static final int DEFAULT_CONNECTION_LINGER = -1;
+    public static final int DEFAULT_CONNECTION_TIMEOUT = -1;
+    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;
+
+    // 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;
+
+    // Ajp13 specific -  needs refactoring for the new model
+    /**
+     * Maximum Total byte size for a AJP packet
+     */
+    public static final int MAX_PACKET_SIZE = 8192;
+    /**
+     * Size of basic packet header
+     */
+    public static final int H_SIZE = 4;
+    /**
+     * Maximum size of data that can be sent in one packet
+     */
+    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;
+
+    // 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"
+    };
+
+
+    /**
+     * CRLF.
+     */
+    public static final String CRLF = "\r\n";
+
+
+    /**
+     * Server string.
+     */
+    public static final byte[] SERVER_BYTES =
+        ByteChunk.convertToBytes("Server: Apache-Coyote/1.1" + CRLF);
+
+
+    /**
+     * 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;
+
+
+    /* Various constant "strings" */
+    public static final byte[] CRLF_BYTES = ByteChunk.convertToBytes(CRLF);
+    public static final byte[] COLON_BYTES = ByteChunk.convertToBytes(": ");
+    public static final String CONNECTION = "Connection";
+    public static final String CLOSE = "close";
+    public static final byte[] CLOSE_BYTES =
+        ByteChunk.convertToBytes(CLOSE);
+    public static final String KEEPALIVE = "keep-alive";
+    public static final byte[] KEEPALIVE_BYTES =
+        ByteChunk.convertToBytes(KEEPALIVE);
+    public static final String CHUNKED = "chunked";
+    public static final byte[] ACK_BYTES =
+        ByteChunk.convertToBytes("HTTP/1.1 100 Continue" + CRLF + CRLF);
+    public static final String TRANSFERENCODING = "Transfer-Encoding";
+    public static final byte[] _200_BYTES =
+        ByteChunk.convertToBytes("200");
+    public static final byte[] _400_BYTES =
+        ByteChunk.convertToBytes("400");
+    public static final byte[] _404_BYTES =
+        ByteChunk.convertToBytes("404");
+
+
+    /**
+     * 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";
+    public static final byte[] HTTP_11_BYTES =
+        ByteChunk.convertToBytes(HTTP_11);
+
+
+    /**
+     * GET.
+     */
+    public static final String GET = "GET";
+
+
+    /**
+     * HEAD.
+     */
+    public static final String HEAD = "HEAD";
+
+
+    /**
+     * POST.
+     */
+    public static final String POST = "POST";
+
+
+}
diff --git a/connectors/jk/java/org/apache/coyote/ajp/LocalStrings.properties b/connectors/jk/java/org/apache/coyote/ajp/LocalStrings.properties
new file mode 100644
index 0000000..491b5ef
--- /dev/null
+++ b/connectors/jk/java/org/apache/coyote/ajp/LocalStrings.properties
@@ -0,0 +1,37 @@
+# $Id$
+
+# language 
+
+# package org.apache.coyote.ajp
+
+#
+# AjpAprProtocol
+#
+
+ajpprotocol.endpoint.initerror=Error initializing endpoint
+ajpprotocol.endpoint.starterror=Error starting endpoint
+ajpprotocol.init=Initializing Coyote AJP/1.3 on {0}
+ajpprotocol.proto.error=Error reading request, ignored
+ajpprotocol.getattribute=Attribute {0}
+ajpprotocol.setattribute=Attribute {0}: {1}
+ajpprotocol.start=Starting Coyote AJP/1.3 on {0}
+ajpprotocol.stop=Stopping Coyote AJP/1.3 on {0}
+ajpprotocol.pause=Pausing Coyote AJP/1.3 on {0}
+ajpprotocol.endpoint.pauseerror=Error pausing endpoint
+ajpprotocol.resume=Resuming Coyote AJP/1.3 on {0}
+ajpprotocol.endpoint.resumeerror=Error resuming endpoint
+ajpprotocol.failedread=Socket read failed
+ajpprotocol.failedwrite=Socket write failed
+ajpprotocol.request.register=Error registering request processor in JMX
+
+ajpprocessor.header.error=Header message parsing failed
+ajpprocessor.request.prepare=Error preparing request
+ajpprocessor.request.process=Error processing request
+ajpprocessor.certs.fail=Certificate convertion failed
+ajpprocessor.socket.info=Exception getting socket information
+
+ajpmessage.null=Cannot append null value
+ajpmessage.overflow=Overflow error for buffer adding {0} bytes at position {1}
+ajpmessage.read=Requested {0} bytes exceeds message available data
+ajpmessage.invalid=Invalid message recieved with signature {0}
+
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..33e59d5
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/apr/AprImpl.java
@@ -0,0 +1,317 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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("JK: 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..7efa319
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/apr/TomcatStarter.java
@@ -0,0 +1,94 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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/AjpConstants.java b/connectors/jk/java/org/apache/jk/common/AjpConstants.java
new file mode 100644
index 0000000..155c93f
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/common/AjpConstants.java
@@ -0,0 +1,193 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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;
+
+
+/**
+ * Common class for the AJP Protocol values
+ */
+
+public class AjpConstants {
+    // Prefix codes for message types from server to container
+    /**
+     * Message code for initial Request packet
+     */
+    public static final byte JK_AJP13_FORWARD_REQUEST   = 2;
+    /**
+     * Message code for a request to shutdown Tomcat
+     */
+    public static final byte JK_AJP13_SHUTDOWN          = 7;
+    /**
+     * Message code for a Ping request (obsolete)
+     */
+    public static final byte JK_AJP13_PING_REQUEST      = 8;
+    /**
+     * Message code for a CPing request
+     */
+    public static final byte JK_AJP13_CPING_REQUEST     = 10;
+
+    // Prefix codes for message types from container to server
+    /**
+     * Response code that the package is part of the Response body
+     */
+    public static final byte JK_AJP13_SEND_BODY_CHUNK   = 3;
+    /**
+     * Response code that the package is the HTTP headers
+     */
+    public static final byte JK_AJP13_SEND_HEADERS      = 4;
+    /**
+     * Response code for EOT
+     */
+    public static final byte JK_AJP13_END_RESPONSE      = 5;
+    /**
+     * Response code to request the next Request body chunk
+     */
+    public static final byte JK_AJP13_GET_BODY_CHUNK    = 6;
+    /**
+     * Response code to reply to a CPing
+     */
+    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
+    /**
+     * Request Attribute is passed as a String
+     */
+    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"
+    };
+
+    /**
+     * Request Method is passed as a String
+     */
+    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"
+    };
+    // Ajp13 specific -  needs refactoring for the new model
+    /**
+     * Maximum Total byte size for a AJP packet
+     */
+    public static final int MAX_PACKET_SIZE=8192;
+    /**
+     * Size of basic packet header
+     */
+    public static final int H_SIZE=4;  
+    /**
+     * Maximum size of data that can be sent in one packet
+     */
+    public static final int  MAX_READ_SIZE = MAX_PACKET_SIZE - H_SIZE - 2;
+
+}
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..b7069ca
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/common/ChannelJni.java
@@ -0,0 +1,192 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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("JK: 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();
+            msg.end();
+            sentResponse = msg;
+        }
+        
+        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/ChannelNioSocket.java b/connectors/jk/java/org/apache/jk/common/ChannelNioSocket.java
new file mode 100644
index 0000000..94145a5
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/common/ChannelNioSocket.java
@@ -0,0 +1,1193 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.util.Set;
+import java.util.Iterator;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.Selector;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.SocketChannel;
+import java.nio.channels.ClosedSelectorException;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.CancelledKeyException;
+import java.nio.channels.ClosedChannelException;
+import java.net.URLEncoder;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+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;
+
+/** 
+ * Accept ( and send ) TCP messages.
+ *
+ * @author Costin Manolache
+ * @author Bill Barker
+ * jmx:mbean name="jk:service=ChannelNioSocket"
+ *            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
+ *
+ * Jk 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.
+ *
+ * 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.
+ *
+ * XXX Make the 'message type' pluggable
+ */
+public class ChannelNioSocket extends JkHandler
+    implements NotificationBroadcaster, JkChannel {
+    private static org.apache.commons.logging.Log log =
+        org.apache.commons.logging.LogFactory.getLog( ChannelNioSocket.class );
+
+    private int startPort=8009;
+    private int maxPort=8019; // 0 for backward compat.
+    private int port=startPort;
+    private InetAddress inet;
+    private int serverTimeout = 0;
+    private boolean tcpNoDelay=true; // nodelay to true by default
+    private int linger=100;
+    private int socketTimeout = 0;
+    private boolean nioIsBroken = false;
+    private Selector selector = null;
+    private int bufferSize = 8*1024;
+    private int packetSize = 8*1024;
+
+    private 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 )
+    */
+    
+    ThreadPool tp=ThreadPool.createThreadPool(true);
+
+    /* ==================== Tcp socket options ==================== */
+
+    /**
+     * jmx:managed-constructor description="default constructor"
+     */
+    public ChannelNioSocket() {
+        // 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;
+    }
+
+    public void setBufferSize(int bs) {
+        if(bs > 8*1024) {
+            bufferSize = bs;
+        }
+    }
+
+    public int getBufferSize() {
+        return bufferSize;
+    }
+
+    public void setPacketSize(int ps) {
+        if(ps < 8*1024) {
+            ps = 8*1024;
+        }
+        packetSize = ps;
+    }
+
+    public int getPacketSize() {
+        return packetSize;
+    }
+
+    /**
+     * 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) {
+    }
+    
+    public void setNioIsBroken(boolean nib) {
+        nioIsBroken = nib;
+    }
+
+    public boolean getNioIsBroken() {
+        return nioIsBroken;
+    }
+    
+    /* ==================== ==================== */
+    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;
+        }
+    }
+
+    public void resume()  {
+        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
+                }
+            }
+        }
+        SocketChannel sc=sSocket.getChannel().accept();
+        Socket s = sc.socket();
+        ep.setNote( socketNote, s );
+        if(log.isDebugEnabled() )
+            log.debug("Accepted socket " + s +" channel "  + sc.isBlocking());
+
+        try {
+            setSocketOptions(s);
+        } catch(SocketException sex) {
+            log.debug("Error initializing Socket Options", sex);
+        }
+        
+        requestCount++;
+
+        sc.configureBlocking(false);
+        InputStream is=new SocketInputStream(sc);
+        OutputStream os = new SocketOutputStream(sc);
+        ep.setNote( isNote, is );
+        ep.setNote( osNote, os );
+        ep.setControl( tp );
+    }
+
+    private void setSocketOptions(Socket s) throws SocketException {
+        if( socketTimeout > 0 ) 
+            s.setSoTimeout( socketTimeout );
+        
+        s.setTcpNoDelay( tcpNoDelay ); // set socket tcpnodelay state
+
+        if( linger > 0 )
+            s.setSoLinger( true, linger);
+    }
+
+    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;
+            if(log.isInfoEnabled())
+                log.info("JK: ajp13 disabling channelNioSocket");
+            running = true;
+            return;
+        }
+        if (maxPort < startPort)
+            maxPort = startPort;
+        ServerSocketChannel ssc = ServerSocketChannel.open();
+        ssc.configureBlocking(false);
+        for( int i=startPort; i<=maxPort; i++ ) {
+            try {
+                InetSocketAddress iddr = null;
+                if( inet == null ) {
+                    iddr = new InetSocketAddress( i);
+                } else {
+                    iddr=new InetSocketAddress( inet, i);
+                }
+                sSocket = ssc.socket();
+                sSocket.bind(iddr);
+                port=i;
+                break;
+            } catch( IOException ex ) {
+                if(log.isInfoEnabled())
+                    log.info("Port busy " + i + " " + ex.toString());
+                sSocket = null;
+            }
+        }
+
+        if( sSocket==null ) {
+            log.error("Can't find free port " + startPort + " " + maxPort );
+            return;
+        }
+        if(log.isInfoEnabled())
+            log.info("JK: ajp13 listening on " + getAddress() + ":" + port );
+
+        selector = Selector.open();
+        ssc.register(selector, SelectionKey.OP_ACCEPT);
+        // 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( "channelNioSocket".equals( name ) &&
+            port != startPort &&
+            (wEnv.getLocalId()==0) ) {
+            wEnv.setLocalId(  port - startPort );
+        }
+
+        // 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();
+        Poller pollAjp = new Poller();
+        tp.runIt(pollAjp);
+    }
+
+    ObjectName tpOName;
+    ObjectName rgOName;
+    RequestGroupInfo global=new RequestGroupInfo();
+    int JMXRequestNote;
+
+    public void start() throws IOException{
+        if( sSocket==null )
+            init();
+        resume();
+    }
+
+    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(null, null).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 );
+        SelectionKey key = s.getChannel().keyFor(selector);
+        if(key != null) {
+            key.cancel();
+        }
+        s.close();
+    }
+
+    public void destroy() throws IOException {
+        running = false;
+        try {
+            /* If we disabled the channel return */
+            if (port == 0)
+                return;
+            tp.shutdown();
+
+            selector.wakeup().close();
+            sSocket.close(); // XXX?
+            
+            if( tpOName != null )  {
+                Registry.getRegistry(null, null).unregisterComponent(tpOName);
+            }
+            if( rgOName != null ) {
+                Registry.getRegistry(null, null).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    {
+        OutputStream os=(OutputStream)ep.getNote( osNote );
+        os.flush();
+        return 0;
+    }
+
+    public int receive( Msg msg, MsgContext ep )
+        throws IOException    {
+        if (log.isTraceEnabled()) {
+            log.trace("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(ClosedChannelException 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( running ) {
+            try{
+                MsgContext ep=createMsgContext();
+                ep.setSource(this);
+                ep.setWorkerEnv( wEnv );
+                this.accept(ep);
+
+                if( !running ) return;
+                
+                // Since this is a long-running connection, we don't care
+                // about the small GC
+                SocketConnection ajpConn=
+                    new SocketConnection( ep);
+                ajpConn.register(ep);
+            }catch(Exception ex) {
+                if (running)
+                    log.warn("Exception executing accept" ,ex);
+            }
+        }
+    }
+
+
+    // 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.isTraceEnabled() )
+            log.trace("Call next " + type + " " + next);
+
+        // Send notification
+        if( nSupport!=null ) {
+            Notification notif=(Notification)ep.getNote(notifNote);
+            if( notif==null ) {
+                notif=new Notification("channelNioSocket.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;
+    }
+
+    protected class SocketConnection implements ThreadPoolRunnable {
+        MsgContext ep;
+        MsgAjp recv = new MsgAjp(packetSize);
+        boolean inProgress = false;
+
+        SocketConnection(MsgContext ep) {
+            this.ep=ep;
+        }
+
+        public Object[] getInitData() {
+            return null;
+        }
+    
+        public void runIt(Object perTh[]) {
+            if(!processConnection(ep)) {
+                unregister(ep);
+            }
+        }
+
+        public boolean isRunning() {
+            return inProgress;
+        }
+
+        public  void setFinished() {
+            inProgress = false;
+        }
+
+        /** Process a single ajp connection.
+         */
+        boolean processConnection(MsgContext ep) {
+            try {
+                InputStream sis = (InputStream)ep.getNote(isNote);
+                boolean haveInput = true;
+                while(haveInput) {
+                    if( !running || paused ) {
+                        return false;
+                    }
+                    int status= 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 );
+                        return false;
+                    }
+                    ep.setLong( MsgContext.TIMER_RECEIVED, System.currentTimeMillis());
+                    
+                    ep.setType( 0 );
+                    // Will call next
+                    status= invoke( recv, ep );
+                    if( status != JkHandler.OK ) {
+                        log.warn("processCallbacks status " + status );
+                        return false;
+                    }
+                    synchronized(this) {
+                        synchronized(sis) {
+                            haveInput = sis.available() > 0;
+                        }
+                        if(!haveInput) {
+                            setFinished();
+                        } else {
+                            if(log.isDebugEnabled())
+                                log.debug("KeepAlive: "+sis.available());
+                        }
+                    }
+                } 
+            } catch( Exception ex ) {
+                String msg = ex.getMessage();
+                if( msg != null && msg.indexOf( "Connection reset" ) >= 0)
+                    log.debug( "Server has been restarted or reset this connection");
+                else if (msg != null && msg.indexOf( "Read timed out" ) >=0 )
+                    log.debug( "connection timeout reached");            
+                else
+                    log.error( "Error, processing connection", ex);
+                return false;
+            } 
+            return true;
+        }
+
+        synchronized void  process(SelectionKey sk) {
+            if(!sk.isValid()) {
+                return;
+            }
+            if(sk.isReadable()) {
+                SocketInputStream sis = (SocketInputStream)ep.getNote(isNote);
+                boolean isok = sis.readAvailable();
+                if(!inProgress) {
+                    if(isok) {
+                        if(sis.available() > 0 || !nioIsBroken){
+                            inProgress = true;
+                            tp.runIt(this);
+                        }
+                    } else {
+                        unregister(ep);
+                        return;
+                    }
+                } 
+            }
+            if(sk.isWritable()) {
+                Object os = ep.getNote(osNote);
+                synchronized(os) {
+                    os.notify();
+                }
+            }
+        }
+
+        synchronized void unregister(MsgContext ep) {
+            try{
+                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);
+                    if( roname != null ) {
+                        Registry.getRegistry(null, null).unregisterComponent(roname);
+                    }
+                    req.getRequestProcessor().setGlobalProcessor(null);
+                }
+            } catch( Exception ee) {
+                log.error( "Error, releasing connection",ee);
+            }
+        }
+
+        void register(MsgContext ep) {
+            Socket s = (Socket)ep.getNote(socketNote);
+            try {
+                s.getChannel().register(selector, SelectionKey.OP_READ, this);
+            } catch(IOException iex) {
+                log.error("Unable to register connection",iex);
+                unregister(ep);
+            }
+        }
+
+    }
+
+    protected class Poller implements ThreadPoolRunnable {
+
+        Poller() {
+        }
+
+        public Object[] getInitData() {
+            return null;
+        }
+    
+        public void runIt(Object perTh[]) {
+            while(running) {
+                try {
+                    int ns = selector.select(serverTimeout);
+                    if(log.isDebugEnabled())
+                        log.debug("Selecting "+ns+" channels");
+                    if(ns > 0) {
+                        Set sels = selector.selectedKeys();
+                        Iterator it = sels.iterator();
+                        while(it.hasNext()) {
+                            SelectionKey sk = (SelectionKey)it.next();
+                            if(sk.isValid()) {
+                                if(sk.isAcceptable()) {
+                                    acceptConnections();
+                                } else {
+                                    SocketConnection sc = (SocketConnection)sk.attachment();
+                                    sc.process(sk);
+                                }
+                            } else {
+                                sk.cancel();
+                            }
+                            it.remove();
+                        }
+                    }
+                } catch(ClosedSelectorException cse) {
+                    log.debug("Selector is closed");
+                    return;
+                } catch(CancelledKeyException cke) {
+                    log.debug("Key Cancelled", cke);
+                } catch(IOException iex) {
+                    log.warn("IO Error in select",iex);
+                } catch(Exception ex) {
+                    log.warn("Error processing select",ex);
+                }
+            }
+        }
+    }
+
+    protected class SocketInputStream extends InputStream {
+        final int BUFFER_SIZE = 8200;
+        private ByteBuffer buffer = ByteBuffer.allocateDirect(BUFFER_SIZE);
+        private SocketChannel channel;
+        private boolean blocking = false;
+        private boolean isClosed = false;
+        private volatile boolean dataAvailable = false;
+
+        SocketInputStream(SocketChannel channel) {
+            this.channel = channel;
+            buffer.limit(0);
+        }
+
+        public int available() {
+            return buffer.remaining();
+        }
+
+        public void mark(int readlimit) {
+            buffer.mark();
+        }
+
+        public boolean markSupported() {
+            return true;
+        }
+
+        public void reset() {
+            buffer.reset();
+        }
+
+        public synchronized int read() throws IOException {
+            if(!checkAvailable(1)) {
+                block(1);
+            }
+            return buffer.get();
+        }
+
+        private boolean checkAvailable(int nbyte) throws IOException {
+            if(isClosed) {
+                throw new ClosedChannelException();
+            }
+            return buffer.remaining() >=  nbyte;
+        }
+
+        private int fill(int nbyte) throws IOException {
+            int rem = nbyte;
+            int read = 0;
+            boolean eof = false;
+            byte [] oldData = null;
+            if(buffer.remaining() > 0) {
+                // should rarely happen, so short-lived GC shouldn't hurt
+                // as much as allocating a long-lived buffer for this
+                if(log.isDebugEnabled())
+                    log.debug("Saving old buffer: "+buffer.remaining());
+                oldData = new byte[buffer.remaining()];
+                buffer.get(oldData);
+            }
+            buffer.clear();
+            if(oldData != null) {
+                buffer.put(oldData);
+            }
+            while(rem > 0) {
+                int count = channel.read(buffer);
+                if(count < 0) {
+                    eof = true;
+                    break;
+                } else if(count == 0) {
+                    log.debug("Failed to recieve signaled read: ");
+                    break;
+                }
+                read += count;
+                rem -= count;
+            }
+            buffer.flip();
+            return eof ? -1 : read;
+        }
+
+        synchronized boolean readAvailable() {
+            if(blocking) {
+                dataAvailable = true;
+                notify();
+            } else if(dataAvailable) {
+                log.debug("Race Condition");
+            } else {
+                int nr=0;
+
+                try {
+                    nr = fill(1);
+                } catch(ClosedChannelException cce) {
+                    log.debug("Channel is closed",cce);
+                    nr = -1;
+                } catch(IOException iex) {
+                    log.warn("Exception processing read",iex);
+                    nr = -1; // Can't handle this yet
+                }
+                if(nr < 0) {
+                    isClosed = true;
+                    notify();
+                    return false;
+                } else if(nr == 0) {
+                    if(!nioIsBroken) {
+                        dataAvailable = (buffer.remaining() <= 0);
+                    }
+                }
+            }
+            return true;
+        }
+
+        public int read(byte [] data) throws IOException {
+            return read(data, 0, data.length);
+        }
+
+        public synchronized int read(byte [] data, int offset, int len) throws IOException {
+            int olen = len;
+            while(!checkAvailable(len)) {
+                int avail = buffer.remaining();
+                if(avail > 0) {
+                    buffer.get(data, offset, avail);
+                }
+                len -= avail;
+                offset += avail;
+                block(len);
+            }
+            buffer.get(data, offset, len);
+            return olen;
+        }
+
+        private void block(int len) throws IOException {
+            if(len <= 0) {
+                return;
+            }
+            if(!dataAvailable) {
+                blocking = true;
+                if(log.isDebugEnabled())
+                    log.debug("Waiting for "+len+" bytes to be available");
+                try{
+                    wait(socketTimeout);
+                }catch(InterruptedException iex) {
+                    log.debug("Interrupted",iex);
+                }
+                blocking = false;
+            }
+            if(dataAvailable) {
+                dataAvailable = false;
+                if(fill(len) < 0) {
+                    isClosed = true;
+                } 
+            }
+        }
+    }
+
+    protected class SocketOutputStream extends OutputStream {
+        ByteBuffer buffer = ByteBuffer.allocateDirect(bufferSize);
+        SocketChannel channel;
+
+        SocketOutputStream(SocketChannel channel) {
+            this.channel = channel;
+        }
+
+        public void write(int b) throws IOException {
+            if(!checkAvailable(1)) {
+                flush();
+            }
+            buffer.put((byte)b);
+        }
+
+        public void write(byte [] data) throws IOException {
+            write(data, 0, data.length);
+        }
+
+        public void write(byte [] data, int offset, int len) throws IOException {
+            if(!checkAvailable(len)) {
+                flush();
+            }
+            buffer.put(data, offset, len);
+        }
+
+        public void flush() throws IOException {
+            buffer.flip();
+            while(buffer.hasRemaining()) {
+                int count = channel.write(buffer);
+                if(count == 0) {
+                    synchronized(this) {
+                        SelectionKey key = channel.keyFor(selector);
+                        key.interestOps(SelectionKey.OP_WRITE);
+                        if(log.isDebugEnabled())
+                            log.debug("Blocking for channel write: "+buffer.remaining());
+                        try {
+                            wait();
+                        } catch(InterruptedException iex) {
+                            // ignore, since can't happen
+                        }
+                        key.interestOps(SelectionKey.OP_READ);
+                    }
+                }
+            }
+            buffer.clear();
+        }
+
+        private boolean checkAvailable(int len) {
+            return buffer.remaining() >= len;
+        }
+    }
+
+}
+
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..bebb3fa
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/common/ChannelShm.java
@@ -0,0 +1,33 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..4b81833
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/common/ChannelSocket.java
@@ -0,0 +1,901 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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;
+
+/** 
+ * Accept ( and send ) TCP messages.
+ *
+ * @author Costin Manolache
+ * @author Bill Barker
+ * jmx:mbean name="jk:service=ChannelNioSocket"
+ *            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
+ *
+ * Jk 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.
+ *
+ * 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.
+ *
+ * XXX Make the 'message type' pluggable
+ */
+public class ChannelSocket extends JkHandler
+    implements NotificationBroadcaster, JkChannel {
+    private static org.apache.commons.logging.Log log =
+        org.apache.commons.logging.LogFactory.getLog( ChannelSocket.class );
+
+    private int startPort=8009;
+    private int maxPort=8019; // 0 for backward compat.
+    private int port=startPort;
+    private int backlog = 0;
+    private InetAddress inet;
+    private int serverTimeout;
+    private boolean tcpNoDelay=true; // nodelay to true by default
+    private int linger=100;
+    private int socketTimeout;
+    private int bufferSize = -1;
+    private int packetSize = AjpConstants.MAX_PACKET_SIZE;
+
+
+    private long requestCount=0;
+    
+    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;
+    }
+
+    public void setBufferSize(int bs) {
+        bufferSize = bs;
+    }
+
+    public int getBufferSize() {
+        return bufferSize;
+    }
+
+    public void setPacketSize(int ps) {
+        if(ps < AjpConstants.MAX_PACKET_SIZE) {
+            ps = AjpConstants.MAX_PACKET_SIZE;
+        }
+        packetSize = ps;
+    }
+
+    public int getPacketSize() {
+        return packetSize;
+    }
+
+    /** 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) {
+        this.backlog = i;
+    }
+  
+    public int getBacklog() {
+        return backlog;
+    }    
+    
+    /* ==================== ==================== */
+    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 );
+
+        try {
+            setSocketOptions(s);
+        } catch(SocketException sex) {
+            log.debug("Error initializing Socket Options", sex);
+        }
+        
+        requestCount++;
+
+        InputStream is=new BufferedInputStream(s.getInputStream());
+        OutputStream os;
+        if( bufferSize > 0 )
+            os = new BufferedOutputStream( s.getOutputStream(), bufferSize);
+        else
+            os = s.getOutputStream();
+        ep.setNote( isNote, is );
+        ep.setNote( osNote, os );
+        ep.setControl( tp );
+    }
+
+    private void setSocketOptions(Socket s) throws SocketException {
+        if( socketTimeout > 0 ) 
+            s.setSoTimeout( socketTimeout );
+        
+        s.setTcpNoDelay( tcpNoDelay ); // set socket tcpnodelay state
+
+        if( linger > 0 )
+            s.setSoLinger( true, linger);
+    }
+
+    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;
+            if(log.isInfoEnabled())
+                log.info("JK: 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, backlog );
+                } else {
+                    sSocket=new ServerSocket( i, backlog, inet );
+                }
+                port=i;
+                break;
+            } catch( IOException ex ) {
+                if(log.isInfoEnabled())
+                    log.info("Port busy " + i + " " + ex.toString());
+                continue;
+            }
+        }
+
+        if( sSocket==null ) {
+            log.error("Can't find free port " + startPort + " " + maxPort );
+            return;
+        }
+        if(log.isInfoEnabled())
+            log.info("JK: 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(null, null).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(port == 0)
+            return;
+        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(null, null).unregisterComponent(tpOName);
+            }
+            if( rgOName != null ) {
+                Registry.getRegistry(null, null).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( bufferSize > 0 ) {
+            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=createMsgContext(packetSize);
+                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(packetSize);
+            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 ) {
+            String msg = ex.getMessage();
+            if( msg != null && msg.indexOf( "Connection reset" ) >= 0)
+                log.debug( "Server has been restarted or reset this connection");
+            else if (msg != null && msg.indexOf( "Read timed out" ) >=0 )
+                log.debug( "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);
+                    if( roname != null ) {
+                        Registry.getRegistry(null, null).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;
+    }
+
+    static class SocketAcceptor implements ThreadPoolRunnable {
+	ChannelSocket wajp;
+    
+	SocketAcceptor(ChannelSocket wajp ) {
+	    this.wajp=wajp;
+	}
+	
+	public Object[] getInitData() {
+	    return null;
+	}
+	
+	public void runIt(Object thD[]) {
+	    wajp.acceptConnections();
+	}
+    }
+
+    static 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);
+	    ep = null;
+	}
+    }
+
+}
+
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..737a4e5
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/common/ChannelUn.java
@@ -0,0 +1,395 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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(null, null).unregisterComponent(tpOName);
+	    }
+	    if(rgOName != null) {
+		Registry.getRegistry(null, null).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(null, null).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);
+                    if( roname != null ) {
+                        Registry.getRegistry(null, null).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..9023a09
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/common/HandlerDispatch.java
@@ -0,0 +1,101 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..c7cbd4a
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/common/HandlerRequest.java
@@ -0,0 +1,669 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 org.apache.coyote.Request;
+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 );
+
+    /*
+     * Note for Host parsing.
+     */
+    public static final int HOSTBUFFER = 10;
+
+    /**
+     * Thread lock.
+     */
+    private static Object lock = new Object();
+
+    private HandlerDispatch dispatch;
+    private String ajpidDir="conf";
+    
+
+    public HandlerRequest() {
+    }
+
+    public void init() {
+        dispatch=(HandlerDispatch)wEnv.getHandler( "dispatch" );
+        if( dispatch != null ) {
+            // register incoming message handlers
+            dispatch.registerMessageType( AjpConstants.JK_AJP13_FORWARD_REQUEST,
+                                          "JK_AJP13_FORWARD_REQUEST",
+                                          this, null); // 2
+            
+            dispatch.registerMessageType( AjpConstants.JK_AJP13_SHUTDOWN,
+                                          "JK_AJP13_SHUTDOWN",
+                                          this, null); // 7
+            
+            dispatch.registerMessageType( AjpConstants.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( AjpConstants.JK_AJP13_SEND_BODY_CHUNK, // 3
+                                          "JK_AJP13_SEND_BODY_CHUNK",
+                                          this,null );
+        }
+
+        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 ) {
+        if(b) {
+            requiredSecret=Double.toString(Math.random());
+        }
+    }
+
+    public void setDecodedUri( boolean b ) {
+        decoded=b;
+    }
+
+    public boolean isTomcatAuthentication() {
+        return tomcatAuthentication;
+    }
+
+    public void setShutdownEnabled(boolean se) {
+        shutdownEnabled = se;
+    }
+
+    public boolean getShutdownEnabled() {
+        return shutdownEnabled;
+    }
+
+    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;
+    }
+
+    /**
+     * Set the flag to delay the initial body read
+     */
+    public void setDelayInitialRead(boolean dir) {
+	delayInitialRead = dir;
+    }
+
+    /**
+     * Get the flag to tell if we delay the initial body read
+     */
+    public boolean getDelayInitialRead() {
+	return delayInitialRead;
+    }
+
+    // -------------------- Ajp13.id --------------------
+
+    private void generateAjp13Id() {
+        int portInt=8009; // tcpCon.getPort();
+        InetAddress address=null; // tcpCon.getAddress();
+
+        if( requiredSecret == null || !shutdownEnabled )
+            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.store( stopF, "Automatically generated, don't edit" );
+        } catch( IOException ex ) {
+            if(log.isDebugEnabled())
+                log.debug( "Can't create stop file: "+sf,ex );
+        }
+    }
+    
+    // -------------------- Incoming message --------------------
+    private String requiredSecret=null;
+    private int secretNote;
+    private int tmpBufNote;
+
+    private boolean decoded=true;
+    private boolean tomcatAuthentication=true;
+    private boolean registerRequests=true;
+    private boolean shutdownEnabled=false;
+    private boolean delayInitialRead = 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= MessageBytes.newInstance();
+            ep.setNote( tmpBufNote, tmpMB);
+        }
+
+        if( log.isDebugEnabled() )
+            log.debug( "Handling " + type );
+        
+        switch( type ) {
+        case AjpConstants.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 AjpConstants.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;
+            }
+
+            if( !shutdownEnabled ) {
+                log.warn("Ignoring shutdown request: shutdown not enabled");
+                return ERROR;
+            }
+            // forward to the default handler - it'll do the shutdown
+            checkRequest(ep);
+            next.invoke( msg, ep );
+
+            if(log.isInfoEnabled())
+                log.info("Exiting");
+            System.exit(0);
+            
+            return OK;
+
+            // We got a PING REQUEST, quickly respond with a PONG
+        case AjpConstants.JK_AJP13_CPING_REQUEST:
+            msg.reset();
+            msg.appendByte(AjpConstants.JK_AJP13_CPONG_REPLY);
+            ep.getSource().send( msg, ep );
+            ep.getSource().flush( msg, ep ); // Server needs to get it
+            return OK;
+
+        case HANDLE_THREAD_END:
+            return OK;
+
+        default:
+            if(log.isInfoEnabled())
+                log.info("Unknown message " + type);
+        }
+
+        return OK;
+    }
+
+    static int count = 0;
+
+    private Request checkRequest(MsgContext ep) {
+        Request req=ep.getRequest();
+        if( req==null ) {
+            req=new Request();
+            Response res=new Response();
+            req.setResponse(res);
+            ep.setRequest( req );
+            if( registerRequests ) {
+                synchronized(lock) {
+                    ep.getSource().registerRequest(req, ep, count++);
+                }
+            }
+        }
+        return req;
+    }
+
+    private int decodeRequest( Msg msg, MsgContext ep, MessageBytes tmpMB )
+        throws IOException    {
+        // FORWARD_REQUEST handler
+        Request req = checkRequest(ep);
+
+        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());
+        
+        // Translate the HTTP method code to a String.
+        byte methodCode = msg.getByte();
+        if (methodCode != AjpConstants.SC_M_JK_STORED) {
+            String mName=AjpConstants.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
+        long cl=req.getContentLengthLong();
+        if(cl > 0) {
+            JkInputStream jkIS = ep.getInputStream();
+            jkIS.setIsReadRequired(true);
+            if(!delayInitialRead) {
+                jkIS.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 == AjpConstants.SC_A_ARE_DONE )
+                return 200;
+
+            /* Special case ( XXX in future API make it separate type !)
+             */
+            if( attributeCode == AjpConstants.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 == AjpConstants.SC_A_REQ_ATTRIBUTE ) {
+                // 2 strings ???...
+                msg.getBytes( tmpMB );
+                String n=tmpMB.toString();
+                msg.getBytes( tmpMB );
+                String v=tmpMB.toString();
+                req.setAttribute(n, v );
+                if(log.isTraceEnabled())
+                    log.trace("jk Attribute set " + n + "=" + v);
+            }
+
+
+            // 1 string attributes
+            switch(attributeCode) {
+            case AjpConstants.SC_A_CONTEXT      :
+                msg.getBytes( tmpMB );
+                // nothing
+                break;
+                
+            case AjpConstants.SC_A_SERVLET_PATH :
+                msg.getBytes( tmpMB );
+                // nothing 
+                break;
+                
+            case AjpConstants.SC_A_REMOTE_USER  :
+                if( tomcatAuthentication ) {
+                    // ignore server
+                    msg.getBytes( tmpMB );
+                } else {
+                    msg.getBytes(req.getRemoteUser());
+                }
+                break;
+                
+            case AjpConstants.SC_A_AUTH_TYPE    :
+                if( tomcatAuthentication ) {
+                    // ignore server
+                    msg.getBytes( tmpMB );
+                } else {
+                    msg.getBytes(req.getAuthType());
+                }
+                break;
+                
+            case AjpConstants.SC_A_QUERY_STRING :
+                msg.getBytes(req.queryString());
+                break;
+                
+            case AjpConstants.SC_A_JVM_ROUTE    :
+                msg.getBytes(req.instanceId());
+                break;
+                
+            case AjpConstants.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 = MessageBytes.newInstance();
+                    req.setNote(WorkerEnv.SSL_CERT_NOTE, tmpMB2);
+                }
+                // SSL certificate extraction is costy, moved to JkCoyoteHandler
+                msg.getBytes(tmpMB2);
+                break;
+                
+            case AjpConstants.SC_A_SSL_CIPHER   :
+                req.scheme().setString( "https" );
+                msg.getBytes(tmpMB);
+                req.setAttribute(SSLSupport.CIPHER_SUITE_KEY,
+                                 tmpMB.toString());
+                break;
+                
+            case AjpConstants.SC_A_SSL_SESSION  :
+                req.scheme().setString( "https" );
+                msg.getBytes(tmpMB);
+                req.setAttribute(SSLSupport.SESSION_ID_KEY, 
+                                  tmpMB.toString());
+                break;
+                
+            case AjpConstants.SC_A_SECRET  :
+                msg.getBytes(tmpMB);
+                String secret=tmpMB.toString();
+                if(log.isTraceEnabled())
+                    log.trace("Secret: " + secret );
+                // endpoint note
+                ep.setNote( secretNote, secret );
+                break;
+                
+            case AjpConstants.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 = AjpConstants.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();
+                vMB=headers.addValue( bc.getBuffer(),
+                                      bc.getStart(), bc.getLength() );
+            }
+
+            msg.getBytes(vMB);
+
+            if (hId == AjpConstants.SC_REQ_CONTENT_LENGTH ||
+                (hId == -1 && tmpMB.equalsIgnoreCase("Content-Length"))) {
+                // just read the content-length header, so set it
+                long cl = vMB.getLong();
+                if(cl < Integer.MAX_VALUE)
+                    req.setContentLength( (int)cl );
+            } else if (hId == AjpConstants.SC_REQ_CONTENT_TYPE ||
+                (hId == -1 && 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..c9d6139
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/common/JkInputStream.java
@@ -0,0 +1,334 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.coyote.OutputBuffer;
+import org.apache.coyote.InputBuffer;
+import org.apache.coyote.Request;
+import org.apache.coyote.Response;
+
+import org.apache.jk.core.Msg;
+import org.apache.jk.core.MsgContext;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.buf.C2BConverter;
+import org.apache.tomcat.util.http.HttpMessages;
+import org.apache.tomcat.util.http.MimeHeaders;
+
+/** Generic input stream impl on top of ajp
+ */
+public class JkInputStream implements InputBuffer, OutputBuffer {
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( JkInputStream.class );
+
+    private Msg bodyMsg ;
+    private Msg outputMsg ;
+    private MsgContext mc;
+
+    
+    // Holds incoming chunks of request body data
+    private MessageBytes bodyBuff = MessageBytes.newInstance();
+    private MessageBytes tempMB = MessageBytes.newInstance();
+    private boolean end_of_stream=false; 
+    private boolean isEmpty = true;
+    private boolean isFirst = true;
+    private boolean isReplay = false;
+    private boolean isReadRequired = false;
+    private int packetSize = AjpConstants.MAX_PACKET_SIZE;
+    
+    static {
+        // Make certain HttpMessages is loaded for SecurityManager
+        try {
+            Class.forName("org.apache.tomcat.util.http.HttpMessages");
+        } catch(Exception ex) {
+            // ignore
+        }
+    }
+   
+    public JkInputStream(MsgContext context, int bsize) {
+        mc = context;
+        if (bsize < AjpConstants.MAX_PACKET_SIZE)
+            this.packetSize = AjpConstants.MAX_PACKET_SIZE;
+        else
+            this.packetSize = bsize;
+        bodyMsg = new MsgAjp(this.packetSize);
+        outputMsg = new MsgAjp(this.packetSize);
+    }
+
+    /**
+     * @deprecated
+     */
+    public JkInputStream(MsgContext context) {
+        this(context, AjpConstants.MAX_PACKET_SIZE);
+    }
+   
+    // -------------------- Jk specific methods --------------------
+
+
+    /**
+     * Set the flag saying that the server is sending a body
+     */
+    public void setIsReadRequired(boolean irr) {
+        isReadRequired = irr;
+    }
+
+    /**
+     * Return the flag saying that the server is sending a body
+     */
+    public boolean isReadRequired() {
+        return isReadRequired;
+    }
+
+    
+    /** Must be called before or after each request
+     */
+    public void recycle() {
+        if(isReadRequired && isFirst) {
+            // The Servlet never read the request body, so we need to junk it
+            try {
+              receive();
+            } catch(IOException iex) {
+              log.debug("Error consuming request body",iex);
+            }
+        }
+
+        end_of_stream = false;
+        isEmpty = true;
+        isFirst = true;
+        isReplay = false;
+        isReadRequired = false;
+        bodyBuff.recycle();
+        tempMB.recycle();
+    }
+
+
+    public void endMessage() throws IOException {
+        outputMsg.reset();
+        outputMsg.appendByte(AjpConstants.JK_AJP13_END_RESPONSE);
+        outputMsg.appendByte(1);
+        mc.getSource().send(outputMsg, mc);
+        mc.getSource().flush(outputMsg, mc);
+    }
+
+
+    // -------------------- 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();
+        }
+
+        int len=chunk.getLength();
+        byte buf[]=outputMsg.getBuffer();
+        // 4 - hardcoded, byte[] marshalling overhead 
+        int chunkSize=buf.length - outputMsg.getHeaderLength() - 4;
+        int off=0;
+        while( len > 0 ) {
+            int thisTime=len;
+            if( thisTime > chunkSize ) {
+                thisTime=chunkSize;
+            }
+            len-=thisTime;
+            
+            outputMsg.reset();
+            outputMsg.appendByte( AjpConstants.JK_AJP13_SEND_BODY_CHUNK);
+            if( log.isTraceEnabled() ) 
+                log.trace("doWrite " + off + " " + thisTime + " " + len );
+            outputMsg.appendBytes( chunk.getBytes(), chunk.getOffset() + off, thisTime );
+            off+=thisTime;
+            mc.getSource().send( outputMsg, mc );
+        }
+        return 0;
+    }
+
+    public int doRead(ByteChunk responseChunk, Request req) 
+        throws IOException {
+
+        if( log.isDebugEnabled())
+            log.debug( "doRead "  + end_of_stream+
+                       " " + responseChunk.getOffset()+ " " + responseChunk.getLength());
+        if( end_of_stream ) {
+            return -1;
+        }
+
+        if( isFirst && isReadRequired ) {
+            // Handle special first-body-chunk, but only if httpd expects it.
+            if( !receive() ) {
+                return 0;
+            }
+        } else if(isEmpty) {
+            if ( !refillReadBuffer() ){
+                return -1;
+            }
+        }
+        ByteChunk bc = bodyBuff.getByteChunk();
+        responseChunk.setBytes( bc.getBuffer(), bc.getStart(), bc.getLength() );
+        isEmpty = true;
+        return responseChunk.getLength();
+    }
+    
+    /** 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 {
+        isFirst = false;
+        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();
+        }
+
+        // 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;
+        }
+        int blen = bodyMsg.peekInt();
+
+        if( blen == 0 ) {
+            return false;
+        }
+
+        if( log.isTraceEnabled() ) {
+            bodyMsg.dump("Body buffer");
+        }
+        
+        bodyMsg.getBytes(bodyBuff);
+        if( log.isTraceEnabled() )
+            log.trace( "Data:\n" + bodyBuff);
+        isEmpty = false;
+        return true;
+    }
+    
+    /**
+     * 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(isReplay) {
+            end_of_stream = true; // we've read everything there is
+        }
+        if (end_of_stream) {
+            if( log.isDebugEnabled() ) 
+                log.debug("refillReadBuffer: end of stream " );
+            return false;
+        }
+
+        // Why not use outBuf??
+        bodyMsg.reset();
+        bodyMsg.appendByte(AjpConstants.JK_AJP13_GET_BODY_CHUNK);
+        // bodyMsg.appendInt(AjpConstants.MAX_READ_SIZE);
+        bodyMsg.appendInt(packetSize - AjpConstants.H_SIZE - 2);
+        
+        if( log.isDebugEnabled() )
+            log.debug("refillReadBuffer " + Thread.currentThread());
+
+        mc.getSource().send(bodyMsg, mc);
+        mc.getSource().flush(bodyMsg, mc); // Server needs to get it
+
+        // In JNI mode, response will be in bodyMsg. In TCP mode, response need to be
+        // read
+
+        boolean moreData=receive();
+        if( !moreData ) {
+            end_of_stream=true;
+        }
+        return moreData;
+    }
+
+    public void appendHead(Response res) throws IOException {
+        if( log.isDebugEnabled() )
+            log.debug("COMMIT sending headers " + res + " " + res.getMimeHeaders() );
+        
+        C2BConverter c2b=mc.getConverter();
+        
+        outputMsg.reset();
+        outputMsg.appendByte(AjpConstants.JK_AJP13_SEND_HEADERS);
+        outputMsg.appendInt( res.getStatus() );
+        
+        String message=res.getMessage();
+        if( message==null ){
+            message= HttpMessages.getMessage(res.getStatus());
+        } else {
+            message = message.replace('\n', ' ').replace('\r', ' ');
+        }
+        tempMB.setString( message );
+        c2b.convert( tempMB );
+        outputMsg.appendBytes(tempMB);
+
+        // 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();
+        outputMsg.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 );
+            outputMsg.appendBytes( hN );
+                        
+            MessageBytes hV=headers.getValue(i);
+            c2b.convert( hV );
+            outputMsg.appendBytes( hV );
+        }
+        mc.getSource().send( outputMsg, mc );
+    }
+
+    /**
+     * Set the replay buffer for Form auth
+     */
+    public void setReplay(ByteChunk replay) {
+        isFirst = false;
+        isEmpty = false;
+        isReplay = true;
+        bodyBuff.setBytes(replay.getBytes(), replay.getStart(), replay.getLength());
+    }
+
+
+}
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..e03f7b6
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/common/JkMX.java
@@ -0,0 +1,395 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 boolean log4jEnabled=true;
+    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 Log4j MBean)
+     */
+    public void setLog4jEnabled(boolean b) {
+        log4jEnabled=b;
+    }
+        
+    public boolean getLog4jEnabled() {
+        return log4jEnabled;
+    }
+
+    /** 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);
+                if(log.isInfoEnabled())
+                    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);
+                if(log.isInfoEnabled())
+                    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);
+                if(log.isInfoEnabled())
+                    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);
+                if(log.isInfoEnabled())
+                    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 {
+            if(log.isInfoEnabled())
+                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();
+            }
+            if( log4jEnabled) {
+                try {
+                    registerObject("org.apache.log4j.jmx.HierarchyDynamicMBean" ,
+                                   "log4j:hierarchy=default");
+                    if(log.isInfoEnabled())
+                         log.info("Registering the JMX hierarchy for Log4J ");
+                } catch( Throwable t ) {
+                    if(log.isInfoEnabled())
+                        log.info("Can't enable log4j mx: ",t);
+                }
+            }
+        } 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..ec3706c
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/common/JniHandler.java
@@ -0,0 +1,318 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 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(null, null).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.setConverter( c2b );
+
+            MessageBytes tmpMB= MessageBytes.newInstance();
+            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=AprImpl.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..945220b
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/common/ModJkMX.java
@@ -0,0 +1,452 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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(null, null).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(null, null);
+            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(null, null).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..76b6f2e
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/common/MsgAjp.java
@@ -0,0 +1,353 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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[];
+    // 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; 
+
+    /**
+     * The maximum packet size
+     */
+    private int bufsize;
+
+    /**
+     * Constructor that takes a buffer size
+     */
+    public MsgAjp(int bsize) {
+        if(bsize < 8*1024) {
+            bsize = 8*1024;
+        }
+        bufsize = bsize;
+        buf = new byte[bsize];
+    
+    }
+
+    /**
+     * No arg constructor.
+     * @deprecated Use the buffer size constructor.
+     */
+    public MsgAjp() {
+        this(8*1024);
+    }
+
+    /**
+     * 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.recycle();
+            return;
+        }
+        mb.setBytes( buf, pos, length );
+        mb.getCharChunk().recycle();
+        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) {
+        if( log.isDebugEnabled() ) 
+            log.debug( msg + ": " + buf + " " + pos +"/" + (len + 4));
+        int max=pos;
+        if( len + 4 > pos )
+            max=len+4;
+        if( max >1000 ) max=1000;
+        if( log.isDebugEnabled() ) 
+            for( int j=0; j < max; j+=16 ) 
+                log.debug( 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..441ab2b
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/common/Shm.java
@@ -0,0 +1,331 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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=mCtx.getConverter();
+        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=mCtx.getConverter();
+        
+        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);
+        }
+
+        if (log.isDebugEnabled())
+            log.debug("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=mCtx.getConverter();
+        
+        msg.appendByte( SHM_WRITE_SLOT );
+        appendString( msg, slotName, c2b );
+
+        // number of channels for this instance
+        msg.appendInt( 0 );
+        msg.appendInt( 0 );
+        
+        if (log.isDebugEnabled())
+            log.debug("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 ) {
+        if (log.isDebugEnabled()) {
+            log.debug("Usage: ");
+            log.debug("  Shm [OPTIONS]");
+            log.debug("");
+            log.debug("  -file SHM_FILE");
+            log.debug("  -group GROUP ( can be specified multiple times )");
+            log.debug("  -host HOST");
+            log.debug("  -port PORT");
+            log.debug("  -unixSocket UNIX_FILE");
+            //        log.debug("  -priority XXX");
+            //        log.debug("  -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..763ee9e
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/common/Shm14.java
@@ -0,0 +1,97 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.
+    
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( Shm14.class );
+    
+    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
+    {
+        if (log.isDebugEnabled())
+            log.debug("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..3767850
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/common/WorkerDummy.java
@@ -0,0 +1,91 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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=MessageBytes.newInstance();
+    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(AjpConstants.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( AjpConstants.JK_AJP13_SEND_BODY_CHUNK);
+        msg.appendInt( body.getLength() );
+        msg.appendBytes( body );
+
+        
+        ep.getSource().invoke(msg, ep);
+
+        msg.reset();
+        msg.appendByte( AjpConstants.JK_AJP13_END_RESPONSE );
+        msg.appendInt( 1 );
+        
+        ep.getSource().invoke(msg, ep );
+        return OK;
+    }
+    
+    private static final int dL=0;
+}
+
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..ab82ceb
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/config/ApacheConfig.java
@@ -0,0 +1,574 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 { 
+
+    private static org.apache.commons.logging.Log log =
+        org.apache.commons.logging.LogFactory.getLog(ApacheConfig.class);
+
+    /** 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.info( "mod_jk location: " + modJk );
+	    log.info( "Make sure it is installed corectly or " +
+		 " set the config location" );
+	    log.info( "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.warn( "Can't find workers.properties at " + workersConfig );
+	    log.warn( "Please install it in the default location or " +
+		 " set the config location" );
+	    log.warn( "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.debug("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( log.isDebugEnabled() )
+            log.debug( "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( log.isDebugEnabled() )
+            log.debug( "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( log.isDebugEnabled() )
+            log.debug( "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..bc84aab
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/config/BaseJkConfig.java
@@ -0,0 +1,516 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 {
+    private static org.apache.commons.logging.Log log =
+        org.apache.commons.logging.LogFactory.getLog(BaseJkConfig.class);
+
+    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=true;
+
+    // -------------------- 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.warn("Unable to open config file");
+            return;
+        }
+        Lifecycle who = evt.getLifecycle();
+        if( who instanceof Server ) {
+            executeServer((Server)who, mod_jk);
+        } else if(who instanceof Engine) {
+            executeEngine((Engine)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) {
+        if(egn.getJvmRoute() != null) {
+            jkWorker = egn.getJvmRoute();
+        }
+        executeServer(egn.getService().getServer(), 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.
+     *   @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.
+     *  @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;
+    }
+}
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..c66a8ce
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/config/GeneratorApache2.java
@@ -0,0 +1,194 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..7f9c237
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/config/GeneratorJk1.java
@@ -0,0 +1,113 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..f06c6e8
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/config/GeneratorJk2.java
@@ -0,0 +1,144 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..6e996d3
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/config/IISConfig.java
@@ -0,0 +1,306 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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  { 
+    private static org.apache.commons.logging.Log log =
+        org.apache.commons.logging.LogFactory.getLog(IISConfig.class);
+
+    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.info("Generating IIS registry file = "+regConfig );
+	    generateRegistrySettings(regfile);
+	    regfile.close();
+	} catch(IOException iex) {
+	    log.warn("Unable to generate registry file " +regConfig);
+	    return false;
+	}
+	log.info("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.debug("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.debug("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( log.isDebugEnabled() )
+            log.debug( "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( log.isDebugEnabled() )
+            log.debug( "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..ad17c8d
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/config/NSConfig.java
@@ -0,0 +1,329 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 { 
+    private static org.apache.commons.logging.Log log =
+        org.apache.commons.logging.LogFactory.getLog(NSConfig.class);
+
+    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.info("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.debug("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.debug("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( log.isDebugEnabled() )
+            log.debug( "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( log.isDebugEnabled() )
+            log.debug( "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..e065fbb
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/config/WebXml2Jk.java
@@ -0,0 +1,452 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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"));
+            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
+        {
+            if (log.isDebugEnabled())
+                log.debug("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..76cb930
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/core/JkChannel.java
@@ -0,0 +1,77 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.coyote.Request;
+
+/**
+ * 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..dd0e0a7
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/core/JkHandler.java
@@ -0,0 +1,201 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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(8*1024);
+     }
+
+     public MsgContext createMsgContext(int bsize) {
+        return new MsgContext(bsize);
+     }
+ 
+    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(null, null).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..da66ba7
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/core/Msg.java
@@ -0,0 +1,154 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..70c231c
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/core/MsgContext.java
@@ -0,0 +1,391 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.io.ByteArrayInputStream;
+import java.net.InetAddress;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.ActionHook;
+import org.apache.coyote.Request;
+import org.apache.coyote.Response;
+
+import org.apache.tomcat.util.buf.C2BConverter;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.net.SSLSupport;
+import org.apache.jk.common.JkInputStream;
+
+
+/**
+ *
+ * @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 implements ActionHook {
+    private static org.apache.commons.logging.Log log =
+        org.apache.commons.logging.LogFactory.getLog(MsgContext.class);
+    private static org.apache.commons.logging.Log logTime=
+        org.apache.commons.logging.LogFactory.getLog( "org.apache.jk.REQ_TIME" );
+
+    private int type;
+    private Object notes[]=new Object[32];
+    private JkHandler next;
+    private JkChannel source;
+    private JkInputStream jkIS;
+    private C2BConverter c2b;
+    private Request 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;
+
+    // Status codes
+    public static final int JK_STATUS_NEW=0;
+    public static final int JK_STATUS_HEAD=1;
+    public static final int JK_STATUS_CLOSED=2;
+    public static final int JK_STATUS_ERROR=3;
+
+    public MsgContext(int bsize) {
+        try {
+            c2b = new C2BConverter("iso-8859-1");
+        } catch(IOException iex) {
+            log.warn("Can't happen", iex);
+        }
+       jkIS = new JkInputStream(this, bsize);
+    }
+
+    /**
+     * @deprecated
+     */
+    public MsgContext() {
+        this(8*1024);
+    }
+    
+    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( Request req ) {
+        this.req=req;
+        req.setInputBuffer(jkIS);
+        Response res = req.getResponse();
+        res.setOutputBuffer(jkIS);
+        res.setHook(this);
+    }
+
+    public final Request 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;
+    }
+
+    public final C2BConverter getConverter() {
+        return c2b;
+    }
+
+    public final void setConverter(C2BConverter c2b) {
+        this.c2b = c2b;
+    }
+    
+    public final boolean isLogTimeEnabled() {
+        return logTime.isDebugEnabled();
+    }
+
+    public JkInputStream getInputStream() {
+        return jkIS;
+    }
+
+    /** 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;
+    }
+
+    // -------------------- Coyote Action implementation --------------------
+    
+    public void action(ActionCode actionCode, Object param) {
+        if( actionCode==ActionCode.ACTION_COMMIT ) {
+            if( log.isDebugEnabled() ) log.debug("COMMIT " );
+            Response res=(Response)param;
+
+            if(  res.isCommitted() ) {
+                if( log.isDebugEnabled() )
+                    log.debug("Response already committed " );
+            } else {
+                try {
+                    jkIS.appendHead( res );
+                } catch(IOException iex) {
+                    log.warn("Unable to send headers",iex);
+                    setStatus(JK_STATUS_ERROR);
+                }
+            }
+        } 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 " );
+            try {
+                source.flush( null, this );
+            } catch(IOException iex) {
+                // This is logged elsewhere, so debug only here
+                log.debug("Error during flush",iex);
+                Response res = (Response)param;
+                res.setErrorException(iex);
+                setStatus(JK_STATUS_ERROR);
+            }
+            
+        } else if( actionCode==ActionCode.ACTION_CLOSE ) {
+            if( log.isDebugEnabled() ) log.debug("CLOSE " );
+            
+            Response res=(Response)param;
+            if( getStatus()== JK_STATUS_CLOSED || getStatus() == JK_STATUS_ERROR) {
+                // 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 );
+            try {            
+                jkIS.endMessage();
+            } catch(IOException iex) {
+                log.warn("Error sending end packet",iex);
+                setStatus(JK_STATUS_ERROR);
+            }
+            if(getStatus() != JK_STATUS_ERROR) {
+                setStatus(JK_STATUS_CLOSED );
+            }
+
+            if( logTime.isDebugEnabled() ) 
+                logTime(res.getRequest(), res);
+        } else if( actionCode==ActionCode.ACTION_REQ_SSL_ATTRIBUTE ) {
+            Request req=(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 ) {
+            Request req=(Request)param;
+
+            // If remoteHost not set by JK, get it's name from it's remoteAddr
+            if( req.remoteHost().isNull()) {
+                try {
+                    req.remoteHost().setString(InetAddress.getByName(
+                                               req.remoteAddr().toString()).
+                                               getHostName());
+                } catch(IOException iex) {
+                    if(log.isDebugEnabled())
+                        log.debug("Unable to resolve "+req.remoteAddr());
+                }
+            }
+        } else if( actionCode==ActionCode.ACTION_ACK ) {
+            if( log.isTraceEnabled() )
+                log.trace("ACK " );
+        } else if ( actionCode == ActionCode.ACTION_REQ_SET_BODY_REPLAY ) {
+            if( log.isTraceEnabled() )
+                log.trace("Replay ");
+            ByteChunk bc = (ByteChunk)param;
+            jkIS.setReplay(bc);
+            req.setContentLength(bc.getLength()); // reset so re-read
+        }
+    }
+    
+
+    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();
+        String uri=req.requestURI().toString();
+        if( uri.indexOf( ".gif" ) >0 ) return;
+        
+        setLong( MsgContext.TIMER_POST_REQUEST, System.currentTimeMillis());
+        long t1= getLong( MsgContext.TIMER_PRE_REQUEST ) -
+            getLong( MsgContext.TIMER_RECEIVED );
+        long t2= getLong( MsgContext.TIMER_POST_REQUEST ) -
+            getLong( MsgContext.TIMER_PRE_REQUEST );
+        
+        logTime.debug("Time pre=" + t1 + "/ service=" + t2 + " " +
+                      res.getContentLength() + " " + 
+                      uri );
+    }
+
+    public void recycle() {
+        jkIS.recycle();
+    }
+}
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..18145eb
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/core/WorkerEnv.java
@@ -0,0 +1,145 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..380185b
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/mbeans-descriptors.xml
@@ -0,0 +1,541 @@
+<?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="The port number on which we listen for ajp13 requests"
+                type="int"/>
+    <attribute   name="maxPort"
+          description="The max port number on which we listen for ajp13 requests"
+                type="int"/>
+    <attribute   name="address"
+          description="The IP address on which to bind"
+                 type="java.lang.String"/>
+    <attribute   name="maxSpareThreads"
+          description="The maximum number of unused request processing threads"
+                 type="int"/>
+    <attribute   name="maxThreads"
+          description="The maximum number of request processing threads to be created"
+                 type="int"/>
+    <attribute   name="minSpareThreads"
+          description="The number of request processing threads that will be created"
+                 type="int"/>
+    <attribute   name="tcpNoDelay"
+          description="Should we use TCP no delay?"
+                 type="boolean"/>
+    <attribute   name="soLinger"
+          description="Linger value on the incoming connection"
+                 type="int"/>
+    <attribute   name="soTimeout"
+          description="Socket timeout"
+                 type="int"/>
+    <attribute   name="requestCount"
+          description="current request count"
+                 type="int"
+            writeable="false"/>
+    <attribute   name="daemon"
+          description="are worker threads on daemon mode"
+                 type="boolean"
+            writeable="false"/>
+    <attribute name="packetSize"
+          description="The maximum AJP packet size"
+          type="int" />
+
+    <operation name="start"
+               description="Start, if server socket no create call init"
+               impact="ACTION"
+               returnType="void" />
+    <operation name="stop"
+               description="Stop"
+               impact="ACTION"
+               returnType="void" />
+    <operation name="pause"
+               description="Pause ajp socket, no new connection accepted"
+               impact="ACTION"
+               returnType="void"/>
+    <operation name="resume"
+               description="Resume socket for new connections"
+               impact="ACTION"
+               returnType="void"/>
+    <operation name="reinit"
+               description="Init and Destroy"
+               impact="ACTION"
+               returnType="void" />
+    <operation name="init"
+               description="Init"
+               impact="ACTION"
+               returnType="void" />
+    <operation name="destroy"
+               description="Destroy"
+               impact="ACTION"
+               returnType="void" />
+    <operation name="resetCounters"
+               description="reset request counter"
+               impact="ACTION"
+               returnType="void"/>
+
+
+  </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..f5bdc94
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/server/JkCoyoteHandler.java
@@ -0,0 +1,228 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.IOException;
+import java.util.Iterator;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.apache.commons.modeler.Registry;
+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.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 Jk 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 {
+    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
+
+    // ----------------------------------------------------------- DoPrivileged
+    private boolean paused = false;
+    int epNote;
+    Adapter adapter;
+    protected JkMain jkMain=null;
+
+    /** 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) ;
+    }
+
+    public Iterator getAttributeNames() {
+       return properties.keySet().iterator();
+    }
+
+    /** 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();
+
+        } catch( Exception ex ) {
+            log.error("Error during init",ex);
+        }
+    }
+
+    public void start() {
+        try {
+            if( oname != null && getJkMain().getDomain() == null) {
+                try {
+                    ObjectName jkmainOname = 
+                        new ObjectName(oname.getDomain() + ":type=JkMain");
+                    Registry.getRegistry(null, null)
+                        .registerComponent(getJkMain(), jkmainOname, "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();
+    }
+
+    
+    // -------------------- Jk handler implementation --------------------
+    // Jk Handler mehod
+    public int invoke( Msg msg, MsgContext ep ) 
+        throws IOException {
+        if( ep.isLogTimeEnabled() ) 
+            ep.setLong( MsgContext.TIMER_PRE_REQUEST, System.currentTimeMillis());
+        
+        Request req=ep.getRequest();
+        Response res=req.getResponse();
+
+        if( log.isDebugEnabled() )
+            log.debug( "Invoke " + req + " " + res + " " + req.requestURI().toString());
+        
+        res.setNote( epNote, ep );
+        ep.setStatus( MsgContext.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() != MsgContext.JK_STATUS_CLOSED) {
+            res.finish();
+        }
+
+        req.recycle();
+        req.updateCounters();
+        res.recycle();
+        ep.recycle();
+        if( ep.getStatus() == MsgContext.JK_STATUS_ERROR ) {
+            return ERROR;
+        }
+        ep.setStatus( MsgContext.JK_STATUS_NEW );
+        rp.setStage(Constants.STAGE_KEEPALIVE);
+        return OK;
+    }
+
+
+    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..625e2df
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/server/JkMain.java
@@ -0,0 +1,695 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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
+ */
+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("channelNioSocket", "org.apache.jk.common.ChannelNioSocket");
+        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 ) {
+            log.info("Error adding SSL Protocol Handler",ex);
+        }
+    }
+
+    // -------------------- Setting --------------------
+    
+    /** Load a .properties file into and set the values
+     *  into jk2 configuration.
+     */
+    public void setPropertiesFile( String p  ) {
+        propFile=p;
+        if( started ) {
+            loadPropertiesFile();
+        }
+    }
+
+    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 );
+        } 
+        if( "propertiesFile".equals( n ) ) {
+            setPropertiesFile( 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(log.isDebugEnabled())
+            log.debug("Starting Jk2, base dir= " + home  );
+        loadPropertiesFile();
+
+        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, null );
+        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 ) {
+            log.warn("Error running",ex);
+        }
+    }
+
+    // -------------------- Private methods --------------------
+
+
+    private boolean checkPropertiesFile() {
+        if(propFile == null) {
+            return false;
+        }
+        propsF = new File(propFile);
+        if(!propsF.isAbsolute()) {
+            String home = getWorkerEnv().getJkHome();
+            if( home == null ) {
+                return false;
+            }
+            propsF = new File(home, propFile);
+        }
+        return propsF.exists();
+    }
+            
+    private void loadPropertiesFile() {
+        if(!checkPropertiesFile()) {
+            return;
+        }
+
+        try {
+            props.load( new FileInputStream(propsF) );
+        } catch(IOException ex ){
+            log.warn("Unable to load properties from "+propsF,ex);
+        }
+    }
+
+    public  void saveProperties() {
+        if( !saveProperties) return;
+        
+        if(propsF == null) {
+            log.warn("No properties file specified. Unable to save");
+            return;
+        }
+        // Temp - to check if it works
+        File outFile= new File(propsF.getParentFile(), propsF.getName()+".save");
+        log.debug("Saving properties " + outFile );
+        try {
+            props.store( new FileOutputStream(outFile), "AUTOMATICALLY GENERATED" );
+        } catch(IOException ex ){
+            log.warn("Unable to save to "+outFile,ex);
+        }
+    }
+
+    // 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("bufferSize", "channelSocket.bufferSize");
+        replacements.put("tomcatAuthentication", "request.tomcatAuthentication");            
+        replacements.put("packetSize", "channelSocket.packetSize");
+    }
+
+    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 {
+                ObjectName handlerOname = new ObjectName
+                    (this.domain + ":" + "type=JkHandler,name=" + fullName);
+                Registry.getRegistry(null, null).registerComponent(handler, handlerOname, classN);
+            } 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..05ac47f
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/ApacheConfig.java
@@ -0,0 +1,54 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..cc28eb3
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/Def.java
@@ -0,0 +1,83 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..daa1a9d
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/JkData.java
@@ -0,0 +1,86 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..4f75222
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/JniConfig.java
@@ -0,0 +1,60 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..106d3c3
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/SoTask.java
@@ -0,0 +1,540 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..6d69c09
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/Source.java
@@ -0,0 +1,51 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..1325edc
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/CcCompiler.java
@@ -0,0 +1,127 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..01107b0
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/CompilerAdapter.java
@@ -0,0 +1,289 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..c82dbad
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/GcjCompiler.java
@@ -0,0 +1,117 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..bc16305
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/GcjLinker.java
@@ -0,0 +1,126 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..a401a14
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/LibtoolCompiler.java
@@ -0,0 +1,106 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..2f9849a
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/LibtoolLinker.java
@@ -0,0 +1,158 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..d724228
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/LinkerAdapter.java
@@ -0,0 +1,65 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..bbaf102
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/MsvcCompiler.java
@@ -0,0 +1,170 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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)
+        {
+            log("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..e708df5
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/MsvcLinker.java
@@ -0,0 +1,195 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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)
+        {
+            log("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..d2b733b
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/MwccCompiler.java
@@ -0,0 +1,165 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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)
+        {
+            log("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..a8c9eed
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/MwldLinker.java
@@ -0,0 +1,223 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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)
+        {
+            log("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/jkstatus/build.xml b/connectors/jk/jkstatus/build.xml
new file mode 100644
index 0000000..9411cd3
--- /dev/null
+++ b/connectors/jk/jkstatus/build.xml
@@ -0,0 +1,142 @@
+<project name="JkStatus" default="dist" basedir=".">
+
+
+  <!-- ===================== Initialize Property Values =================== -->
+
+  <property file="../../../build/build.properties" />
+  <property file="../../../build/build.properties.default" />
+
+  <!-- Build Defaults -->
+  <property name="jtc.home"  location="../.."/>
+  <property name="catalina.build" location="../../../build/build"/>
+  <property name="jk.build"  value="${jtc.home}/jk/jkstatus/build"/>
+  <property name="jk.dist"   value="${jtc.home}/jk/jkstatus/dist"/>
+
+    <!-- Construct Catalina classpath -->
+  <path id="jtc.classpath">
+    <pathelement location="${catalina.build}/server/lib/catalina.jar"/>
+    <pathelement location="${catalina.build}/server/lib/catalina-ant.jar"/>
+    <pathelement location="${catalina.build}/server/lib/tomcat-util.jar"/>
+    <pathelement location="${commons-modeler.jar}"/>
+    <pathelement location="${commons-logging.jar}"/>
+    <pathelement location="${jmx.jar}"/>
+    <pathelement location="${ant.home}/lib/ant.jar"/>
+      <pathelement location="${catalina.build}/common/lib/servlet-api.jar"/>
+  </path>
+
+    <!-- Source path -->
+  <path id="javadoc.sourcepath">
+    <pathelement location="src/share"/>
+  </path>
+
+
+  <!-- =================== BUILD: Set compile flags ======================= -->
+  <target name="flags">
+    <!-- JDK flags -->
+    <available property="jdk.1.2.present" classname="java.util.HashMap" />
+    <available property="jdk.1.3.present" 
+     classname="java.lang.reflect.Proxy" />
+    <available property="jdk.1.4.present" classname="java.nio.Buffer" />
+  </target>
+
+
+  <!-- =================== BUILD: Set compile flags ======================= -->
+  <target name="flags.display" depends="flags" unless="flags.hide">
+
+    <echo message="--- Build environment for Catalina ---" />
+
+    <echo message="If ${property_name} is displayed, then the property is not set)" />
+
+    <echo message="--- Build options ---" />
+    <echo message="full.dist=${full.dist}" />
+    <echo message="build.sysclasspath=${build.sysclasspath}" />
+    <echo message="compile.debug=${compile.debug}" />
+    <echo message="compile.deprecation=${compile.deprecation}" />
+    <echo message="compile.optimize=${compile.optimize}" />
+
+    <echo message="--- Ant Flags ---" />
+    <echo message="&lt;style&gt; task available (required)=${style.available}" />
+
+    <echo message="--- JDK ---" />
+    <echo message="jdk.1.2.present=${jdk.1.2.present}" />
+    <echo message="jdk.1.3.present=${jdk.1.3.present}" />
+    <echo message="jdk.1.4.present=${jdk.1.4.present}" />
+
+  </target>
+
+  <!-- =================== BUILD: Create Directories ====================== -->
+  <target name="build-prepare">
+    <mkdir dir="${catalina.build}"/>
+    <mkdir dir="${catalina.build}/classes"/>
+    <mkdir dir="${jk.dist}"/>
+    <mkdir dir="${jk.build}"/>
+    <mkdir dir="${jk.build}/classes"/>
+  </target>
+
+  <!-- ================ BUILD: Compile Catalina Components ================ -->
+  
+  <target name="build-jk-status" depends="build-prepare">
+    <!-- Compile internal server components -->
+    <javac srcdir="${basedir}/src/share" destdir="${jk.build}/classes"
+           debug="${compile.debug}" deprecation="${compile.deprecation}"
+           optimize="${compile.optimize}"
+           excludes="**/.svn/**"  	   
+    	>
+        <classpath refid="jtc.classpath" />
+    </javac>
+    <copy file="${basedir}/src/share/org/apache/jk/status/LocalStrings.properties"
+    	  tofile="${jk.build}/classes/org/apache/jk/status/LocalStrings.properties"/>
+    <copy file="${basedir}/src/share/org/apache/jk/status/mbeans-descriptors.xml"
+    	  tofile="${jk.build}/classes/org/apache/jk/status/mbeans-descriptors.xml"/>
+    <copy file="${basedir}/src/share/org/apache/jk/status/antlib.xml"
+    	  tofile="${jk.build}/classes/org/apache/jk/status/antlib.xml"/>
+    <copy file="${basedir}/src/share/org/apache/jk/status/jkstatus.tasks"
+    	  tofile="${jk.build}/classes/org/apache/jk/status/jkstatus.tasks"/>
+   </target>
+
+
+  <!-- ================ BUILD: Create Catalina Javadocs =================== -->
+  <target name="javadoc">
+    <delete dir="${jk.build}/javadoc"/>
+    <mkdir dir="${jk.build}/javadoc"/>
+    <javadoc packagenames="org.apache.jk.status.*"
+      classpathref="jtc.classpath"
+      sourcepathref="javadoc.sourcepath"
+      destdir="${jk.build}/javadoc"
+      author="true"
+      version="true"
+      windowtitle="Jk Status Internal API Documentation"
+      doctitle="Jk Status API"
+      bottom="Copyright &#169; 2000-2005 Apache Software Foundation.  All Rights Reserved."
+    />
+  </target>
+
+  <!-- ======================= BUILD: Clean Directory ===================== -->
+  <target name="build-clean">
+    <delete dir="${jk.build}"/>
+  </target>
+
+  <!-- ==================== BUILD: Rebuild Everything ===================== -->
+
+
+  <!-- ================ DIST: Create Distribution ========================= -->
+  <target name="dist" depends="build-jk-status">
+    
+    <jar destfile="${jk.dist}/tomcat-jkstatus-ant.jar"
+         basedir="${jk.build}/classes">
+       <include name="org/apache/jk/status/**" />
+       <exclude name="**/package.html" />
+       <exclude name="**/LocalStrings_*" />
+    </jar>
+  </target>
+
+  <target name="copy" depends="dist" >
+     <copy file="${jk.dist}/tomcat-jkstatus-ant.jar" todir="${catalina.build}/server/lib" />
+     <copy file="conf/jkstatus-tasks.xml" todir="${catalina.build}/bin" />
+  </target>
+  
+  <!-- ======================== DIST: Clean Directory ===================== -->
+
+  <!-- ====================== Convenient Synonyms ========================= -->
+
+</project>
diff --git a/connectors/jk/jkstatus/conf/jkstatus-tasks.xml b/connectors/jk/jkstatus/conf/jkstatus-tasks.xml
new file mode 100644
index 0000000..290c949
--- /dev/null
+++ b/connectors/jk/jkstatus/conf/jkstatus-tasks.xml
@@ -0,0 +1,22 @@
+<!--
+  XML file for importing apache jk status ant tasks.
+  <import file="${jkstatus.home}/lib/jkstatus-tasks.xml"/>
+-->
+
+<project name="jkstatus-tasks" >
+  <description>Apache mod_jk ant jkstatus Tasks</description>
+  <!-- set jkstatus.home if it's not already set -->
+  <dirname property="jkstatus.home.bin.dir" file="${ant.file.jkstatus-tasks}"/>
+  <property name="jkstatus.home" value="${jkstatus.home.bin.dir}/.."/>
+  <path id="jkstatus.path">
+      <pathelement location="${jkstatus.home}/bin/commons-logging-api.jar"/>
+      <pathelement location="${jkstatus.home}/lib/catalina-ant.jar"/>
+      <pathelement location="${jkstatus.home}/lib/tomcat-jkstatus-ant.jar"/>
+      <pathelement location="${jkstatus.home}/lib/tomcat-util.jar"/>
+  </path>
+
+  <taskdef resource="org/apache/jk/status/jkstatus.tasks">
+       <classpath refid="jkstatus.path"/>
+  </taskdef>
+
+</project>
\ No newline at end of file
diff --git a/connectors/jk/jkstatus/example/jkstatus.properties.default b/connectors/jk/jkstatus/example/jkstatus.properties.default
new file mode 100644
index 0000000..3114346
--- /dev/null
+++ b/connectors/jk/jkstatus/example/jkstatus.properties.default
@@ -0,0 +1,8 @@
+jkstatus.host=localhost
+jkstatus.port=2090
+jkstatus.username=manager
+jkstatus.password=tomcat
+jkstatus.url=http://${jkstatus.host}:${jkstatus.port}/jkstatus
+jkstatus.testlb=loadbalancer
+jkstatus.testworker=node01
+catalina.home=../../../../build/build
\ No newline at end of file
diff --git a/connectors/jk/jkstatus/example/jkstatus.xml b/connectors/jk/jkstatus/example/jkstatus.xml
new file mode 100644
index 0000000..350e2ce
--- /dev/null
+++ b/connectors/jk/jkstatus/example/jkstatus.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- @author Peter Rossbach -->
+
+<project name="status" 
+         default="status" basedir=".">
+
+    <property name="profile" value=""/>
+    <property file="jkstatus${profile}.properties"/>
+    <property file="jkstatus${profile}.properties.default"/>
+ 
+    <path id="jkstatus.path">
+      <pathelement location="${catalina.home}/bin/commons-logging-api.jar"/>
+      <pathelement location="${catalina.home}/server/lib/catalina-ant.jar"/>
+      <pathelement location="../dist/tomcat-jkstatus-ant.jar"/>
+      <pathelement location="${catalina.home}/server/lib/tomcat-util.jar"/>
+    </path>
+
+    <taskdef resource="org/apache/jk/status/jkstatus.tasks">
+       <classpath refid="jkstatus.path"/>
+    </taskdef>
+
+    <!--
+       ########################################################################################################
+       public
+       ########################################################################################################
+    -->   
+    <target name="status"
+            depends="-status-modjk-access" 
+            description="got jk status" />
+
+    <target name="reset"
+            depends="-status-modjk-reset" 
+            description="reset jk status" />
+    
+   <!--
+       ########################################################################################################
+       private
+       ########################################################################################################
+    -->   
+	  <target name="-status-modjk-access">
+	      <jkStatus url="${jkstatus.url}" 
+	                username="${jkstatus.username}"
+	                password="${jkstatus.password}"
+	                resultproperty="jkstatus.before"
+	      	        echo="on"
+	                failOnError="off"/>
+	      <echoproperties prefix="jkstatus" />           
+ 	      <jkUpdateWorker url="${jkstatus.url}" 
+	               username="${jkstatus.username}"
+	               password="${jkstatus.password}"
+	               loadbalancer="${jkstatus.testlb}"
+	               worker="${jkstatus.testworker}"
+	               domain="d20"
+	               />
+	      <jkUpdateLoadbalancer url="${jkstatus.url}" 
+	               username="${jkstatus.username}"
+	               password="${jkstatus.password}"
+	               loadbalancer="${jkstatus.testlb}"
+	      		   method="Sessions"
+	               />
+	      <jkStatus url="${jkstatus.url}" 
+	                username="${jkstatus.username}"
+	                password="${jkstatus.password}"
+	                resultproperty="workerafter"
+	                failOnError="false"/>
+	      <echoproperties prefix="jkstatus.after" />
+	   </target>        
+
+   <target name="-status-modjk-reset">
+      <jkStatus url="${jkstatus.url}" 
+                username="${jkstatus.username}"
+                password="${jkstatus.password}"
+                resultproperty="jkstatus.before"
+                failOnError="false"/>
+      <echoproperties prefix="jkstatus.before" />   
+        
+      <jkReset  url="${jkstatus.url}" 
+                username="${jkstatus.username}"
+                password="${jkstatus.password}"
+                loadbalancer="${jkstatus.testlb}"
+		        worker="${jkstatus.testworker}"
+       />
+
+       <jkStatus url="${jkstatus.url}" 
+                username="${jkstatus.username}"
+                password="${jkstatus.password}"
+                resultproperty="jkstatus.after"
+                failOnError="false"/>
+      <echoproperties prefix="jkstatus.after" />
+   </target>           
+
+	
+</project>
diff --git a/connectors/jk/jkstatus/example/show.txt b/connectors/jk/jkstatus/example/show.txt
new file mode 100644
index 0000000..c3b9bdb
--- /dev/null
+++ b/connectors/jk/jkstatus/example/show.txt
@@ -0,0 +1,10 @@
+How to use the show example:
+ant -f show.xml
+Configure mod_jk (httpd.conf and workers.properties):
+JkMount /jkstatus jkstatus
+
+worker.list=jkstatus
+worker.jkstatus.type=status
+
+Adjust jkstatus.properties.default:
+jkstatus.port (for example).
diff --git a/connectors/jk/jkstatus/example/show.xml b/connectors/jk/jkstatus/example/show.xml
new file mode 100644
index 0000000..379b765
--- /dev/null
+++ b/connectors/jk/jkstatus/example/show.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project name="modjk-status" 
+         xmlns:jk="urn:org-apache-jk-status"
+         default="status" basedir=".">
+
+	<property name="profile" value=""/>
+	<property file="jkstatus${profile}.properties"/>
+	<property file="jkstatus.properties.default"/>
+
+    <path id="jkstatus.classpath">
+      <pathelement location="${catalina.home}/bin/commons-logging-api.jar"/>
+      <pathelement location="${catalina.home}/server/lib/catalina-ant.jar"/>
+      <!--<pathelement location="${catalina.home}/server/lib/tomcat-jkstatus-ant.jar"/>-->
+      <pathelement location="../dist/tomcat-jkstatus-ant.jar"/>
+      <pathelement location="${catalina.home}/server/lib/tomcat-util.jar"/>
+    </path>
+
+    <typedef resource="org/apache/jk/status/antlib.xml"       
+           uri="urn:org-apache-jk-status" classpathref="jkstatus.classpath"/> 
+           
+    <target name="status" >       
+ 	    <jk:status url="${jkstatus.url}" 
+	                username="${jkstatus.username}"
+	                password="${jkstatus.password}"
+	                resultproperty="worker"
+	      	        echo="off"
+	                failOnError="off"/>
+	    <echoproperties prefix="worker" />
+    </target>
+</project>           
diff --git a/connectors/jk/jkstatus/src/share/org/apache/jk/status/AbstractJkStatusTask.java b/connectors/jk/jkstatus/src/share/org/apache/jk/status/AbstractJkStatusTask.java
new file mode 100644
index 0000000..76b317c
--- /dev/null
+++ b/connectors/jk/jkstatus/src/share/org/apache/jk/status/AbstractJkStatusTask.java
@@ -0,0 +1,209 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.status;
+
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.ProtocolException;
+import java.net.URL;
+import java.net.URLConnection;
+
+import org.apache.catalina.ant.AbstractCatalinaTask;
+import org.apache.catalina.util.Base64;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * Ant task that implements mod_jk 1.2.20 result message string
+ * 
+ * @author Peter Rossbach
+ * @version $Revision:$
+ * @since mod_jk 1.2.20
+ */
+public abstract class AbstractJkStatusTask extends AbstractCatalinaTask {
+
+	/**
+     * Execute the requested operation.
+     * 
+     * @exception BuildException
+     *                if an error occurs
+     */
+    public void execute() throws BuildException {
+
+        super.execute();
+        checkParameter();
+        StringBuffer sb = createLink();
+        execute(sb.toString(), null, null, -1);
+
+    }
+    
+    protected abstract void checkParameter() ;
+    protected abstract StringBuffer createLink() ;
+    	 
+    /**
+     * Execute the specified command, based on the configured properties.
+     * The input stream will be closed upon completion of this task, whether
+     * it was executed successfully or not.
+     *
+     * @param command Command to be executed
+     * @param istream InputStream to include in an HTTP PUT, if any
+     * @param contentType Content type to specify for the input, if any
+     * @param contentLength Content length to specify for the input, if any
+     *
+     * @exception BuildException if an error occurs
+     */
+    public void execute(String command, InputStream istream,
+                        String contentType, int contentLength)
+        throws BuildException {
+
+        InputStreamReader reader = null;
+        try {
+
+            HttpURLConnection hconn = send(command, istream, contentType, contentLength);
+
+            // Process the response message
+            reader = new InputStreamReader(hconn.getInputStream(), "UTF-8");
+            String error = null;
+            error = handleResult(reader, error);
+            if (error != null && isFailOnError()) {
+                // exception should be thrown only if failOnError == true
+                // or error line will be logged twice
+                throw new BuildException(error);
+            }
+        } catch (Throwable t) {
+            if (isFailOnError()) {
+                throw new BuildException(t);
+            } else {
+                handleErrorOutput(t.getMessage());
+            }
+        } finally {
+            closeRedirector();
+            if (reader != null) {
+                try {
+                    reader.close();
+                } catch (Throwable u) {
+                    ;
+                }
+                reader = null;
+            }
+            if (istream != null) {
+                try {
+                    istream.close();
+                } catch (Throwable u) {
+                    ;
+                }
+                istream = null;
+            }
+        }
+
+    }
+
+	private String handleResult(InputStreamReader reader, String error) throws IOException {
+		StringBuffer buff = new StringBuffer();
+		int msgPriority = Project.MSG_INFO;
+		boolean first = true;
+		while (true) {
+		    int ch = reader.read();
+		    if (ch < 0) {
+		        break;
+		    } else if ((ch == '\r') || (ch == '\n')) {
+		        // in Win \r\n would cause handleOutput() to be called
+		        // twice, the second time with an empty string,
+		        // producing blank lines
+		        if (buff.length() > 0) {
+		            String line = buff.toString();
+		            buff.setLength(0);
+		            if (first) {
+		                if (!line.startsWith("Result: type=OK")) {
+		                    error = line;
+		                    msgPriority = Project.MSG_ERR;
+		                }
+		                first = false;
+		            }
+		            handleOutput(line, msgPriority);
+		        }
+		    } else {
+		        buff.append((char) ch);
+		    }
+		}
+		if (buff.length() > 0) {
+		    handleOutput(buff.toString(), msgPriority);
+		}
+		return error;
+	}
+    
+	protected HttpURLConnection send(String command, InputStream istream, String contentType, int contentLength) throws IOException, MalformedURLException, ProtocolException {
+		URLConnection conn;
+		// Create a connection for this command
+		conn = (new URL(url + command)).openConnection();
+		HttpURLConnection hconn = (HttpURLConnection) conn;
+
+		// Set up standard connection characteristics
+		hconn.setAllowUserInteraction(false);
+		hconn.setDoInput(true);
+		hconn.setUseCaches(false);
+		if (istream != null) {
+		    hconn.setDoOutput(true);
+		    hconn.setRequestMethod("PUT");
+		    if (contentType != null) {
+		        hconn.setRequestProperty("Content-Type", contentType);
+		    }
+		    if (contentLength >= 0) {
+		        hconn.setRequestProperty("Content-Length",
+		                                 "" + contentLength);
+		    }
+		} else {
+		    hconn.setDoOutput(false);
+		    hconn.setRequestMethod("GET");
+		}
+		hconn.setRequestProperty("User-Agent",
+		                         "JkStatus-Ant-Task/1.1");
+
+		// Set up an authorization header with our credentials
+		String input = username + ":" + password;
+		String output = new String(Base64.encode(input.getBytes()));
+		hconn.setRequestProperty("Authorization",
+		                         "Basic " + output);
+
+		// Establish the connection with the server
+		hconn.connect();
+        // Send the request data (if any)
+        if (istream != null) {
+            BufferedOutputStream ostream =
+                new BufferedOutputStream(hconn.getOutputStream(), 1024);
+            byte buffer[] = new byte[1024];
+            while (true) {
+                int n = istream.read(buffer);
+                if (n < 0) {
+                    break;
+                }
+                ostream.write(buffer, 0, n);
+            }
+            ostream.flush();
+            ostream.close();
+            istream.close();
+        }
+		return hconn;
+	}
+
+
+}
diff --git a/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkBalancer.java b/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkBalancer.java
new file mode 100644
index 0000000..0a43f1e
--- /dev/null
+++ b/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkBalancer.java
@@ -0,0 +1,335 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.status;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ * @see org.apache.jk.status.JkStatusParser
+ */
+public class JkBalancer implements Serializable {
+
+    int id =-1;
+    String name ;
+    String type ;
+    boolean sticky ;
+    boolean stickyforce;
+    int retries ;
+    int recover ;
+    String method ;
+    String lock ;
+    int good = -1 ;
+    int degraded = -1;
+    int bad = -1 ;
+    int busy = -1;
+    int max_busy = -1 ;
+    int member_count = -1 ;
+    int map_count = -1 ;
+    int time_to_maintenance_min = -1 ;
+    int time_to_maintenance_max = -1 ;
+       
+    List members = new ArrayList() ;
+    List mappings = new ArrayList() ;
+     
+    /**
+     * @return Returns the id.
+     */
+    public int getId() {
+        return id;
+    }
+    /**
+     * @param id The id to set.
+     */
+    public void setId(int id) {
+        this.id = id;
+    }
+    /**
+     * @return Returns the mappings.
+     */
+    public List getBalancerMappings() {
+        return mappings;
+    }
+    /**
+     * @param mappings The mappings to set.
+     */
+    public void setBalancerMappings(List mappings) {
+        this.mappings = mappings;
+    }
+    public void addBalancerMapping(JkBalancerMapping mapping) {
+        mappings.add(mapping);
+    }
+    public void removeBalancerMapping(JkBalancerMapping mapping) {
+        mappings.remove(mapping);
+    }
+    /**
+     * @return Returns the members.
+     */
+    public List getBalancerMembers() {
+        return members;
+    }
+    /**
+     * @param members The members to set.
+     */
+    public void setBalancerMembers(List members) {
+        this.members = members;
+    }
+    public void addBalancerMember(JkBalancerMember member) {
+        members.add(member);
+    }   
+    public void removeBalancerMember(JkBalancerMember member) {
+        members.remove(member);
+    }   
+    /**
+     * @return Returns the name.
+     */
+    public String getName() {
+        return name;
+    }
+    /**
+     * @param name The name to set.
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+    /**
+     * @return Returns the recover.
+     */
+    public int getRecover_time() {
+        return recover;
+    }
+    /**
+     * @param recover The recover to set.
+     */
+    public void setRecover_time(int recover) {
+        this.recover = recover;
+    }
+    /**
+     * @return Returns the retries.
+     */
+    public int getRetries() {
+        return retries;
+    }
+    /**
+     * @param retries The retries to set.
+     */
+    public void setRetries(int retries) {
+        this.retries = retries;
+    }
+    /**
+     * @return Returns the sticky.
+     */
+    public boolean isSticky_session() {
+        return sticky;
+    }
+    /**
+     * @param sticky The sticky to set.
+     */
+    public void setSticky_session(boolean sticky) {
+        this.sticky = sticky;
+    }
+    /**
+     * @return Returns the stickyforce.
+     */
+    public boolean isSticky_session_force() {
+        return stickyforce;
+    }
+    /**
+     * @param stickyforce The stickyforce to set.
+     */
+    public void setSticky_session_force(boolean stickyforce) {
+        this.stickyforce = stickyforce;
+    }
+    /**
+     * @return Returns the type.
+     */
+    public String getType() {
+        return type;
+    }
+    /**
+     * @param type The type to set.
+     */
+    public void setType(String type) {
+        this.type = type;
+    }
+    /**
+     * @return the bad
+     * @since mod_jk 1.2.20
+     */
+    public int getBad() {
+        return bad;
+    }
+    /**
+     * @param bad the bad to set
+     * @since mod_jk 1.2.20
+     */
+    public void setBad(int bad) {
+        this.bad = bad;
+    }
+    /**
+     * @return the busy
+     * @since mod_jk 1.2.20
+     */
+    public int getBusy() {
+        return busy;
+    }
+    /**
+     * @param busy the busy to set
+     * @since mod_jk 1.2.20
+     */
+    public void setBusy(int busy) {
+        this.busy = busy;
+    }
+    /**
+     * @return the degraded
+     * @since mod_jk 1.2.20
+     */
+    public int getDegraded() {
+        return degraded;
+    }
+    /**
+     * @param degraded the degraded to set
+     * @since mod_jk 1.2.20
+     */
+    public void setDegraded(int degraded) {
+        this.degraded = degraded;
+    }
+    /**
+     * @return the good
+     * @since mod_jk 1.2.20
+     */
+    public int getGood() {
+        return good;
+    }
+    /**
+     * @param good the good to set
+     * @since mod_jk 1.2.20
+     */
+    public void setGood(int good) {
+        this.good = good;
+    }
+    /**
+     * @return the lock
+     * @since mod_jk 1.2.20
+     */
+    public String getLock() {
+        return lock;
+    }
+    /**
+     * @param lock the lock to set
+     * @since mod_jk 1.2.20
+     */
+    public void setLock(String lock) {
+        this.lock = lock;
+    }
+    /**
+     * @return the max_busy
+     * @since mod_jk 1.2.20
+     */
+    public int getMax_busy() {
+        return max_busy;
+    }
+    /**
+     * @param max_busy the max_busy to set
+     * @since mod_jk 1.2.20
+     */
+    public void setMax_busy(int max_busy) {
+        this.max_busy = max_busy;
+    }
+    /**
+     * @return the method
+     * @since mod_jk 1.2.20
+     */
+    public String getMethod() {
+        return method;
+    }
+    /**
+     * @param method the method to set
+     * @since mod_jk 1.2.20
+     */
+    public void setMethod(String method) {
+        this.method = method;
+    }
+    
+    /**
+     * @return the member_count
+     * @since mod_jk 1.2.20
+     */
+    public int getMember_count() {
+        return member_count;
+    }
+    
+    /**
+     * @param member_count the member_count to set
+     * @since mod_jk 1.2.20
+     */
+    public void setMember_count(int member_count) {
+        this.member_count = member_count;
+    }
+    
+    /**
+     * @return the map_count
+     * @since mod_jk 1.2.20
+     */
+    public int getMap_count() {
+        return map_count;
+    }
+
+    /**
+     * @param map_count the map_count to set
+     * @since mod_jk 1.2.20
+     */
+    public void setMap_count(int map_count) {
+        this.map_count = map_count;
+    }
+    
+    /**
+     * @return the time_to_maintenance_min
+     * @since mod_jk 1.2.21
+     */
+    public int getTime_to_maintenance_min() {
+        return time_to_maintenance_min;
+    }
+
+    /**
+     * @param time_to_maintenance_min the time_to_maintenance_min to set
+     * @since mod_jk 1.2.21
+     */
+    public void setTime_to_maintenance_min(int time_to_maintenance_min) {
+        this.time_to_maintenance_min = time_to_maintenance_min;
+    }
+    
+    /**
+     * @return the time_to_maintenance_max
+     * @since mod_jk 1.2.21
+     */
+    public int getTime_to_maintenance_max() {
+        return time_to_maintenance_max;
+    }
+
+    /**
+     * @param time_to_maintenance_max the time_to_maintenance_max to set
+     * @since mod_jk 1.2.21
+     */
+    public void setTime_to_maintenance_max(int time_to_maintenance_max) {
+        this.time_to_maintenance_max = time_to_maintenance_max;
+    }
+    
+}
diff --git a/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkBalancerMapping.java b/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkBalancerMapping.java
new file mode 100644
index 0000000..bba59b3
--- /dev/null
+++ b/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkBalancerMapping.java
@@ -0,0 +1,98 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.status;
+
+import java.io.Serializable;
+
+/**
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ * @see org.apache.jk.status.JkStatusParser
+ */
+public class JkBalancerMapping implements Serializable {
+    int id =-1 ;
+	String type ;
+    String uri;
+    String context ;
+    String source ;
+    
+    /**
+	 * @return the id
+	 */
+	public int getId() {
+		return id;
+	}
+	/**
+	 * @param id the id to set
+	 */
+	public void setId(int id) {
+		this.id = id;
+	}
+	/**
+     * @return Returns the context.
+     * @deprecated mod_jk 1.2.20
+     */
+    public String getContext() {
+        return context;
+    }
+    /**
+     * @param context The context to set.
+     * @deprecated mod_jk 1.2.20
+     */
+    public void setContext(String context) {
+        this.context = context;
+    }
+    /**
+     * @return Returns the type.
+     */
+    public String getType() {
+        return type;
+    }
+    /**
+     * @param type The type to set.
+     */
+    public void setType(String type) {
+        this.type = type;
+    }
+    /**
+     * @return Returns the uri.
+     */
+    public String getUri() {
+        return uri;
+    }
+    /**
+     * @param uri The uri to set.
+     */
+    public void setUri(String uri) {
+        this.uri = uri;
+    }
+	/**
+	 * @return the source
+     * @since mod_jk 1.2.20
+ 	 */
+	public String getSource() {
+		return source;
+	}
+	/**
+	 * @param source the source to set
+	 * @since mod_jk 1.2.20
+     */
+	public void setSource(String source) {
+		this.source = source;
+	}
+    
+ }
diff --git a/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkBalancerMember.java b/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkBalancerMember.java
new file mode 100644
index 0000000..2eb8fd5
--- /dev/null
+++ b/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkBalancerMember.java
@@ -0,0 +1,516 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.status;
+
+import java.io.Serializable;
+
+/**
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ * @see org.apache.jk.status.JkStatusParser
+ */
+/**
+ * @author peter
+ *
+ */
+public class JkBalancerMember implements Serializable {
+    
+    int id = -1;
+
+    String name;
+
+    /* possible with >= 1.2.16 */
+    String jvm_route;
+
+    /* possible with >= 1.2.20 */
+    String route;
+
+    String type;
+
+    String host;
+
+    int port;
+
+    String address;
+
+    /* deprecated with mod_jk 1.2.16*/
+    String status;
+    
+    /* possible with > 1.2.16 */
+    String activation; 
+
+    /* possible with > 1.2.16 */
+    String state; 
+        
+    int lbfactor;
+
+    long lbvalue;
+
+    /* possible with > 1.2.16 */
+    long lbmult = -1 ;
+    
+    int elected;
+
+    long readed;
+
+    long transferred;
+
+    long errors;
+
+    long clienterrors = -1;
+    
+    int busy;
+    
+    /* possible with > 1.2.16 */
+    int maxbusy = -1;
+    
+    String redirect;
+    
+    String domain;
+    
+    /* possible with > 1.2.16 */
+    int distance = -1;
+
+    /* possible with > 1.2.20 */
+    int time_to_recover = -1 ;
+    
+    /* possible with > 1.2.21 */
+    int time_to_recover_max = -1 ;
+    
+    /* possible with > 1.2.21 */
+    int time_to_recover_min = -1 ;
+    
+    /**
+     * @return Returns the jvm_route.
+     * @since mod_jk 1.2.16
+     * @deprecated
+     */
+    public String getJvm_route() {
+        return jvm_route;
+    }
+
+    /**
+     * @param jvm_route The jvm_route to set.
+     * @since mod_jk 1.2.16
+     * @deprecated
+     */
+    public void setJvm_route(String jvm_route) {
+        this.jvm_route = jvm_route;
+    }
+
+    /**
+     * @return the route
+    * @since mod_jk 1.2.20
+     */
+    public String getRoute() {
+        return route;
+    }
+
+    /**
+     * @param route the route to set
+    * @since mod_jk 1.2.20
+     */
+    public void setRoute(String route) {
+        this.route = route;
+    }
+
+    /**
+     * @return Returns the address.
+     */
+    public String getAddress() {
+        return address;
+    }
+
+    /**
+     * @param address
+     *            The address to set.
+     */
+    public void setAddress(String address) {
+        this.address = address;
+    }
+
+    /**
+     * @return Returns the busy.
+     */
+    public int getBusy() {
+        return busy;
+    }
+
+    /**
+     * @param busy
+     *            The busy to set.
+     */
+    public void setBusy(int busy) {
+        this.busy = busy;
+    }
+
+
+    /**
+     * @return Returns the maxbusy.
+     * @since mod_jk 1.2.18
+     */
+    public int getMax_busy() {
+        return maxbusy;
+    }
+
+    /**
+     * @param maxbusy The maxbusy to set.
+     * @since mod_jk 1.2.18
+     */
+    public void setMax_busy(int maxbusy) {
+        this.maxbusy = maxbusy;
+    }
+
+    /**
+     * @return Returns the elected.
+     */
+    public int getElected() {
+        return elected;
+    }
+
+    /**
+     * @param elected
+     *            The elected to set.
+     */
+    public void setElected(int elected) {
+        this.elected = elected;
+    }
+
+    /**
+     * @return Returns the clienterrors.
+     * @since mod_jk 1.2.19
+     */
+    public long getClient_errors() {
+        return clienterrors;
+    }
+
+    /**
+     * @param clienterrors The clienterrors to set.
+     * @since mod_jk 1.2.19
+     */
+    public void setClient_errors(long clienterrors) {
+        this.clienterrors = clienterrors;
+    }
+
+    /**
+     * @return Returns the errors.
+     */
+    public long getErrors() {
+        return errors;
+    }
+
+    /**
+     * @param errors
+     *            The errors to set.
+     */
+    public void setErrors(long errors) {
+        this.errors = errors;
+    }
+
+    /**
+     * @return Returns the host.
+     */
+    public String getHost() {
+        return host;
+    }
+
+    /**
+     * @param host
+     *            The host to set.
+     */
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    /**
+     * @return Returns the id.
+     */
+    public int getId() {
+        return id;
+    }
+
+    /**
+     * @param id
+     *            The id to set.
+     */
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    /**
+     * @return Returns the lbfactor.
+     */
+    public int getLbfactor() {
+        return lbfactor;
+    }
+
+    /**
+     * @param lbfactor
+     *            The lbfactor to set.
+     */
+    public void setLbfactor(int lbfactor) {
+        this.lbfactor = lbfactor;
+    }
+
+    /**
+     * @return Returns the lbvalue.
+     */
+    public long getLbvalue() {
+        return lbvalue;
+    }
+
+    /**
+     * @param lbvalue
+     *            The lbvalue to set.
+     */
+    public void setLbvalue(long lbvalue) {
+        this.lbvalue = lbvalue;
+    }
+    
+    /**
+     * @return Returns the lbmult.
+     * @since mod_jk 1.2.19
+     */
+    public long getLbmult() {
+        return lbmult;
+    }
+
+    /**
+     * @param lbmult The lbmult to set.
+     * @since mod_jk 1.2.19
+     */
+    public void setLbmult(long lbmult) {
+        this.lbmult = lbmult;
+    }
+
+    /**
+     * @return Returns the name.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * @param name
+     *            The name to set.
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+
+    /**
+     * @return Returns the port.
+     */
+    public int getPort() {
+        return port;
+    }
+
+    /**
+     * @param port
+     *            The port to set.
+     */
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    /**
+     * @return Returns the readed.
+     */
+    public long getReaded() {
+        return readed;
+    }
+
+    /**
+     * @param readed
+     *            The readed to set.
+     */
+    public void setReaded(long readed) {
+        this.readed = readed;
+    }
+
+    /**
+     * @return Returns the status.
+     * @deprecated since 1.2.16
+     */
+    public String getStatus() {
+        return status;
+    }
+
+    /**
+     * @param status
+     *            The status to set.
+     * @deprecated since 1.2.16
+     */
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    /**
+     * @return Returns the activation.
+     * @since mod_jk 1.2.19
+     */
+    public String getActivation() {
+        return activation;
+    }
+
+    /**
+     * @param activation The activation to set.
+     * @since mod_jk 1.2.19
+     */
+    public void setActivation(String activation) {
+        this.activation = activation;
+    }
+
+    /**
+     * @return Returns the state.
+     * @since mod_jk 1.2.19
+     */
+    public String getState() {
+        return state;
+    }
+
+    /**
+     * @param state The state to set.
+     * @since mod_jk 1.2.19
+     */
+    public void setState(String state) {
+        this.state = state;
+    }
+
+    /**
+     * @return Returns the transferred.
+     */
+    public long getTransferred() {
+        return transferred;
+    }
+
+    /**
+     * @param transferred
+     *            The transferred to set.
+     */
+    public void setTransferred(long transferred) {
+        this.transferred = transferred;
+    }
+
+    /**
+     * @return Returns the type.
+     */
+    public String getType() {
+        return type;
+    }
+
+    /**
+     * @param type
+     *            The type to set.
+     */
+    public void setType(String type) {
+        this.type = type;
+    }
+    
+    
+    /**
+     * @return Returns the domain.
+     */
+    public String getDomain() {
+        return domain;
+    }
+    /**
+     * @param domain The domain to set.
+     */
+    public void setDomain(String domain) {
+        this.domain = domain;
+    }
+ 
+    /**
+     * @return Returns the redirect.
+     */
+    public String getRedirect() {
+        return redirect;
+    }
+    /**
+     * @param redirect The redirect to set.
+     */
+    public void setRedirect(String redirect) {
+        this.redirect = redirect;
+    }
+
+    /**
+     * @return Returns the distance.
+     * @since mod_jk 1.2.18
+     */
+    public int getDistance() {
+        return distance;
+    }
+
+    /**
+     * @param distance The distance to set.
+     * @since mod_jk 1.2.18
+     */
+    public void setDistance(int distance) {
+        this.distance = distance;
+    }
+
+    /**
+     * @return the time_to_recover
+     * @since mod_jk 1.2.20
+     */
+    public int getTime_to_recover() {
+        return time_to_recover;
+    }
+
+    /**
+     * @param time_to_recover the time_to_recover to set
+     * @since mod_jk 1.2.20
+     */
+    public void setTime_to_recover(int time_to_recover) {
+        this.time_to_recover = time_to_recover;
+    }
+
+    /**
+     * @return the time_to_recover_min
+     * @since mod_jk 1.2.21
+     */
+    public int getTime_to_recover_min() {
+        return time_to_recover_min;
+    }
+
+    /**
+     * @param time_to_recover_min the time_to_recover_min to set
+     * @since mod_jk 1.2.21
+     */
+    public void setTime_to_recover_min(int time_to_recover_min) {
+        this.time_to_recover_min = time_to_recover_min;
+    }
+
+    /**
+     * @return the time_to_recover_max
+     * @since mod_jk 1.2.21
+     */
+    public int getTime_to_recover_max() {
+        return time_to_recover_max;
+    }
+
+    /**
+     * @param time_to_recover_max the time_to_recover_max to set
+     * @since mod_jk 1.2.21
+     */
+    public void setTime_to_recover_max(int time_to_recover_max) {
+        this.time_to_recover_max = time_to_recover_max;
+    }
+
+}
diff --git a/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkResult.java b/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkResult.java
new file mode 100644
index 0000000..d986ca0
--- /dev/null
+++ b/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkResult.java
@@ -0,0 +1,54 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.status;
+
+import java.io.Serializable;
+
+/**
+ * @author Peter Rossbach
+ * @version $Revision:$ $Date:$
+ * @see org.apache.jk.status.JkStatusParser
+ */
+public class JkResult implements Serializable {
+    String type ;
+    String message;
+	/**
+	 * @return the message
+	 */
+	public String getMessage() {
+		return message;
+	}
+	/**
+	 * @param message the message to set
+	 */
+	public void setMessage(String message) {
+		this.message = message;
+	}
+	/**
+	 * @return the type
+	 */
+	public String getType() {
+		return type;
+	}
+	/**
+	 * @param type the type to set
+	 */
+	public void setType(String type) {
+		this.type = type;
+	}  
+     
+}
diff --git a/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkServer.java b/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkServer.java
new file mode 100644
index 0000000..66c0356
--- /dev/null
+++ b/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkServer.java
@@ -0,0 +1,56 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.status;
+
+import java.io.Serializable;
+
+/**
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ * @see org.apache.jk.status.JkStatusParser
+ */
+public class JkServer implements Serializable {
+    String name ;
+    String port;  
+     
+    /**
+     * @return Returns the name.
+     */
+    public String getName() {
+        return name;
+    }
+    /**
+     * @param name The name to set.
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+    /**
+     * @return Returns the port.
+     */
+    public String getPort() {
+        return port;
+    }
+    /**
+     * @param port The port to set.
+     */
+    public void setPort(String port) {
+        this.port = port;
+    }
+
+ 
+}
diff --git a/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkSoftware.java b/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkSoftware.java
new file mode 100644
index 0000000..e3e5580
--- /dev/null
+++ b/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkSoftware.java
@@ -0,0 +1,54 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.status;
+
+import java.io.Serializable;
+
+/**
+ * @author Peter Rossbach
+ * @version $Revision:$ $Date:$
+ * @see org.apache.jk.status.JkStatusParser
+ */
+public class JkSoftware implements Serializable {
+    String web_server;
+    String jk_version ;
+    
+     /**
+     * @return Returns the software.
+     */
+    public String getWeb_server() {
+        return web_server;
+    }
+    /**
+     * @param software The software to set.
+     */
+    public void setWeb_server(String software) {
+        this.web_server = software;
+    }
+    /**
+     * @return Returns the version.
+     */
+    public String getJk_version() {
+        return jk_version;
+    }
+    /**
+     * @param version The version to set.
+     */
+    public void setJk_version(String version) {
+        this.jk_version = version;
+    }
+}
diff --git a/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkStatus.java b/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkStatus.java
new file mode 100644
index 0000000..08e1591
--- /dev/null
+++ b/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkStatus.java
@@ -0,0 +1,90 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.status;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ * @see org.apache.jk.status.JkStatusParser
+ */
+public class JkStatus implements Serializable {
+
+    JkServer server ;
+    JkSoftware software ;
+    JkResult result ;
+    List balancers = new ArrayList() ;
+   
+    /**
+     * @return Returns the balancers.
+     */
+    public List getBalancers() {
+        return balancers;
+    }
+    /**
+     * @param balancers The balancers to set.
+     */
+    public void setBalancers(List balancers) {
+        this.balancers = balancers;
+    }
+    
+    public void addBalancer(JkBalancer balancer) {
+      balancers.add(balancer);
+    }
+    
+    public void removeBalancer(JkBalancer balancer) {
+      balancers.remove(balancer);
+    }
+
+    /**
+     * @return Returns the server.
+     */
+    public JkServer getServer() {
+        return server;
+    }
+    public void setServer(JkServer server) {
+       this.server = server ;
+    }
+	/**
+	 * @return the result
+	 */
+	public JkResult getResult() {
+		return result;
+	}
+	/**
+	 * @param result the result to set
+	 */
+	public void setResult(JkResult result) {
+		this.result = result;
+	}
+    
+    /**
+     * @return Returns the software.
+     */
+    public JkSoftware getSoftware() {
+        return software;
+    }
+    /**
+     * @param software The software to set.
+     */
+    public void setSoftware(JkSoftware software) {
+        this.software = software;
+    }
+}
diff --git a/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkStatusAccessor.java b/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkStatusAccessor.java
new file mode 100644
index 0000000..d2eb3db
--- /dev/null
+++ b/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkStatusAccessor.java
@@ -0,0 +1,133 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.status;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.ProtocolException;
+import java.net.URL;
+import java.net.URLConnection;
+
+import org.apache.catalina.util.Base64;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.tomcat.util.digester.Digester;
+
+/**
+ * Create connection to mod_jk jkstatus page.
+ * Optional you can use Http basic auth user and password.
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ * @see org.apache.jk.status.JkStatusParser
+ * @since 5.5.10
+ */
+public class JkStatusAccessor {
+    
+    private static Log log = LogFactory.getLog(JkStatusAccessor.class);
+    /**
+     * The descriptive information about this implementation.
+     */
+    protected static final String info = "org.apache.jk.status.JkStatusAccessor/1.0";
+
+    /**
+     * Parse Apache mod_jk Status  from base url http://host:port/jkstatus)
+     * @param url
+     * @param username
+     * @param password
+     *  
+     */
+    public JkStatus status(String url, String username, String password)
+            throws Exception {
+
+        if(url == null || "".equals(url))
+            return null ;
+        HttpURLConnection hconn = null;
+        JkStatus status = null;
+
+        try {
+        	// FIXME: use cmd show for older mod_jk versions
+            hconn = openConnection(url + "?cmd=list&mime=xml", username, password);
+            Digester digester = JkStatusParser.getDigester();
+            synchronized (digester) {
+                status = (JkStatus) digester.parse(hconn.getInputStream());
+            }
+        } catch (Throwable t) {
+            throw new Exception(t);
+        } finally {
+            if (hconn != null) {
+                try {
+                    hconn.disconnect();
+                } catch (Throwable u) {
+                    ;
+                }
+                hconn = null;
+            }
+        }
+        return status;
+    }
+
+    /**
+     * Create a auth http connection for this url
+     * 
+     * @param url
+     * @param username
+     * @param password
+     * @return HttpConnection
+     * @throws IOException
+     * @throws MalformedURLException
+     * @throws ProtocolException
+     */
+    protected HttpURLConnection openConnection(String url, String username,
+            String password) throws IOException, MalformedURLException,
+            ProtocolException {
+        URLConnection conn;
+        conn = (new URL(url)).openConnection();
+        HttpURLConnection hconn = (HttpURLConnection) conn;
+
+        // Set up standard connection characteristics
+        hconn.setAllowUserInteraction(false);
+        hconn.setDoInput(true);
+        hconn.setUseCaches(false);
+        hconn.setDoOutput(false);
+        hconn.setRequestMethod("GET");
+        hconn.setRequestProperty("User-Agent", "JkStatus-Client/1.0");
+
+        if(username != null && password != null ) {
+             setAuthHeader(hconn, username, password);
+        }
+        // Establish the connection with the server
+        hconn.connect();
+        return hconn;
+    }
+
+    /**
+     * Set Basic Auth Header
+     * 
+     * @param hconn
+     * @param username
+     * @param password
+     */
+    protected void setAuthHeader(HttpURLConnection hconn, String username,
+            String password) {
+        // Set up an authorization header with our credentials
+        String input = username + ":" + password;
+        String output = new String(Base64.encode(input.getBytes()));
+        hconn.setRequestProperty("Authorization", "Basic " + output);
+    }
+
+}
\ No newline at end of file
diff --git a/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkStatusParser.java b/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkStatusParser.java
new file mode 100644
index 0000000..942b5aa
--- /dev/null
+++ b/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkStatusParser.java
@@ -0,0 +1,223 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.status;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.tomcat.util.digester.Digester;
+
+/**
+ * mod_jk 1.2.19 document:<br/>
+ * <code>
+ *
+ *  &lt;?xml version="1.0" encoding="UTF-8" ?&gt;
+ *  &lt;jk:status xmlns:jk="http://tomcat.apache.org"&gt;
+ *    &lt;jk:server name="localhost" port="2010" software="Apache/2.0.58 (Unix) mod_jk/1.2.19-dev" version="1.2.19" /&gt;
+ *    &lt;jk:balancers&gt;
+ *    &lt;jk:balancer id="0" name="loadbalancer" type="lb" sticky="True" stickyforce="False" retries="2" recover="60" &gt;
+ *        &lt;jk:member id="0" name="node01" type="ajp13" host="localhost" port="20012" address="127.0.0.1:20012" activation="ACT" state="N/A" distance="0" lbfactor="1" lbmult="1" lbvalue="0" elected="0" errors="0" transferred="0" readed="0" busy="0" maxbusy="0" jvm_route="node01" /&gt;
+ *        &lt;jk:member id="1" name="node02" type="ajp13" host="localhost" port="20022" address="127.0.0.1:20022" activation="ACT" state="N/A" distance="0" lbfactor="1" lbmult="1" lbvalue="0" elected="0" errors="0" transferred="0" readed="0" busy="0" maxbusy="0" jvm_route="node02" /&gt;
+ *      &lt;jk:map type="Wildchar" uri="/ClusterSession*" context="/ClusterSession*" /&gt;
+ *      &lt;jk:map type="Wildchar" uri="/ClusterTest*" context="/ClusterTest*" /&gt;
+ *      &lt;jk:map type="Wildchar" uri="/test*" context="/test*" /&gt;
+ *    &lt;/jk:balancer&gt;
+ *    &lt;/jk:balancers&gt;
+ *  &lt;/jk:status&gt;
+ * </code>
+ * <br/>
+ * mod_jk 1.2.20 document:<br/>
+ * <code>
+ * &lt;?xml version="1.0" encoding="UTF-8" ?&gt;
+ * &lt;jk:status xmlns:jk="http://tomcat.apache.org"&gt;
+ * &lt;jk:server
+ *  name="127.0.0.1"
+ *  port="2080"
+ *  software="Apache/2.0.59 (Unix) mod_jk/1.2.20-dev"
+ *  version="1.2.20"/&gt;
+ * &lt;jk:balancers&gt;
+ *   &lt;jk:balancer
+ *    name="loadbalancer"
+ *    type="lb"
+ *    sticky="True"
+ *    stickyforce="False"
+ *    retries="2"
+ *    recover="60"
+ *    method="Request"
+ *    lock="Optimistic"
+ *    good="2"
+ *    degraded="0"
+ *    bad="0"
+ *    busy="0"
+ *    max_busy="0"&gt;
+ *      &lt;jk:member
+ *        name="node01"
+ *        type="ajp13"
+ *        host="localhost"
+ *        port="7309"
+ *        address="127.0.0.1:7309"
+ *        activation="ACT"
+ *        lbfactor="1"
+ *        jvm_route="node01"
+ *        redirect=""
+ *        domain=""
+ *        distance="0"
+ *        state="N/A"
+ *        lbmult="1"
+ *        lbvalue="0"
+ *        elected="0"
+ *        errors="0"
+ *        clienterrors="0"
+ *        transferred="0"
+ *        readed="0"
+ *        busy="0"
+ *        maxbusy="0"
+ *        time-to-recover="0"/&gt;
+ *      &lt;jk:member
+ *        name="node02"
+ *        type="ajp13"
+ *        host="localhost"
+ *        port="7409"
+ *        address="127.0.0.1:7409"
+ *        activation="ACT"
+ *        lbfactor="1"
+ *        jvm_route="node02"
+ *        redirect=""
+ *        domain=""
+ *        distance="0"
+ *        state="N/A"
+ *        lbmult="1"
+ *        lbvalue="0"
+ *        elected="0"
+ *        errors="0"
+ *        clienterrors="0"
+ *        transferred="0"
+ *        readed="0"
+ *        busy="0"
+ *        maxbusy="0"
+ *        time-to-recover="0"/&gt;
+ *      &lt;jk:map
+ *        type="Wildchar"
+ *        uri="/ClusterTest*"
+ *        source="JkMount"/&gt;
+ *      &lt;jk:map
+ *        type="Wildchar"
+ *        uri="/myapps*"
+ *        source="JkMount"/&gt;
+ *      &lt;jk:map
+ *        type="Wildchar"
+ *        uri="/last*"
+ *        source="JkMount"/&gt;
+ *  &lt;/jk:balancer&gt;
+ * &lt;/jk:balancers&gt;
+ * &lt;/jk:status&gt;
+ *
+ *
+ * </code>
+ * @author Peter Rossbach
+ * @version $Revision$ $Date$
+ * @since 5.5.10
+ */
+public class JkStatusParser {
+    private static Log log = LogFactory.getLog(JkStatusParser.class);
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    private static final String info = "org.apache.jk.status.JkStatusParser/1.1";
+
+    /**
+     * Return descriptive information about this implementation and the
+     * corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+    /**
+     * The <code>Digester</code> instance used to parse registry descriptors.
+     */
+    public static Digester digester = createDigester();
+
+    public static Digester getDigester() {
+        return digester;
+    }
+
+    /**
+     * Create and configure the Digester we will be using for setup mod_jk jk status page.
+     */
+    public static Digester createDigester() {
+        long t1 = System.currentTimeMillis();
+        // Initialize the digester
+        Digester digester = new Digester();
+        digester.setValidating(false);
+        digester.setClassLoader(JkStatus.class.getClassLoader());
+
+        // parse status
+        digester.addObjectCreate("jk:status", "org.apache.jk.status.JkStatus",
+                "className");
+        digester.addSetProperties("jk:status");
+
+        digester.addObjectCreate("jk:status/jk:server",
+                "org.apache.jk.status.JkServer", "className");
+        digester.addSetProperties("jk:status/jk:server");
+        digester.addSetNext("jk:status/jk:server", "setServer",
+                "org.apache.jk.status.JkServer");
+
+        digester.addObjectCreate("jk:status/jk:software",
+                "org.apache.jk.status.JkSoftware", "className");
+        digester.addSetProperties("jk:status/jk:software");
+        digester.addSetNext("jk:status/jk:software", "setSoftware",
+                "org.apache.jk.status.JkSoftware");
+
+        digester.addObjectCreate("jk:status/jk:result",
+                "org.apache.jk.status.JkResult", "className");
+        digester.addSetProperties("jk:status/jk:result");
+        digester.addSetNext("jk:status/jk:result", "setResult",
+                "org.apache.jk.status.JkResult");
+
+        digester.addObjectCreate("jk:status/jk:balancers/jk:balancer",
+                "org.apache.jk.status.JkBalancer", "className");
+        digester.addSetProperties("jk:status/jk:balancers/jk:balancer");
+        digester.addSetNext("jk:status/jk:balancers/jk:balancer",
+                "addBalancer", "org.apache.jk.status.JkBalancer");
+
+        digester.addObjectCreate(
+                "jk:status/jk:balancers/jk:balancer/jk:member",
+                "org.apache.jk.status.JkBalancerMember", "className");
+        digester
+                .addSetProperties("jk:status/jk:balancers/jk:balancer/jk:member");
+        digester.addSetNext("jk:status/jk:balancers/jk:balancer/jk:member",
+                "addBalancerMember", "org.apache.jk.status.JkBalancerMember");
+
+        digester.addObjectCreate("jk:status/jk:balancers/jk:balancer/jk:map",
+                "org.apache.jk.status.JkBalancerMapping", "className");
+        digester.addSetProperties("jk:status/jk:balancers/jk:balancer/jk:map");
+        digester.addSetNext("jk:status/jk:balancers/jk:balancer/jk:map",
+                "addBalancerMapping", "org.apache.jk.status.JkBalancerMapping");
+
+        long t2 = System.currentTimeMillis();
+        if (log.isDebugEnabled())
+            log.debug("Digester for apache mod_jk jkstatus page is created "
+                    + (t2 - t1));
+        return (digester);
+
+    }
+
+}
diff --git a/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkStatusResetTask.java b/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkStatusResetTask.java
new file mode 100644
index 0000000..6ac0ee4
--- /dev/null
+++ b/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkStatusResetTask.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.status;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Ant task that implements the <code>/jkstatus?cmd=reset&amp;w=loadbalancer</code> command, supported by the
+ * mod_jk status (1.2.20) application.
+ * 
+ * @author Peter Rossbach
+ * @version $Revision$
+ * @since mod_jk 1.2.20
+ */
+public class JkStatusResetTask extends AbstractJkStatusTask {
+
+	/**
+     * The descriptive information about this implementation.
+     */
+    private static final String info = "org.apache.jk.status.JkStatusResetTask/1.1";
+
+    private String worker;
+    private String loadbalancer;
+
+    /**
+     *  
+     */
+    public JkStatusResetTask() {
+        super();
+        setUrl("http://localhost/jkstatus");
+    }
+
+    /**
+     * Return descriptive information about this implementation and the
+     * corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+    
+    /**
+	 * @return the loadbalancer
+	 */
+	public String getLoadbalancer() {
+		return loadbalancer;
+	}
+
+
+	/**
+	 * @param loadbalancer the loadbalancer to set
+	 */
+	public void setLoadbalancer(String loadbalancer) {
+		this.loadbalancer = loadbalancer;
+	}
+
+
+	/**
+	 * @return the worker
+	 */
+	public String getWorker() {
+		return worker;
+	}
+
+
+	/**
+	 * @param worker the worker to set
+	 */
+	public void setWorker(String worker) {
+		this.worker = worker;
+	}
+
+
+	/**
+     * Create jkstatus reset link
+     * <ul>
+     * <li><b>loadbalancer example:
+     * </b>http://localhost/jkstatus?cmd=reset&mime=txt&w=loadbalancer</li>
+     * <li><b>loadbalancer + sub worker example:
+     * </b>http://localhost/jkstatus?cmd=reset&mime=txt&w=loadbalancer&sw=node01</li>
+     * </ul>
+     * 
+     * @return create jkstatus reset link
+     */
+    protected StringBuffer createLink() {
+        // Building URL
+        StringBuffer sb = new StringBuffer();
+        try {
+            sb.append("?cmd=reset");
+            sb.append("&mime=txt");
+            sb.append("&w=");
+            sb.append(URLEncoder.encode(loadbalancer, getCharset()));
+            if(worker != null) {
+                sb.append("&sw=");
+            	sb.append(URLEncoder.encode(worker, getCharset()));        	
+        	}
+
+        } catch (UnsupportedEncodingException e) {
+            throw new BuildException("Invalid 'charset' attribute: "
+                    + getCharset());
+        }
+        return sb;
+    }
+
+    /**
+     * check correct pararmeter
+     */
+    protected void checkParameter() {
+        if (loadbalancer == null) {
+            throw new BuildException("Must specify 'loadbalanacer' attribute");
+        }
+    }
+}
\ No newline at end of file
diff --git a/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkStatusTask.java b/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkStatusTask.java
new file mode 100644
index 0000000..4b24589
--- /dev/null
+++ b/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkStatusTask.java
@@ -0,0 +1,732 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.status;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.catalina.ant.BaseRedirectorHelperTask;
+import org.apache.tomcat.util.IntrospectionUtils;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * Ant task that implements the show <code>/jkstatus</code> command, supported
+ * by the mod_jk status (1.2.13) application.
+ * 
+ * @author Peter Rossbach
+ * @version $Revision$
+ * @since 5.5.10
+ */
+public class JkStatusTask extends BaseRedirectorHelperTask {
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    private static final String info = "org.apache.jk.status.JkStatusTask/1.2";
+
+    /**
+     * Store status as <code>resultProperty</code> prefix.
+     */
+    protected String resultproperty;
+
+    /**
+     * Echo status at ant console
+     */
+    protected boolean echo = false;
+
+    /**
+     * The login password for the <code>mod_jk status</code> page.
+     */
+    protected String password = null;
+
+    /**
+     * The URL of the <code>mod_jk status</code> page to be used.
+     */
+    protected String url = "http://localhost:80/jkstatus";
+
+    /**
+     * The login username for the <code>mod_jk status</code> page.
+     */
+    protected String username = null;
+
+    private String errorProperty;
+
+    private String worker;
+
+    private String loadbalancer;
+
+    /**
+     * Return descriptive information about this implementation and the
+     * corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+    public String getPassword() {
+        return (this.password);
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public String getUrl() {
+        return (this.url);
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public String getUsername() {
+        return (this.username);
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    /**
+     * @return Returns the echo.
+     */
+    public boolean isEcho() {
+        return echo;
+    }
+
+    /**
+     * @param echo
+     *            The echo to set.
+     */
+    public void setEcho(boolean echo) {
+        this.echo = echo;
+    }
+
+    /**
+     * @return Returns the resultproperty.
+     */
+    public String getResultproperty() {
+        return resultproperty;
+    }
+
+    /**
+     * @param resultproperty
+     *            The resultproperty to set.
+     */
+    public void setResultproperty(String resultproperty) {
+        this.resultproperty = resultproperty;
+    }
+
+    /**
+     * @return Returns the loadbalancer.
+     */
+    public String getLoadbalancer() {
+        return loadbalancer;
+    }
+    /**
+     * @param loadbalancer The loadbalancer to set.
+     */
+    public void setLoadbalancer(String loadbalancer) {
+        this.loadbalancer = loadbalancer;
+    }
+    /**
+     * @return Returns the worker.
+     */
+    public String getWorker() {
+        return worker;
+    }
+    /**
+     * @param worker The worker to set.
+     */
+    public void setWorker(String worker) {
+        this.worker = worker;
+    }
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Get jkstatus from server.
+     * 
+     * @exception BuildException
+     *                if a validation error occurs
+     */
+    public void execute() throws BuildException {
+
+        if (url == null) {
+            throw new BuildException("Must specify an 'url'");
+        }
+        boolean isWorkerOnly = worker != null && !"".equals(worker);
+        boolean isLoadbalancerOnly = loadbalancer != null
+                && !"".equals(loadbalancer);
+
+        StringBuffer error = new StringBuffer();
+        try {
+            JkStatusAccessor accessor = new JkStatusAccessor();
+            JkStatus status = accessor.status(url, username, password);
+            if (status.result != null && !"OK".equals(status.result.type) ) {
+                if (getErrorProperty() != null) {
+                    getProject().setNewProperty(errorProperty, status.result.message);
+                }
+                if (isFailOnError()) {
+                    throw new BuildException(status.result.message);
+                } else {
+                    handleErrorOutput(status.result.message);
+                    return;
+                }
+
+            }
+            
+            if (!isWorkerOnly && !isLoadbalancerOnly) {
+                JkServer server = status.getServer();
+                JkSoftware software = status.getSoftware();
+                JkResult result = status.getResult();
+                if (resultproperty != null) {
+                    createProperty(server, "server", "name");
+                    createProperty(server, "server", "port");
+                    createProperty(software, "web_server");
+                    createProperty(software, "jk_version");
+                    createProperty(result, "result", "type");
+                    createProperty(result, "result", "message");
+                }
+                if (isEcho()) {
+                    handleOutput("server name=" + server.getName() + ":"
+                            + server.getPort() + " - " + software.getWeb_server() + " - " + software.getJk_version());
+                }
+            }
+            List balancers = status.getBalancers();
+            for (Iterator iter = balancers.iterator(); iter.hasNext();) {
+                JkBalancer balancer = (JkBalancer) iter.next();
+                String balancerIndex = null;
+                if (isLoadbalancerOnly) {
+                    if (loadbalancer.equals(balancer.getName())) {
+                        if (resultproperty != null) {
+                            setPropertyBalancerOnly(balancer);
+                        }
+                        echoBalancer(balancer);
+                        return;
+                    }
+                } else {
+                    if (!isWorkerOnly) {
+                        if (resultproperty != null) { 
+                        	if ( balancer.getId() >= 0)
+                        		balancerIndex = Integer.toString(balancer.getId());
+                        	else
+                        		balancerIndex = balancer.getName() ;
+                        	
+                        	setPropertyBalancer(balancer,balancerIndex);
+                        }
+                        echoBalancer(balancer);
+                    }
+                    List members = balancer.getBalancerMembers();
+                    for (Iterator iterator = members.iterator(); iterator
+                            .hasNext();) {
+                        JkBalancerMember member = (JkBalancerMember) iterator
+                                .next();
+                        if (isWorkerOnly) {
+                            if (worker.equals(member.getName())) {
+                                if (resultproperty != null) {
+                                    setPropertyWorkerOnly(balancer, member);
+                                }
+                                echoWorker(member);
+                                return;
+                            }
+                        } else {
+                            if (resultproperty != null) {
+                                setPropertyWorker(null, member);
+                            }
+                            echoWorker(member);
+                            if (member.getStatus() != null && !"OK".equals(member.getStatus())) {
+                                error.append(" worker name=" + member.getName()
+                                        + " status=" + member.getStatus()
+                                        + " host=" + member.getAddress());
+                            }
+                            if (member.getState() != null && !("OK".equals(member.getState()) || "N/A".equals(member.getState())) ){
+                                error.append(" worker name=" + member.getName()
+                                        + " state=" + member.getState()
+                                        + " host=" + member.getAddress());
+                            }
+                        }
+                    }
+                    if (!isWorkerOnly) {
+                        if (resultproperty != null && members.size() > 0) {
+                            getProject().setNewProperty(
+                                    resultproperty + "."
+                                            + balancer.getName() + ".length",
+                                    Integer.toString(members.size()));
+                        }
+                        List mappings = balancer.getBalancerMappings();
+                        int j = 0;
+                        String mapIndex ;
+                        if( balancerIndex != null )
+                        	mapIndex = balancerIndex + ".map" ;
+                        else
+                        	mapIndex = "map" ;
+                        for (Iterator iterator = mappings.iterator(); iterator
+                                .hasNext(); j++) {
+                            JkBalancerMapping mapping = (JkBalancerMapping) iterator
+                                    .next();
+                            if (resultproperty != null) {
+                            	String stringIndex2 ;
+                                if( mapping.getId() >= 0) {
+                                    stringIndex2 =  Integer.toString(mapping.getId()) ;
+                                } else {
+                                    stringIndex2 = Integer.toString(j);
+                                }                       	
+                                createProperty(mapping, mapIndex,
+                                        stringIndex2, "type");
+                                createProperty(mapping, mapIndex,
+                                        stringIndex2, "uri");
+                                createProperty(mapping, mapIndex,
+                                        stringIndex2, "context");
+                                createProperty(mapping, mapIndex,
+                                        stringIndex2, "source");
+                            }
+                            if (isEcho()) {
+                            	String mappingOut ;
+                            	
+                            	if(mapping.source != null) {
+                            		mappingOut =
+                            			"balancer name="
+                                        + balancer.getName() + " mappingtype="
+                                        + mapping.getType() + " uri="
+                                        + mapping.getUri() + " source="
+                                        + mapping.getSource() ;
+                            	} else {
+                            		mappingOut = "balancer name="
+                                        + balancer.getName() + " mappingtype="
+                                        + mapping.getType() + " uri="
+                                        + mapping.getUri() + " context="
+                                        + mapping.getContext() ;
+                            	}
+                            	handleOutput(mappingOut);
+                            }
+                        }
+                        if (resultproperty != null && mappings.size() > 0) {
+                            getProject().setNewProperty(
+                                    resultproperty + "."
+                                            + mapIndex + ".length",
+                                    Integer.toString(mappings.size()));
+                        }
+                    }
+                }
+            }
+            if (!isWorkerOnly && !isLoadbalancerOnly) {
+                if (resultproperty != null && balancers.size() > 0) {
+                    getProject().setNewProperty(
+                            resultproperty + ".length",
+                            Integer.toString(balancers.size()));
+                }
+            }
+        } catch (Throwable t) {
+            error.append(t.getMessage());
+            if (getErrorProperty() != null) {
+                getProject().setNewProperty(errorProperty, error.toString());
+            }
+            if (isFailOnError()) {
+                throw new BuildException(t);
+            } else {
+                handleErrorOutput(t.getMessage());
+                return;
+            }
+        }
+        if (error.length() != 0) {
+            if (getErrorProperty() != null) {
+                getProject().setNewProperty(errorProperty, error.toString());
+            }
+            if (isFailOnError()) {
+                // exception should be thrown only if failOnError == true
+                // or error line will be logged twice
+                throw new BuildException(error.toString());
+            }
+        }
+
+    }
+
+    /**
+     * @param member
+     */
+    private void echoWorker(JkBalancerMember member) {
+        if (isEcho()) {
+            StringBuffer state = new StringBuffer("worker name=") ;
+            state.append( member.getName()) ;
+            if(member.getStatus() != null) {
+                state.append(" status=");
+                state.append(member.getStatus());
+            }
+            if(member.getState() != null) {
+                state.append(" state=");
+                state.append(member.getState())  ;
+            }
+            state.append(" host=");
+            state.append(member.getAddress());
+            handleOutput(state.toString());
+        }
+    }
+
+    /**
+     * @param balancer
+     */
+    private void echoBalancer(JkBalancer balancer) {
+        if (isEcho()) {
+            handleOutput("balancer name=" + balancer.getName() + " type="
+                    + balancer.getType());
+        }
+    }
+
+    /**
+     * @param balancer
+     */
+    private void setPropertyBalancerOnly(JkBalancer balancer) {
+        String prefix = resultproperty + "." + balancer.getName();
+        if(balancer.getId() >= 0 ) {
+        	getProject().setNewProperty(prefix + ".id",
+                Integer.toString(balancer.getId()));
+        }
+        getProject().setNewProperty(prefix + ".type", balancer.getType());
+        getProject().setNewProperty(prefix + ".stick_session",
+                Boolean.toString(balancer.isSticky_session()));
+        getProject().setNewProperty(prefix + ".sticky_session_force",
+                Boolean.toString(balancer.isSticky_session_force()));
+        getProject().setNewProperty(prefix + ".retries",
+                Integer.toString(balancer.getRetries()));
+        getProject().setNewProperty(prefix + ".recover_time",
+                Integer.toString(balancer.getRecover_time()));
+        getProject().setNewProperty(prefix + ".method",
+                balancer.getMethod());
+        getProject().setNewProperty(prefix + ".good",
+                Integer.toString(balancer.getGood()));
+        getProject().setNewProperty(prefix + ".degraded",
+                Integer.toString(balancer.getDegraded()));
+        getProject().setNewProperty(prefix + ".bad",
+                Integer.toString(balancer.getBad()));
+        getProject().setNewProperty(prefix + ".busy",
+                Integer.toString(balancer.getBusy()));
+        getProject().setNewProperty(prefix + ".map_count",
+                Integer.toString(balancer.getMap_count()));
+        getProject().setNewProperty(prefix + ".member_count",
+                Integer.toString(balancer.getMember_count()));
+        getProject().setNewProperty(prefix + ".max_busy",
+                Integer.toString(balancer.getMax_busy()));
+        getProject().setNewProperty(prefix + ".time_to_maintenance_min",
+                Integer.toString(balancer.getTime_to_maintenance_min()));
+        getProject().setNewProperty(prefix + ".time_to_maintenance_max",
+                Integer.toString(balancer.getTime_to_maintenance_max()));
+        getProject().setNewProperty(prefix + ".lock",
+                balancer.getLock());
+ 
+    }
+
+    /**
+     * @param balancer
+     * @return
+     */
+    private void setPropertyBalancer(JkBalancer balancer,String balancerIndex) {
+     	if(balancer.id >= 0) {
+    		createProperty(balancer, balancerIndex, "id");
+    	}
+
+        createProperty(balancer, balancerIndex, "name");
+        createProperty(balancer, balancerIndex, "type");
+        createProperty(balancer, balancerIndex, "sticky_session");
+        createProperty(balancer, balancerIndex, "sticky_session_force");
+        createProperty(balancer, balancerIndex, "retries");
+        createProperty(balancer, balancerIndex, "recover_time");
+        if(balancer.getMethod() != null) {
+        	createProperty(balancer, balancerIndex, "method");
+        }
+        if(balancer.getLock() != null) {
+        	createProperty(balancer, balancerIndex, "lock");
+        }
+        if(balancer.getGood() >= 0) {
+        	createProperty(balancer, balancerIndex, "good");
+        }
+        if(balancer.getDegraded() >= 0) {
+        	createProperty(balancer, balancerIndex, "degraded");
+        }
+        if(balancer.getBad() >= 0) {
+        	createProperty(balancer, balancerIndex, "bad");
+        }
+        if(balancer.getBusy() >= 0) {
+        	createProperty(balancer, balancerIndex, "busy");
+        }
+        if(balancer.getMax_busy() >= 0) {
+        	createProperty(balancer, balancerIndex, "max_busy");
+        }
+        if(balancer.getMember_count() >=0) {
+        	createProperty(balancer, balancerIndex, "member_count");
+        }
+        if(balancer.getMap_count() >=0) {
+        	createProperty(balancer, balancerIndex, "map_count");
+        }
+        if(balancer.getTime_to_maintenance_min() >=0) {
+        	createProperty(balancer, balancerIndex, "time_to_maintenance_min");
+        }
+        if(balancer.getTime_to_maintenance_max() >=0) {
+        	createProperty(balancer, balancerIndex, "time_to_maintenance_max");
+        }
+   }
+
+    /**
+     * @param balancerIndex
+     * @param member
+     */
+    private void setPropertyWorker(String balancerIndex, JkBalancerMember member) {
+        String workerIndex ;
+        if(member.getId() >= 0) {
+        	workerIndex = Integer.toString(member.getId());
+            createProperty(member, balancerIndex, workerIndex, "id");
+            createProperty(member, balancerIndex, workerIndex, "name");
+        } else {
+        	workerIndex = member.getName();
+        }
+        createProperty(member, balancerIndex, workerIndex, "type");
+        createProperty(member, balancerIndex, workerIndex, "host");
+        createProperty(member, balancerIndex, workerIndex, "port");
+        createProperty(member, balancerIndex, workerIndex, "address");
+        if(member.getJvm_route() != null) {
+            createProperty(member, balancerIndex, workerIndex, "jvm_route");
+        }
+        if(member.getRoute() != null) {
+            createProperty(member, balancerIndex, workerIndex, "route");
+        }       
+        if(member.getStatus() != null) {
+            createProperty(member, balancerIndex, workerIndex, "status");
+        }
+        if(member.getActivation() != null) {
+            createProperty(member, balancerIndex, workerIndex, "activation");
+        }
+        if(member.getState() != null) {
+            createProperty(member, balancerIndex, workerIndex, "state");
+        }
+        createProperty(member, balancerIndex, workerIndex, "lbfactor");
+        createProperty(member, balancerIndex, workerIndex, "lbvalue");
+        if(member.getLbmult() >= 0) {
+            createProperty(member, balancerIndex, workerIndex, "lbmult");
+        }
+        createProperty(member, balancerIndex, workerIndex, "elected");
+        createProperty(member, balancerIndex, workerIndex, "readed");
+        createProperty(member, balancerIndex, workerIndex, "busy");
+        if(member.getMax_busy() >= 0) {
+            createProperty(member, balancerIndex, workerIndex, "max_busy");
+        }
+        createProperty(member, balancerIndex, workerIndex, "transferred");
+        createProperty(member, balancerIndex, workerIndex, "errors");
+        if(member.getClient_errors() >= 0) {
+            createProperty(member, balancerIndex, workerIndex, "client_errors");
+        }
+        if(member.getDistance() >= 0) {
+            createProperty(member, balancerIndex, workerIndex, "distance");
+        }
+        if (member.getDomain() != null) {
+            createProperty(member, balancerIndex, workerIndex, "domain");
+        } else {
+            getProject().setNewProperty(resultproperty + "." + balancerIndex + "." + workerIndex +
+                    ".domain", "");          
+        }
+        if (member.getRedirect() != null) {
+            createProperty(member, balancerIndex, workerIndex, "redirect");
+        } else {
+            getProject().setNewProperty(resultproperty + "." + balancerIndex + "." + workerIndex +
+                    ".redirect", "");          
+        }
+    }
+
+    /**
+     * @param balancer
+     * @param member
+     */
+    private void setPropertyWorkerOnly(JkBalancer balancer,
+            JkBalancerMember member) {
+        //String prefix = resultproperty + "." + balancer.getName() + "." + member.getName();
+        String prefix = resultproperty + "." + member.getName();
+               Project currentProject = getProject();
+        if ( balancer.getId() >= 0) { 
+        	currentProject.setNewProperty(prefix + ".lb.id",
+                Integer.toString(balancer.getId()));
+        }
+        //currentProject.setNewProperty(prefix + ".lb.name", balancer.getName());
+        if( member.getId() >= 0) {
+        	currentProject.setNewProperty(prefix + ".id",
+                Integer.toString(member.getId()));
+        }
+        currentProject.setNewProperty(prefix + ".type", member.getType());
+        if (member.getJvm_route() != null) {
+            currentProject.setNewProperty(prefix + ".jvm_route", member.getJvm_route());
+        }
+        if (member.getRoute() != null) {
+            currentProject.setNewProperty(prefix + ".route", member.getRoute());
+        }
+        if (member.getStatus() != null) {
+            currentProject.setNewProperty(prefix + ".status", member.getStatus());
+        }
+        if (member.getActivation() != null) {
+            currentProject.setNewProperty(prefix + ".activation", member.getActivation());
+        }
+        if (member.getState() != null) {
+            currentProject.setNewProperty(prefix + ".state", member.getState());
+        }
+        currentProject.setNewProperty(prefix + ".host", member.getHost());
+        currentProject.setNewProperty(prefix + ".address", member.getAddress());
+        currentProject.setNewProperty(prefix + ".port",
+                Integer.toString(member.getPort()));
+        currentProject.setNewProperty(prefix + ".lbfactor",
+                Integer.toString(member.getLbfactor()));
+        currentProject.setNewProperty(prefix + ".lbvalue",
+                Long.toString(member.getLbvalue()));
+        if(member.getLbmult() >= 0) {
+            currentProject.setNewProperty(prefix + ".lbmult",
+                    Long.toString(member.getLbmult()));
+        }
+        currentProject.setNewProperty(prefix + ".elected",
+                Long.toString(member.getElected()));
+        currentProject.setNewProperty(prefix + ".readed",
+                Long.toString(member.getReaded()));
+        currentProject.setNewProperty(prefix + ".transferred",
+                Long.toString(member.getTransferred()));
+        currentProject.setNewProperty(prefix + ".busy",
+                Integer.toString(member.getBusy()));
+        if(member.getMax_busy() >= 0) {
+            currentProject.setNewProperty(prefix + ".max_busy",
+                    Long.toString(member.getMax_busy()));
+        }
+        currentProject.setNewProperty(prefix + ".errors",
+                Long.toString(member.getErrors()));
+        if(member.getClient_errors() >= 0) {
+            currentProject.setNewProperty(prefix + ".client_errors",
+                    Long.toString(member.getClient_errors()));
+        }
+        if(member.getDistance() >= 0) {
+            currentProject.setNewProperty(prefix + ".distance",
+                    Integer.toString(member.getDistance()));
+        }
+        if (member.getDomain() != null) {
+            currentProject.setNewProperty(prefix + ".domain", member.getDomain());
+        } else {
+            currentProject.setNewProperty(prefix + ".domain", "");
+        }
+        if (member.getRedirect() != null) {
+            currentProject.setNewProperty(prefix + ".redirect",
+                    member.getRedirect());
+        } else {
+            currentProject.setNewProperty(prefix + ".redirect", "");
+        }
+        if(member.getTime_to_recover() >= 0) {
+            currentProject.setNewProperty(prefix + ".time_to_recover",
+                    Integer.toString(member.getTime_to_recover()));
+        }
+        if(member.getTime_to_recover_min() >= 0) {
+            currentProject.setNewProperty(prefix + ".time_to_recover_min",
+                    Integer.toString(member.getTime_to_recover_min()));
+        }
+        if(member.getTime_to_recover_max() >= 0) {
+            currentProject.setNewProperty(prefix + ".time_to_recover_max",
+                    Integer.toString(member.getTime_to_recover_max()));
+            currentProject.setNewProperty(prefix + ".time_to_recover",
+                    (Integer.toString((member.getTime_to_recover_min()
+                            + member.getTime_to_recover_max()) / 2)));
+        }
+            
+    }
+
+    /*
+     * Set ant property for save error state
+     * 
+     * @see org.apache.catalina.ant.BaseRedirectorHelperTask#setErrorProperty(java.lang.String)
+     */
+    public void setErrorProperty(String arg0) {
+        errorProperty = arg0;
+        super.setErrorProperty(arg0);
+    }
+
+    /**
+     * @return Returns the errorProperty.
+     */
+    public String getErrorProperty() {
+        return errorProperty;
+    }
+
+    protected void createProperty(Object result, String attribute) {
+        createProperty(result, null, null, attribute);
+    }
+
+    protected void createProperty(Object result, String arraymark,
+            String attribute) {
+        createProperty(result, arraymark, null, attribute);
+    }
+
+    /**
+     * create result as property with name from attribute resultproperty
+     */
+    protected void createProperty(Object result, String arraymark,
+            String arraymark2, String attribute) {
+        if (resultproperty != null) {
+            Object value = IntrospectionUtils.getProperty(result, attribute);
+            if (value != null ) {
+                StringBuffer propertyname = new StringBuffer(resultproperty);
+
+                if (result instanceof JkBalancer) {
+                    if (arraymark != null) {
+                        propertyname.append(".");
+                        propertyname.append(arraymark);
+                    }
+                } else if (result instanceof JkServer) {
+					if (arraymark != null) {
+						propertyname.append(".");
+						propertyname.append(arraymark);
+					}
+                } else if (result instanceof JkSoftware) {
+					if (arraymark != null) {
+						propertyname.append(".");
+						propertyname.append(arraymark);
+					}
+				} else if (result instanceof JkResult) {
+					if (arraymark != null) {
+						propertyname.append(".");
+						propertyname.append(arraymark);
+					}
+                } else if (result instanceof JkBalancerMember) {
+                    if (arraymark != null) {
+                        propertyname.append(".");
+                        propertyname.append(arraymark);
+                    }
+                    if (arraymark2 != null) {
+                        propertyname.append(".");
+                        propertyname.append(arraymark2);
+                    }
+                } else if (result instanceof JkBalancerMapping) {
+                    if (arraymark != null) {
+                        propertyname.append(".");
+                        propertyname.append(arraymark);
+                    }
+                    if (arraymark2 != null) {
+                        propertyname.append(".");
+                        propertyname.append(arraymark2);
+                    }
+                }
+                propertyname.append(".");
+                propertyname.append(attribute);
+                getProject().setNewProperty(propertyname.toString(),
+                        value.toString());
+            }
+        }
+    }
+
+}
diff --git a/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkStatusUpdateLoadbalancerTask.java b/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkStatusUpdateLoadbalancerTask.java
new file mode 100644
index 0000000..c8a8204
--- /dev/null
+++ b/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkStatusUpdateLoadbalancerTask.java
@@ -0,0 +1,287 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.status;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Ant task that implements the <code>/status</code> update loadbalancer command, supported by the
+ * mod_jk status (1.2.20) application.
+ * 
+ * 
+ * @author Peter Rossbach
+ * @version $Revision:$
+ * @since mod_jk 1.2.20
+ */
+public class JkStatusUpdateLoadbalancerTask extends AbstractJkStatusTask {
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    private static final String info = "org.apache.jk.status.JkStatusUpdateLoadbalancerTask/1.0";
+
+    protected String loadbalancer ;
+    
+    protected int retries = -1;
+    protected int recoverWaitTime = -1;
+    protected int methodCode = -1;
+    protected String method;
+
+    protected Boolean stickySession ;
+    
+    protected Boolean forceStickySession ;
+   
+    protected int lockCode = -1;
+    protected String lock;
+
+    /**
+     * Return descriptive information about this implementation and the
+     * corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+    
+    /**
+     *  
+     */
+    public JkStatusUpdateLoadbalancerTask() {
+        super();
+        setUrl("http://localhost/jkstatus");
+    }
+ 
+    /**
+	 * @return the forceStickySession
+	 */
+	public Boolean getForceStickySession() {
+		return forceStickySession;
+	}
+
+	/**
+	 * @param forceStickySession the forceStickySession to set
+	 */
+	public void setForceStickySession(Boolean forceStickySession) {
+		this.forceStickySession = forceStickySession;
+	}
+
+	/**
+	 * @return the loadbalancer
+	 */
+	public String getLoadbalancer() {
+		return loadbalancer;
+	}
+
+	/**
+	 * @param loadbalancer the loadbalancer to set
+	 */
+	public void setLoadbalancer(String loadbalancer) {
+		this.loadbalancer = loadbalancer;
+	}
+
+
+	/**
+	 * @return the locking
+	 */
+	public String getLock() {
+		return lock;
+	}
+
+	/**
+	 * @param locking the locking to set
+	 */
+	public void setLock(String locking) {
+		this.lock = locking;
+	}
+
+	/**
+	 * @return the lockingCode
+	 */
+	public int getLockCode() {
+		return lockCode;
+	}
+
+	/**
+	 * @param lockingCode the lockingCode to set
+	 */
+	public void setLockCode(int lockingCode) {
+		this.lockCode = lockingCode;
+	}
+
+	/**
+	 * @return the method
+	 */
+	public String getMethod() {
+		return method;
+	}
+
+	/**
+	 * @param method the method to set
+	 */
+	public void setMethod(String method) {
+		this.method = method;
+	}
+
+	/**
+	 * @return the methodCode
+	 */
+	public int getMethodCode() {
+		return methodCode;
+	}
+
+	/**
+	 * @param methodCode the methodCode to set
+	 */
+	public void setMethodCode(int methodCode) {
+		this.methodCode = methodCode;
+	}
+
+	/**
+	 * @return the recoverWaitTime
+	 */
+	public int getRecoverWaitTime() {
+		return recoverWaitTime;
+	}
+
+	/**
+	 * @param recoverWaitTime the recoverWaitTime to set
+	 */
+	public void setRecoverWaitTime(int recoverWaitTime) {
+		this.recoverWaitTime = recoverWaitTime;
+	}
+
+	/**
+	 * @return the retries
+	 */
+	public int getRetries() {
+		return retries;
+	}
+
+	/**
+	 * @param retries the retries to set
+	 */
+	public void setRetries(int retries) {
+		this.retries = retries;
+	}
+
+	/**
+	 * @return the stickySession
+	 */
+	public Boolean getStickySession() {
+		return stickySession;
+	}
+
+	/**
+	 * @param stickySession the stickySession to set
+	 */
+	public void setStickySession(Boolean stickySession) {
+		this.stickySession = stickySession;
+	}
+
+	/**
+     * Create JkStatus worker update link
+     * <ul>
+     * </b>http://localhost/jkstatus?cmd=update&mime=txt&w=loadbalancer&lm=1&ll=1&lr=2&lt=60&ls=true&lf=false
+     * <br/>
+     *
+     * 
+     * <br/>Tcp worker parameter:
+     * <br/>
+     * <ul>
+     * <li><b>w:<b/> name loadbalancer</li>
+     * <li><b>lm:<b/> method (lb strategy)</li>
+     * <li><b>ll:<b/> lock</li>
+     * <li><b>lr:<b/> retries</li>
+     * <li><b>lt:<b/> recover wait timeout</li>
+     * <li><b>ls:<b/> sticky session</li>
+     * <li><b>lf:<b/> force sticky session</li>
+     * </ul>
+     * <ul>
+     * <li>lm=1 or Requests</li>
+     * <li>lm=2 or Traffic</li>
+     * <li>lm=3 or Busyness</li>
+     * <li>lm=4 or Sessions</li>
+     * </ul>
+     * <ul>
+     * <li>ll=1 or Optimistic</li>
+     * <li>ll=2 or Pessimistic</li>
+     * </ul>
+     * 
+     * @return create jkstatus update worker link
+     */
+    protected StringBuffer createLink() {
+        // Building URL
+        StringBuffer sb = new StringBuffer();
+        try {
+            sb.append("?cmd=update&mime=txt");
+            sb.append("&w=");
+            sb.append(URLEncoder.encode(loadbalancer, getCharset()));
+            if (stickySession != null) { 
+				sb.append("&ls=");
+				sb.append(stickySession);
+			}
+            if (forceStickySession != null) { 
+ 				sb.append("&lf=");
+ 				sb.append(forceStickySession);
+ 			}
+			if (retries >= 0) {
+				sb.append("&lr=");
+				sb.append(retries);
+			}
+			if (recoverWaitTime >= 0) {
+				sb.append("&lt=");
+				sb.append(recoverWaitTime);
+			}
+			if (method == null && methodCode > 0 && methodCode < 5) {
+				sb.append("&lm=");
+				sb.append(methodCode);
+			}
+            if (method != null) { 
+ 				sb.append("&lm=");
+ 				sb.append(method);
+ 			}
+			if (lock == null && lockCode > 0 && lockCode < 3) {
+				sb.append("&ll=");
+				sb.append(lockCode);
+			}
+            if (lock != null) { 
+ 				sb.append("&ll=");
+ 				sb.append(lock);
+ 			}
+            
+        } catch (UnsupportedEncodingException e) {
+            throw new BuildException("Invalid 'charset' attribute: "
+                    + getCharset());
+        }
+        return sb;
+    }
+
+    /**
+	 * check correct lb and worker pararmeter
+	 */
+    protected void checkParameter() {
+        if (loadbalancer == null) {
+            throw new BuildException("Must specify 'loadbalancer' attribute");
+        }
+    }
+}
\ No newline at end of file
diff --git a/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkStatusUpdateTask.java b/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkStatusUpdateTask.java
new file mode 100644
index 0000000..1db85f9
--- /dev/null
+++ b/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkStatusUpdateTask.java
@@ -0,0 +1,528 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.status;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+
+import org.apache.catalina.ant.AbstractCatalinaTask;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Ant task that implements the <code>/status</code> command, supported by the
+ * mod_jk status (1.2.13) application.
+ * 
+ * 
+ * @author Peter Rossbach
+ * @version $Revision$
+ * @since 5.5.10
+ * @deprecated
+ */
+public class JkStatusUpdateTask extends AbstractCatalinaTask {
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    private static final String info = "org.apache.jk.status.JkStatusUpdateTask/1.1";
+
+    private String worker = "lb";
+
+    private String workerType = "lb";
+
+    private int internalid = 0;
+
+    private Integer lbRetries;
+
+    private Integer lbRecovertime;
+
+    private Boolean lbStickySession = Boolean.TRUE;
+
+    private Boolean lbForceSession = Boolean.FALSE;
+
+    private Integer workerLoadFactor;
+
+    private String workerJvmRoute ;
+    
+    private int workerDistance = -1;
+    
+    private String workerRedirect;
+
+    private String workerClusterDomain;
+
+    private Boolean workerDisabled ;
+
+    private Boolean workerStopped ;
+    
+    private int workerActivation = -1;
+      
+    private boolean isLBMode = true;
+    
+    
+
+    private String workerLb;
+
+    /**
+     * Return descriptive information about this implementation and the
+     * corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+    
+    /**
+     *  
+     */
+    public JkStatusUpdateTask() {
+        super();
+        setUrl("http://localhost/jkstatus");
+    }
+
+    /**
+     * @return Returns the workerDistance.
+     */
+    public int getWorkerDistance() {
+        return workerDistance;
+    }
+
+    /**
+     * @param workerDistance The workerDistance to set.
+     */
+    public void setWorkerDistance(int workerDistance) {
+        this.workerDistance = workerDistance;
+    }
+
+    /**
+     * @return Returns the workerJvmRoute.
+     */
+    public String getWorkerJvmRoute() {
+        return workerJvmRoute;
+    }
+
+    /**
+     * @param workerJvmRoute The workerJvmRoute to set.
+     */
+    public void setWorkerJvmRoute(String workerJvmRoute) {
+        this.workerJvmRoute = workerJvmRoute;
+    }
+
+    /**
+     * @return Returns the internalid.
+     */
+    public int getInternalid() {
+        return internalid;
+    }
+
+    /**
+     * @param internalid
+     *            The internalid to set.
+     */
+    public void setInternalid(int internalid) {
+        this.internalid = internalid;
+    }
+
+    /**
+     * @return Returns the lbForceSession.
+     */
+    public Boolean getLbForceSession() {
+        return lbForceSession;
+    }
+
+    /**
+     * @param lbForceSession
+     *            The lbForceSession to set.
+     */
+    public void setLbForceSession(Boolean lbForceSession) {
+        this.lbForceSession = lbForceSession;
+    }
+
+    /**
+     * @return Returns the lbRecovertime.
+     */
+    public Integer getLbRecovertime() {
+        return lbRecovertime;
+    }
+
+    /**
+     * @param lbRecovertime
+     *            The lbRecovertime to set.
+     */
+    public void setLbRecovertime(Integer lbRecovertime) {
+        this.lbRecovertime = lbRecovertime;
+    }
+
+    /**
+     * @return Returns the lbRetries.
+     */
+    public Integer getLbRetries() {
+        return lbRetries;
+    }
+
+    /**
+     * @param lbRetries
+     *            The lbRetries to set.
+     */
+    public void setLbRetries(Integer lbRetries) {
+        this.lbRetries = lbRetries;
+    }
+
+    /**
+     * @return Returns the lbStickySession.
+     */
+    public Boolean getLbStickySession() {
+        return lbStickySession;
+    }
+
+    /**
+     * @param lbStickySession
+     *            The lbStickySession to set.
+     */
+    public void setLbStickySession(Boolean lbStickySession) {
+        this.lbStickySession = lbStickySession;
+    }
+
+    /**
+     * @return Returns the worker.
+     */
+    public String getWorker() {
+        return worker;
+    }
+
+    /**
+     * @param worker
+     *            The worker to set.
+     */
+    public void setWorker(String worker) {
+        this.worker = worker;
+    }
+
+    /**
+     * @return Returns the workerType.
+     */
+    public String getWorkerType() {
+        return workerType;
+    }
+
+    /**
+     * @param workerType
+     *            The workerType to set.
+     */
+    public void setWorkerType(String workerType) {
+        this.workerType = workerType;
+    }
+
+    /**
+     * @return Returns the workerLb.
+     */
+    public String getWorkerLb() {
+        return workerLb;
+    }
+
+    /**
+     * @param workerLb
+     *            The workerLb to set.
+     */
+    public void setWorkerLb(String workerLb) {
+        this.workerLb = workerLb;
+    }
+
+    /**
+     * @return Returns the workerClusterDomain.
+     */
+    public String getWorkerClusterDomain() {
+        return workerClusterDomain;
+    }
+
+    /**
+     * @param workerClusterDomain
+     *            The workerClusterDomain to set.
+     */
+    public void setWorkerClusterDomain(String workerClusterDomain) {
+        this.workerClusterDomain = workerClusterDomain;
+    }
+
+    /**
+     * @return Returns the workerDisabled.
+     */
+    public Boolean getWorkerDisabled() {
+        return workerDisabled;
+    }
+
+    /**
+     * @param workerDisabled
+     *            The workerDisabled to set.
+     */
+    public void setWorkerDisabled(Boolean workerDisabled) {
+        this.workerDisabled = workerDisabled;
+    }
+
+    /**
+     * @return Returns the workerActivation.
+     */
+    public int getWorkerActivation() {
+        return workerActivation;
+    }
+    
+    /**
+     * <ul>
+     * <li>1 active</li>
+     * <li>2 disabled</li>
+     * <li>3 stopped</li>
+     * </ul>
+     * @param workerActivation The workerActivation to set.
+     * 
+     */
+    public void setWorkerActivation(int workerActivation) {
+        this.workerActivation = workerActivation;
+    }
+    
+    /**
+     * @return Returns the workerStopped.
+     */
+    public Boolean getWorkerStopped() {
+        return workerStopped;
+    }
+    
+    /**
+     * @param workerStopped The workerStopped to set.
+     */
+    public void setWorkerStopped(Boolean workerStopped) {
+        this.workerStopped = workerStopped;
+    }
+    
+    /**
+     * @return Returns the workerLoadFactor.
+     */
+    public Integer getWorkerLoadFactor() {
+        return workerLoadFactor;
+    }
+
+    /**
+     * @param workerLoadFactor
+     *            The workerLoadFactor to set.
+     */
+    public void setWorkerLoadFactor(Integer workerLoadFactor) {
+        this.workerLoadFactor = workerLoadFactor;
+    }
+
+    /**
+     * @return Returns the workerRedirect.
+     */
+    public String getWorkerRedirect() {
+        return workerRedirect;
+    }
+
+    /**
+     * @param workerRedirect
+     *            The workerRedirect to set.
+     */
+    public void setWorkerRedirect(String workerRedirect) {
+        this.workerRedirect = workerRedirect;
+    }
+
+    /**
+     * Execute the requested operation.
+     * 
+     * @exception BuildException
+     *                if an error occurs
+     */
+    public void execute() throws BuildException {
+
+        super.execute();
+        checkParameter();
+        StringBuffer sb = createLink();
+        execute(sb.toString(), null, null, -1);
+
+    }
+
+    /**
+     * Create JkStatus link
+     * <ul>
+     * <li><b>load balance example:
+     * </b>http://localhost/jkstatus?cmd=update&mime=txt&w=lb&lf=false&ls=true</li>
+     * <li><b>worker example:
+     * </b>http://localhost/jkstatus?cmd=update&mime=txt&w=node1&wn=node01&l=lb&wf=1&wa=1&wx=0
+     * <br/>
+     * <ul>
+     * <li>wa=1 active</li>
+     * <li>wa=2 disabled</li>
+     * <li>wa=3 stopped</li>
+     * </ul>
+     * </li>
+     * </ul>
+     *
+     * <br/>Loadbalacing parameter:
+     * <br/>
+     * <ul>
+     * <li><b>w:<b/> name lb worker</li>
+     * <li><b>lr:<b/> Number of Retries</li>
+     * <li><b>lt:<b/> recover wait time</li>
+     * <li><b>lf:<b/> Force Sticky Session</li>
+     * <li><b>ls:<b/> Sticky session</li>
+     * </ul>
+     * 
+     * <br/>Tcp worker parameter:
+     * <br/>
+     * <ul>
+     * <li><b>w:<b/> name tcp worker node</li>
+     * <li><b>l:<b/> name loadbalancer</li>
+     * <li><b>wf:<b/> load factor</li>
+     * <li><b>wn:<b/> jvm route</li>
+     * <li><b>wx:<b/> distance</li>
+     * <li><b>wa:<b/> activation state</li>
+     * <li><b>wr:<b/> redirect route</li>
+     * <li><b>wd:<b/> cluster domain</li>
+     * <li><b>ws:<b/> stopped deprecated 1.2.16</li>
+     * <li><b>wd:<b/> disabled deprecated 1.2.16</li>
+     * </ul>
+     * 
+     * @return create jkstatus link
+     */
+    private StringBuffer createLink() {
+        // Building URL
+        StringBuffer sb = new StringBuffer();
+        try {
+            sb.append("?cmd=update&mime=txt");
+            sb.append("&w=");
+            sb.append(URLEncoder.encode(worker, getCharset()));
+
+            if (isLBMode) {
+                //http://localhost/jkstatus?cmd=update&mime=txt&w=lb&lf=false&ls=true
+                if ((lbRetries != null)) { // > 0
+                    sb.append("&lr=");
+                    sb.append(lbRetries);
+                }
+                if ((lbRecovertime != null)) { // > 59
+                    sb.append("&lt=");
+                    sb.append(lbRecovertime);
+                }
+                if ((lbStickySession != null)) {
+                    sb.append("&ls=");
+                    sb.append(lbStickySession);
+                }
+                if ((lbForceSession != null)) {
+                    sb.append("&lf=");
+                    sb.append(lbForceSession);
+                }
+            } else {
+                //http://localhost/status?cmd=update&mime=txt&w=node1&l=lb&wf=1&wd=false&ws=false
+                if (workerLb != null) { // must be configured
+                    sb.append("&l=");
+                    sb.append(URLEncoder.encode(workerLb, getCharset()));
+                }
+                if (workerLoadFactor != null) { // >= 1
+                    sb.append("&wf=");
+                    sb.append(workerLoadFactor);
+                }
+                if (workerJvmRoute != null) {
+                    sb.append("&wn=");
+                    sb.append(URLEncoder.encode(workerJvmRoute, getCharset()));
+                } 
+                if (workerDisabled != null) {
+                    sb.append("&wd=");
+                    sb.append(workerDisabled);
+                }
+                if (workerStopped != null) {
+                    sb.append("&ws=");
+                    sb.append(workerStopped);
+                }
+                if (workerActivation > 0 && workerActivation < 4) {
+                    sb.append("&wa=");
+                    sb.append(workerActivation);
+                } 
+                if (workerDistance >= 0) {
+                    sb.append("&wx=");
+                    sb.append(workerDistance);
+                }
+                if (workerRedirect != null) { // other worker conrecte lb's
+                    sb.append("&wr=");
+                    sb.append(URLEncoder.encode(workerRedirect,
+                            getCharset()));
+                }
+                if (workerClusterDomain != null) {
+                    sb.append("&wc=");
+                    sb.append(URLEncoder.encode(workerClusterDomain,
+                            getCharset()));
+                }
+            }
+
+        } catch (UnsupportedEncodingException e) {
+            throw new BuildException("Invalid 'charset' attribute: "
+                    + getCharset());
+        }
+        return sb;
+    }
+
+    /**
+     * check correct lb and worker pararmeter
+     */
+    protected void checkParameter() {
+        if (worker == null) {
+            throw new BuildException("Must specify 'worker' attribute");
+        }
+        if (workerType == null) {
+            throw new BuildException("Must specify 'workerType' attribute");
+        }
+        if ("lb".equals(workerType)) {
+            if (lbRecovertime == null && lbRetries == null) {
+                throw new BuildException(
+                        "Must specify at a lb worker either 'lbRecovertime' or"
+                                + "'lbRetries' attribute");
+            }
+            if (lbStickySession == null || lbForceSession == null) {
+                throw new BuildException("Must specify at a lb worker either"
+                        + "'lbStickySession' and 'lbForceSession' attribute");
+            }
+            if (null != lbRecovertime && 60 < lbRecovertime.intValue()) {
+                throw new BuildException(
+                        "The 'lbRecovertime' must be greater than 59");
+            }
+            if (null != lbRetries && 1 < lbRetries.intValue()) {
+                throw new BuildException(
+                        "The 'lbRetries' must be greater than 1");
+            }
+            isLBMode = true;
+        } else if ("worker".equals(workerType)) {
+            if (workerLoadFactor == null ) {
+                throw new BuildException(
+                        "Must specify at a node worker 'workerLoadFactor' attribute");
+            }
+            if (workerClusterDomain == null) {
+                throw new BuildException(
+                        "Must specify at a node worker 'workerClusterDomain' attribute");
+            }
+            if (workerRedirect == null) {
+                throw new BuildException(
+                        "Must specify at a node worker 'workerRedirect' attribute");
+            }
+            if (workerLb == null) {
+                throw new BuildException("Must specify 'workerLb' attribute");
+            }
+            if (workerLoadFactor.intValue() < 1) {
+                throw new BuildException(
+                        "The 'workerLoadFactor' must be greater or equal 1");
+            }
+            isLBMode = false;
+        } else {
+            throw new BuildException(
+                    "Only 'lb' and 'worker' supported as workerType attribute");
+        }
+    }
+}
\ No newline at end of file
diff --git a/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkStatusUpdateWorkerTask.java b/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkStatusUpdateWorkerTask.java
new file mode 100644
index 0000000..e3ee097
--- /dev/null
+++ b/connectors/jk/jkstatus/src/share/org/apache/jk/status/JkStatusUpdateWorkerTask.java
@@ -0,0 +1,291 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.status;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Ant task that implements the <code>/status</code> update worker command, supported by the
+ * mod_jk status (1.2.20) application.
+ * 
+ * 
+ * @author Peter Rossbach
+ * @version $Revision:$
+ * @since mod_jk 1.2.20
+ */
+public class JkStatusUpdateWorkerTask extends AbstractJkStatusTask {
+
+    /**
+     * The descriptive information about this implementation.
+     */
+    private static final String info = "org.apache.jk.status.JkStatusUpdateWorkerTask/1.0";
+
+    protected String loadbalancer ;
+    
+    protected String worker ;
+
+    protected int loadfactor =-1;
+
+    protected String route ;
+    
+    protected int distance = -1;
+    
+    protected String redirect;
+
+    protected String domain;
+    
+    protected int activationCode = -1;
+
+    protected String activation ;
+
+    /**
+     * Return descriptive information about this implementation and the
+     * corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+    
+    /**
+     *  
+     */
+    public JkStatusUpdateWorkerTask() {
+        super();
+        setUrl("http://localhost/jkstatus");
+    }
+
+	/**
+	 * @return the activation
+	 */
+	public String getActivation() {
+		return activation;
+	}
+
+	/**
+	 * @param activation the activation to set
+	 */
+	public void setActivation(String activation) {
+		this.activation = activation;
+	}
+
+	/**
+	 * @return the activationCode
+	 */
+	public int getActivationCode() {
+		return activationCode;
+	}
+
+	/**
+	 * @param activationCode the activationCode to set
+	 */
+	public void setActivationCode(int activationCode) {
+		this.activationCode = activationCode;
+	}
+
+	/**
+	 * @return the distance
+	 */
+	public int getDistance() {
+		return distance;
+	}
+
+	/**
+	 * @param distance the distance to set
+	 */
+	public void setDistance(int distance) {
+		this.distance = distance;
+	}
+
+	/**
+	 * @return the domain
+	 */
+	public String getDomain() {
+		return domain;
+	}
+
+	/**
+	 * @param domain the domain to set
+	 */
+	public void setDomain(String domain) {
+		this.domain = domain;
+	}
+
+	/**
+	 * @return the loadbalancer
+	 */
+	public String getLoadbalancer() {
+		return loadbalancer;
+	}
+
+	/**
+	 * @param loadbalancer the loadbalaner to set
+	 */
+	public void setLoadbalancer(String loadbalancer) {
+		this.loadbalancer = loadbalancer;
+	}
+
+	/**
+	 * @return the loadfactor
+	 */
+	public int getLoadfactor() {
+		return loadfactor;
+	}
+
+	/**
+	 * @param loadfactor the loadfactor to set
+	 */
+	public void setLoadfactor(int loadfactor) {
+		this.loadfactor = loadfactor;
+	}
+
+	/**
+	 * @return the redirect
+	 */
+	public String getRedirect() {
+		return redirect;
+	}
+
+	/**
+	 * @param redirect the redirect to set
+	 */
+	public void setRedirect(String redirect) {
+		this.redirect = redirect;
+	}
+
+	/**
+	 * @return the route
+	 */
+	public String getRoute() {
+		return route;
+	}
+
+	/**
+	 * @param route the route to set
+	 */
+	public void setRoute(String route) {
+		this.route = route;
+	}
+
+	/**
+	 * @return the worker
+	 */
+	public String getWorker() {
+		return worker;
+	}
+
+	/**
+	 * @param worker the worker to set
+	 */
+	public void setWorker(String worker) {
+		this.worker = worker;
+	}
+
+
+
+    /**
+     * Create JkStatus worker update link
+     * <ul>
+     * </b>http://localhost/jkstatus?cmd=update&mime=txt&w=loadbalancer&sw=node01&wn=node01&l=lb&wf=1&wa=1&wd=0
+     * <br/>
+     *
+     * 
+     * <br/>Tcp worker parameter:
+     * <br/>
+     * <ul>
+     * <li><b>w:<b/> name loadbalancer</li>
+     * <li><b>sw:<b/> name tcp worker node</li>
+     * <li><b>wf:<b/> load factor</li>
+     * <li><b>wn:<b/> route</li>
+     * <li><b>wd:<b/> distance</li>
+     * <li><b>wa:<b/> activation state</li>
+     * <li><b>wr:<b/> redirect route</li>
+     * <li><b>wc:<b/> cluster domain</li>
+     * </ul>
+     * <ul>
+     * <li>wa=1 active</li>
+     * <li>wa=2 disabled</li>
+     * <li>wa=3 stopped</li>
+     * </ul>
+     * </li>
+     * </ul>
+     * 
+     * @return create jkstatus update worker link
+     */
+    protected StringBuffer createLink() {
+        // Building URL
+        StringBuffer sb = new StringBuffer();
+        try {
+            sb.append("?cmd=update&mime=txt");
+            sb.append("&w=");
+            sb.append(URLEncoder.encode(loadbalancer, getCharset()));
+            sb.append("&sw=");
+            sb.append(URLEncoder.encode(worker, getCharset()));
+            if (loadfactor >= 0) {
+				sb.append("&wf=");
+				sb.append(loadfactor);
+			}
+			if (route != null) {
+				sb.append("&wn=");
+				sb.append(URLEncoder.encode(route, getCharset()));
+			}
+			if (activation == null && activationCode > 0 && activationCode < 4) {
+				sb.append("&wa=");
+				sb.append(activation);
+			}
+			if (activation != null) {
+				sb.append("&wa=");
+				sb.append(URLEncoder.encode(activation, getCharset()));
+			}
+			if (distance >= 0) {
+				sb.append("&wd=");
+				sb.append(distance);
+			}
+			if (redirect != null) { // other worker conrecte lb's
+				sb.append("&wr=");
+				sb.append(URLEncoder.encode(redirect, getCharset()));
+			}
+			if (domain != null) {
+				sb.append("&wc=");
+				sb.append(URLEncoder.encode(domain, getCharset()));
+			}
+            
+        } catch (UnsupportedEncodingException e) {
+            throw new BuildException("Invalid 'charset' attribute: "
+                    + getCharset());
+        }
+        return sb;
+    }
+
+    /**
+	 * check correct lb and worker pararmeter
+	 */
+    protected void checkParameter() {
+        if (worker == null) {
+            throw new BuildException("Must specify 'worker' attribute");
+        }
+        if (loadbalancer == null) {
+            throw new BuildException("Must specify 'loadbalancer' attribute");
+        }
+    }
+}
\ No newline at end of file
diff --git a/connectors/jk/jkstatus/src/share/org/apache/jk/status/LocalStrings.properties b/connectors/jk/jkstatus/src/share/org/apache/jk/status/LocalStrings.properties
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/connectors/jk/jkstatus/src/share/org/apache/jk/status/LocalStrings.properties
diff --git a/connectors/jk/jkstatus/src/share/org/apache/jk/status/antlib.xml b/connectors/jk/jkstatus/src/share/org/apache/jk/status/antlib.xml
new file mode 100644
index 0000000..7e19502
--- /dev/null
+++ b/connectors/jk/jkstatus/src/share/org/apache/jk/status/antlib.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<antlib>
+  <typedef
+        name="updateworker"
+        classname="org.apache.jk.status.JkStatusUpdateWorkerTask" />
+  <typedef
+        name="updateloadbalancer"
+        classname="org.apache.jk.status.JkStatusUpdateLoadbalancerTask" />
+  <typedef
+        name="update"
+        classname="org.apache.jk.status.JkStatusUpdateTask" />
+  <typedef
+        name="reset"
+        classname="org.apache.jk.status.JkStatusResetTask" />
+  <typedef
+        name="status"
+        classname="org.apache.jk.status.JkStatusTask" />
+</antlib>
\ No newline at end of file
diff --git a/connectors/jk/jkstatus/src/share/org/apache/jk/status/jkstatus.tasks b/connectors/jk/jkstatus/src/share/org/apache/jk/status/jkstatus.tasks
new file mode 100644
index 0000000..7b172ac
--- /dev/null
+++ b/connectors/jk/jkstatus/src/share/org/apache/jk/status/jkstatus.tasks
@@ -0,0 +1,7 @@
+# Apache mod_jk jk status tasks
+jkUpdateWorker=org.apache.jk.status.JkStatusUpdateWorkerTask
+jkUpdateLoadbalancer=org.apache.jk.status.JkStatusUpdateLoadbalancerTask
+jkUpdate=org.apache.jk.status.JkStatusUpdateTask
+jkReset=org.apache.jk.status.JkStatusResetTask
+jkStatus=org.apache.jk.status.JkStatusTask
+
diff --git a/connectors/jk/jkstatus/src/share/org/apache/jk/status/mbeans-descriptors.xml b/connectors/jk/jkstatus/src/share/org/apache/jk/status/mbeans-descriptors.xml
new file mode 100644
index 0000000..b8349bd
--- /dev/null
+++ b/connectors/jk/jkstatus/src/share/org/apache/jk/status/mbeans-descriptors.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0"?>
+<mbeans-descriptors>
+
+<!--
+  <mbean name="JkStatus"
+         description="represent the apache mod_jk status"
+         domain="Jk"
+         group="Status"
+         type="org.apache.jk.status.JkStatusMbean">
+    
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+
+    <attribute name="info"
+               description="The base directory for Context configuration files"
+               type="java.lang.String"
+               writeable="false" />
+      
+    <operation name="check"
+               description="Check a web application name for updates"
+               impact="ACTION"
+               returnType="void">
+      <parameter name="name"
+                 description="Application name"
+                 type="java.lang.String"/>
+    </operation>
+
+  </mbean>
+-->
+</mbeans-descriptors>
diff --git a/connectors/jk/jkstatus/src/share/org/apache/jk/status/package.html b/connectors/jk/jkstatus/src/share/org/apache/jk/status/package.html
new file mode 100644
index 0000000..c7ba746
--- /dev/null
+++ b/connectors/jk/jkstatus/src/share/org/apache/jk/status/package.html
@@ -0,0 +1,208 @@
+<body>
+
+<p>This package contains a set of <code>Task</code> implementations for
+<em>Ant (version 1.6.x or later)</em> that can be used to interact with the
+Apaache mod_jk status page to show, update, disable and stop mod_jk worker.
+For more information, see
+<a href="http://tomcat.apache.org/connectors-doc/index.html"><strong>JK Documenation</strong></a>.</p>
+
+<p>The attributes of each task element correspond
+exactly to the request parameters that are included with an HTTP request
+sent directly to jk status page.  They are summarized as follows:
+</p>
+
+<b>General parameter</b><br/>
+<table>
+  <tr>
+    <th align="center" width="15%">Attribute</th>
+    <th align="center" width="85%">Description</th>
+  </tr>
+  <tr>
+    <td align="center">url</td>
+    <td>
+      The URL of the jk status page you will use to
+      perform the requested operations.  If not specified, defaults to
+      <code>http://localhost:80/jkstatus</code> (which corresponds
+      to a standard installation of Apache mod_jk).
+    </td>
+  </tr>
+  <tr>
+    <td align="center">username</td>
+    <td>
+      The username of a mod_jk status user that has been configured with the
+      <code>Allow user</code> Apache Location constraint. This attribute is optional.
+    </td>
+  </tr>
+  <tr>
+    <td align="center">password</td>
+    <td>
+      The password of a mod_jk status user that has been configured with the
+      <code>Allow user</code> Apache Location constraint. This attribute is optional.
+    </td>
+  </tr>
+  <tr>
+    <td align="center">resultProperty</td>
+    <td>
+    	  Bind all show results with this prefix property name. This attribute is optional.
+    </td>
+  </tr>
+  <tr>
+    <td align="center">echo</td>
+    <td>
+    	  show result at ant console. (default false)
+    </td>
+  </tr>
+  <tr>
+    <td align="center">errorProperty</td>
+    <td>
+    	  set this property, as a failure detected. This attribute is optional.
+    </td>
+  </tr>
+</table>
+
+<b>Command show parameter</b><br/>
+<table>
+  <tr>
+    <th align="center" width="15%">Attribute</th>
+    <th align="center" width="85%">Description</th>
+  </tr>
+  <tr>
+    <td align="center">worker</td>
+    <td>
+      only bind properties from this balancer tcp worker (node)
+    </td>
+  </tr>
+  <tr>
+    <td align="center">loadbalancer</td>
+    <td>
+      only bind properties from this loadbalancer worker
+    </td>
+  </tr>
+</table>
+
+<b>Command reset parameter</b><br/>
+<table>
+  <tr>
+    <th align="center" width="15%">Attribute</th>
+    <th align="center" width="85%">Description</th>
+  </tr>
+  <tr>
+    <td align="center">workerLb</td>
+    <td>
+      name of loadbalancer worker.
+    </td>
+  </tr>
+</table>
+
+<b>Command update loadbalancer parameter</b><br/>
+<table>
+  <tr>
+    <th align="center" width="15%">Attribute</th>
+    <th align="center" width="85%">Description</th>
+  </tr>
+  <tr>
+    <td align="center">workerType=loadbalancer</td>
+    <td>
+      type of update
+    </td>
+  </tr>
+  <tr>
+    <td align="center">workerLb</td>
+    <td>
+      name of loadbalancer worker.
+    </td>
+  </tr>
+  <tr>
+    <td align="center">lbForceSession</td>
+    <td>
+      Force Sticky Session. (true/false)
+    </td>
+  </tr>
+  <tr>
+    <td align="center">lbStickySession</td>
+    <td>
+      Sticky Session. (true/false)
+    </td>
+  </tr>
+  <tr>
+    <td align="center">lbRetries</td>
+    <td>
+      loadbalancer retries after worker connection failure (int)
+    </td>
+  </tr>
+  <tr>
+    <td align="center">lbRecovertime</td>
+    <td>
+      Recover timeout after a worker set to "error" state (int sec's)
+    </td>
+  </tr>
+</table>
+
+<b>Command update worker parameter</b><br/>
+<table>
+  <tr>
+    <th align="center" width="15%">Attribute</th>
+    <th align="center" width="85%">Description</th>
+  </tr>
+  <tr>
+    <td align="center">workerType=worker</td>
+    <td>
+      type of update
+    </td>
+  </tr>
+  <tr>
+    <td align="center">worker</td>
+    <td>
+      name of tcp worker.
+    </td>
+  </tr>
+  <tr>
+    <td align="center">workerActivation (>=1.2.19</td>
+    <td>
+      set worker activation  (1 Active, 2 Disabled, 3 Stopped)
+    </td>
+  </tr>
+  <tr>
+    <td align="center">workerDisabled (< 1.2.19)</td>
+    <td>
+      set disable state. (true/false)
+    </td>
+  </tr>
+  <tr>
+    <td align="center">workerStoppend (< 1.2.19)</td>
+    <td>
+      set stopped state. (true/false)      
+    </td>
+  </tr>
+  <tr>
+    <td align="center">workerJvmRoute</td>
+    <td>
+      set jvm route
+    </td>
+  </tr>
+  <tr>
+    <td align="center">workerLaodFactor</td>
+    <td>
+      set load factor (int)
+    </td>
+  </tr>
+  <tr>
+    <td align="center">workerDistance</td>
+    <td>
+      set worker distance (int)
+    </td>
+  <tr>
+    <td align="center">workerRedirect</td>
+    <td>
+      other worker name to redirect after failure
+    </td>
+  </tr>
+  <tr>
+    <td align="center">workerClusterDomain</td>
+    <td>
+      cluster domain name, group of worker at a repliation cluster.
+    </td>
+  </tr>
+</table>
+
+</body>
diff --git a/connectors/jk/jkstatus/test/build.xml b/connectors/jk/jkstatus/test/build.xml
new file mode 100644
index 0000000..61f265b
--- /dev/null
+++ b/connectors/jk/jkstatus/test/build.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- @author Peter Rossbach -->
+<project name="JkStatusTestcases" basedir="." default="test">
+	<property file="../../../../build/build.properties" />
+	<property file="../../../../build/build.properties.default" />
+	<property name="test.report.logs" value="logs/reports" />
+	<property name="test.results" value="logs/test-results" />
+
+	<property name="compile.optimize" value="true" />
+	<property name="compile.debug" value="true" />
+	<property name="compile.source" value="1.4" />
+	<property name="compile.deprecation" value="true" />
+	<property name="compile.nowarn" value="off" />
+	<property name="compile.encoding" value="ISO-8859-1" />
+	<property name="build.dir" value="build/test" />
+	<property name="src.dir" value="src/share" />
+	<property name="catalina.home" value="../../../../build/build" />
+
+	<!-- Build the classpath -->
+	<path id="project.classpath">
+		<pathelement location="../build/classes" />
+		<pathelement location="${jmx.jar}" />
+		<pathelement location="${commons-logging.jar}" />
+		<pathelement location="${log4j.jar}" />
+		<fileset dir="${catalina.home}/common/endorsed">
+			<include name="*.jar" />
+		</fileset>
+		<fileset dir="${catalina.home}/common/lib">
+			<include name="*.jar" />
+		</fileset>
+		<fileset dir="${catalina.home}/server/lib">
+			<include name="*.jar" />
+		</fileset>
+	</path>
+
+	<target name="build-prepare">
+		<mkdir dir="${build.dir}" />
+	</target>
+
+	<target name="info" description="Shows a information about this ant script">
+		<echo>
+			This ant script implements some testcases to verify the key functions of tomcat apache mod_jk jkstatus module.
+			You find this script at: ${ant.file}
+		</echo>
+	</target>
+
+	<!-- This target compiles all sources out of the 
+			projects source tree -->
+	<target name="compile" depends="build-prepare" description="This target compiles all sources out of the projects source tree">
+
+		<!-- Copies the static resources out of the src tree
+				to the build/classes dir -->
+		<copy todir="${build.dir}/classes">
+			<fileset dir="${src.dir}">
+				<include name="**" />
+				<exclude name="**/*.java" />
+			</fileset>
+		</copy>
+
+		<!-- Compiles all sources -->
+		<javac destdir="${build.dir}/classes" srcdir="${src.dir}" includes="**/*.java" excludes="**/.svn/**" deprecation="${compile.deprecation}" debug="${compile.debug}" source="${compile.source}" optimize="${compile.optimize}" nowarn="${compile.nowarn}" encoding="${compile.encoding}">
+			<classpath>
+				<path refid="project.classpath" />
+			</classpath>
+		</javac>
+	</target>
+
+	<target name="test" depends="compile" description="Run unit tests">
+		<delete dir="${test.results}" />
+		<mkdir dir="${test.results}" />
+		<junit fork="yes" failureProperty="test.failure">
+			<jvmarg value="-Dcatalina.base=${basedir}" />
+			<jvmarg value="-Dcatalina.home=${catalina.home}" />
+			<jvmarg value="-Dlog4j.configuration=file:conf/log4j.xml" />
+			<classpath>
+				<pathelement location="${build.dir}/classes" />
+				<path refid="project.classpath" />
+			</classpath>
+			<formatter type="plain" usefile="false" />
+			<formatter type="xml" />
+			<batchtest todir="${test.results}">
+				<fileset dir="${build.dir}/classes" includes="**/*Test.class" />
+			</batchtest>
+		</junit>
+		<mkdir dir="${test.report.logs}" />
+		<junitreport todir="${test.report.logs}">
+			<fileset dir="${test.results}" />
+			<report format="frames" todir="${test.report.logs}" />
+		</junitreport>
+		<antcall target="checktest" />
+	</target>
+
+	<target name="checktest" if="test.failure">
+		<fail message="some test failed" />
+	</target>
+
+	<target name="clean">
+		<delete dir="${build}/dir" />
+		<delete dir="build" />
+		<delete dir="${test.report.logs}" />
+		<delete dir="${test.results}" />
+		<delete dir="logs" />
+	</target>
+</project>
diff --git a/connectors/jk/jkstatus/test/conf/jkstatus.xml b/connectors/jk/jkstatus/test/conf/jkstatus.xml
new file mode 100644
index 0000000..13f16a6
--- /dev/null
+++ b/connectors/jk/jkstatus/test/conf/jkstatus.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<jk:status xmlns:jk="http://jakarta.apache.org">
+  <jk:server name="localhost" port="80" software="Apache/2.0.54 (Win32) mod_ssl/2.0.54 OpenSSL/0.9.7g mod_jk/1.2.15" version="1.2.15" />
+  <jk:balancers>
+  <jk:balancer id="0" name="lb" type="lb" sticky="True" stickyforce="False" retries="3" recover="60" >
+      <jk:member id="0" name="node1" type="ajp13" host="localhost" port="9012" address="127.0.0.1:9012" status="OK" lbfactor="1" lbvalue="1" elected="0" readed="0" transferred="0" errors="0" busy="0" />
+      <jk:member id="1" name="node2" type="ajp13" host="localhost" port="9022" address="127.0.0.1:9022" status="OK" lbfactor="1" lbvalue="1" elected="0" readed="0" transferred="0" errors="0" busy="0" />
+    <jk:map type="Wildchar" uri="/ClusterTest/*" context="/ClusterTest/*" />
+    <jk:map type="Exact" uri="/ClusterTest" context="/ClusterTest" />
+    <jk:map type="Wildchar" uri="/myapps/*" context="/myapps/*" />
+    <jk:map type="Exact" uri="/myapps" context="/myapps" />
+  </jk:balancer>
+  </jk:balancers>
+</jk:status>
+
diff --git a/connectors/jk/jkstatus/test/conf/log4j.xml b/connectors/jk/jkstatus/test/conf/log4j.xml
new file mode 100644
index 0000000..1f2eda6
--- /dev/null
+++ b/connectors/jk/jkstatus/test/conf/log4j.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<!-- ===================================================================== -->
+<!-- -->
+<!-- Log4j Configuration -->
+<!-- -->
+<!-- ===================================================================== -->
+<!-- $Id:$ -->
+<!--
+| For more configuration infromation and examples see the Jakarta Log4j
+| owebsite: http://jakarta.apache.org/log4j
+-->
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
+
+<!-- ============================== -->
+<!-- Append messages to the console -->
+<!-- ==============================-->
+
+<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+<param name="Target" value="System.out"/>
+<layout class="org.apache.log4j.PatternLayout">
+<!--The default pattern: Date Priority [Category] Message\n-->
+<param name="ConversionPattern" value="%d{ABSOLUTE} %-5p [%c{1}] %m%n"/>
+</layout>
+</appender>
+
+
+<category name="org.apache.jk.status" 
+           additivity="false"> 
+   <priority value="info" />
+   <appender-ref ref="CONSOLE" />
+</category>
+
+<category name="org.apache.catalina" 
+           additivity="false"> 
+   <priority value="info" />
+   <appender-ref ref="CONSOLE" />
+</category>
+<category name="org.apache.tomcat" 
+           additivity="false"> 
+   <priority value="error" />
+   <appender-ref ref="CONSOLE" />
+</category>
+<category name="org.apache.naming" 
+           additivity="false"> 
+   <priority value="info" />
+   <appender-ref ref="CONSOLE" />
+</category>
+<category name="org.apache.commons" 
+           additivity="false"> 
+   <priority value="info" />
+   <appender-ref ref="CONSOLE" />
+</category>
+
+<!-- Setup the Root c  -->
+<root>
+<appender-ref ref="CONSOLE"/>
+</root>
+</log4j:configuration> 
+
diff --git a/connectors/jk/jkstatus/test/src/share/org/apache/jk/status/JkStatusParserTest.java b/connectors/jk/jkstatus/test/src/share/org/apache/jk/status/JkStatusParserTest.java
new file mode 100644
index 0000000..5445b4c
--- /dev/null
+++ b/connectors/jk/jkstatus/test/src/share/org/apache/jk/status/JkStatusParserTest.java
@@ -0,0 +1,62 @@
+/*
+ *  Copyright 1999-2004, 2006 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.status;
+
+import java.io.IOException;
+import java.io.StringReader;
+
+import junit.framework.TestCase;
+
+import org.apache.tomcat.util.digester.Digester;
+import org.xml.sax.SAXException;
+
+/**
+ * @author Peter Rossbach
+ *  
+ */
+public class JkStatusParserTest extends TestCase {
+
+	public void testDigester() throws IOException, SAXException {
+		Digester digester = JkStatusParser.createDigester();
+        String example = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"
+         +   "<jk:status xmlns:jk=\"http://tomcat.apache.org\">"
+         +   "<jk:server name=\"localhost\" port=\"80\" software=\"Apache/2.0.58 (Unix) mod_jk/1.2.19\" version=\"1.2.19\" />"
+         +   "<jk:balancers>"
+         +   "<jk:balancer id=\"0\" name=\"loadbalancer\" type=\"lb\" sticky=\"True\" stickyforce=\"False\" retries=\"2\" recover=\"60\" >"
+         +   "<jk:member id=\"0\" name=\"node1\" type=\"ajp13\" host=\"localhost\" port=\"9012\" address=\"127.0.0.1:9012\" activation=\"ACT\" state=\"N/A\" distance=\"0\" lbfactor=\"1\" lbmult=\"1\" lbvalue=\"0\" elected=\"0\" errors=\"0\" transferred=\"0\" readed=\"0\" busy=\"0\" maxbusy=\"0\" jvm_route=\"node1\" />"
+         +   "<jk:member id=\"0\" name=\"node2\" type=\"ajp13\" host=\"localhost\" port=\"9022\" address=\"127.0.0.1:9022\" activation=\"ACT\" state=\"N/A\" distance=\"0\" lbfactor=\"1\" lbmult=\"1\" lbvalue=\"0\" elected=\"0\" errors=\"0\" transferred=\"0\" readed=\"0\" busy=\"0\" maxbusy=\"0\" jvm_route=\"node2\" />"
+         +   "<jk:map type=\"Wildchar\" uri=\"/ClusterTest/*\" context=\"/ClusterTest/*\" />"
+         +   "<jk:map type=\"Exact\" uri=\"/ClusterTest\" context=\"/ClusterTest\" />"
+         +   "<jk:map type=\"Wildchar\" uri=\"/myapps/*\" context=\"/myapps/*\" />"
+         +   "<jk:map type=\"Exact\" uri=\"/myapps\" context=\"/myapps\" />"
+         +   "</jk:balancer>"
+         +   "</jk:balancers>"
+         +   "</jk:status>" ;
+
+		StringReader reader = new StringReader(example);
+		JkStatus status = (JkStatus) digester
+				.parse(reader);
+	    assertNotNull(status);
+        assertNotNull(status.getServer());
+        assertEquals(1,status.getBalancers().size());
+        JkBalancer balancer = (JkBalancer)status.getBalancers().get(0);
+        assertEquals(2,balancer.getBalancerMembers().size());
+        assertEquals("node1",((JkBalancerMember)balancer.getBalancerMembers().get(0)).getName());
+        assertEquals("node2",((JkBalancerMember)balancer.getBalancerMembers().get(1)).getName());
+        assertEquals(4,balancer.getBalancerMappings().size());
+	}
+
+}
\ No newline at end of file
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..6ded1fe
--- /dev/null
+++ b/connectors/jk/native/BUILDING
@@ -0,0 +1,157 @@
+ 
+  Building from the subversion tree: (for developers only).
+  -----------------------------------
+  When using a subversion 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 tomcat-connectors's autoconf script, you will need libtool
+  1.5.2 or higher, and autoconf 2.59 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 tomcat-connectors run the following commands.
+ 
+  ./buildconf.sh  (not required unless you are a developer)
+  ./configure [autoconf arguments] [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 
+
+  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
+
+
+
+  Building for Netscape/iPlanet/SunONE WebServer
+  ----------------------------------------------
+  make clean (to remove all previously compiled modules)
+  use configure and indicate that you will be building the
+  netscape nsapi redirector, then make the redirector plugin:
+
+  ./configure --enable-netscape
+  cd netscape
+  make -f Makefile.solaris
+
+  This assumes that SUITSPOT_HOME is defined and points to
+  your SunONE install path (eg: /opt/SUNWwbsvr/plugins)
+  and that JAVA_HOME is defined (eg: /opt/SUNWwbsvr/bin/https/jdk).
+
+  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..110fa84
--- /dev/null
+++ b/connectors/jk/native/CHANGES.txt
@@ -0,0 +1,132 @@
+TOMCAT CONNECTORS (JK) CHANGELOG:           -*-text-*-
+Last modified at [$Date$]
+PLEASE DON'T WRITE IN THIS FILE BUT IN ../xdocs/changelog.xml AND USE ../tools/jkrelease.sh
+TO BUILD THE TEXT VERSION.
+
+Changes in JK 1.2.11
+    * BUG 34358: Fix the load balance worker load balance method. It was not being
+      set from the worker.properties file
+    * BUG 34357: Fix a segfault in Apache 2 when using JKAutoAlias.
+    * Update tomcat_trend.pl for new error log string formatting.
+
+Changes in JK 1.2.10
+    * Do not mark the worker in error state if headers are larger then AJP13
+      limit.
+
+Changes in JK 1.2.9
+    * lbfactor is now relationship between members, so worker.a.lbfactor=1 and worker.b.lbfactor=1
+      will give 50-50% load.
+    * Use of shm for load balancer via JkShmFile. Be sure to define JkShmFile /path/to/smfile 
+      in your httpd.conf
+    * on Series you should use the latest PTF for Apache 2.0 (which is now 2.0.52) and ad minima
+      SI17402/SI17061 or cumulative including them.
+     
+Changes in JK 1.2.8
+    * cachesize has changed from unlimited to limited, so it's basically a pool of connections 
+      with recycle timeout like in mod_proxy for Apache 2.2.
+      
+Changes with JK 1.2.7
+    * Fix iis redirector that was figuring .properties
+      file on each request [mturk]
+    * Start fixing 64/32 bit compatibility issues [mturk]
+    * Indent all source files. [mturk]
+    
+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..9e1b12e
--- /dev/null
+++ b/connectors/jk/native/README
@@ -0,0 +1,30 @@
+README for tomcat-connector
+
+$Id$
+
+Please see doc/mod_jk-howto.html for more verbose instructions
+
+* What is tomcat-connector ?
+
+tomcat-connector is a new project to release web-servers connector
+for the 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..fbe689d
--- /dev/null
+++ b/connectors/jk/native/STATUS.txt
@@ -0,0 +1,44 @@
+TOMCAT CONNECTORS (JK) STATUS:			-*-text-*-
+Last modified at [$Date$]
+
+Release:
+
+    1.2.24  : in development
+    1.2.23  : released May 18, 2007
+    1.2.22  : released April 17, 2007
+    1.2.21  : released March 1, 2007
+    1.2.20  : released December 10, 2006
+    1.2.19  : released September 17, 2006
+    1.2.18  : released July, 2006
+    1.2.17  : not released
+    1.2.16  : not released
+    1.2.15  : released November 8, 2005
+    1.2.14  : released July 13, 2005
+    1.2.13  : released May 16, 2005
+    1.2.12  : released May 7, 2005
+    1.2.11  : released April 29, 2005
+    1.2.10  : released March 30, 2005
+    1.2.9   : not released
+    1.2.8   : released December 24, 2004
+    1.2.7   : not released
+    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 December 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/TODO b/connectors/jk/native/TODO
new file mode 100644
index 0000000..a29d1a0
--- /dev/null
+++ b/connectors/jk/native/TODO
@@ -0,0 +1,221 @@
+TODO for tomcat-connectors
+
+$Id$
+
+1) Optimize "distance"
+======================
+
+Sorting the list of balanced workers by distance would be nice, but:
+How to combine the sorting with the offset implementation (especially
+useful for strategy BUSYNESS under low load).
+
+2) Reduce number of string comparisons in most_suitable
+========================================================
+
+a) redirect/domains
+
+It would be easy to improve the redirect string b an integer, giving the
+index of the worker in the lb. Then lb would not need to search for the redirect worker.
+
+The same way, one could add a list with indizes to workers in the same domain.
+Whenever domain names are managed (init and status worker update) one would
+scan the worker list and update the index list.
+
+Finally one could have a list of workers, whose domain is the same as the redirect
+attribute of the worker, because that's also something we consider.
+
+What I'm not sure about, even in the existing code, is the locking between updates
+by the status worker and the process local information about the workers,
+especially in the case, when status updates a redirect or domain attribute.
+
+I would like to keep these attributes and the new index arrays process local,
+and the processes should find out about changes made by status to shm (redirect/domain)
+and then rebuild their data. No need to get these on every request from the shm,
+only the check for up-to-date should be made.
+
+b) exact matches for jvmRoutes
+
+Could we use hashes instead of string comparisons all the time?
+I'm not sure, if a good enough hash takes longer than a string comparison though.
+
+3) Optimization of JK_WORKER_USABLE
+====================================
+
+We use that one quite a lot. Since it is now a combination of four
+ANDs of negated values, we could also do
+
+#define JK_WORKER_USABLE(w)   (!((w)->in_error_state || ($w)->is_stopped || (w)->is_disabled || (w)->is_busy))
+
+I think it we should consider combining the flags into an additional
+is_usable (makes checks easier, but of course we would have to set it
+every time we change one of the four other flags). But in terms of
+performance that happens rarely.
+
+4) Code separation between factory, validate and init in lb
+============================================================
+
+The factory contains:
+
+        private_data->worker.retries = JK_RETRIES;
+        private_data->s->recover_wait_time = WAIT_BEFORE_RECOVER;
+
+I think, this should move to validate() or init().
+It might even be obsolete, because in init, we already have:
+
+    pThis->retries = jk_get_worker_retries(props, p->s->name,
+    p->s->retries = pThis->retries;
+    p->s->recover_wait_time = jk_get_worker_recover_timeout(props, p->s->name, WAIT_BEFORE_RECOVER);
+    if (p->s->recover_wait_time < WAIT_BEFORE_RECOVER)
+        p->s->recover_wait_time = WAIT_BEFORE_RECOVER;
+
+Then: In validate there is
+
+                p->lb_workers[i].s->error_time = 0;
+
+So shouldn't there also be
+
+                p->lb_workers[i].s->maintain_time = time(NULL);
+
+5) Refactor Logging
+====================
+
+a) Use the same code files for the request logging functions in Apache 1.3 and 2.0.
+
+b) Use the same code files for piped logging in Apache 1.3 and 2.0.
+
+6) ajpget
+==========
+
+Combine ajplib and Apache ab to an ajp13 commandline client ajpget.
+
+7) Manage lb method and locking via jk_status
+=============================================
+
+It's not yet contained in the shm.
+
+8) Parsing workers.properties
+=============================
+
+Parsing of workers.properties aditionally to just looking up attributes
+would help users to detect syntax errors in the file. At the moment
+no information will be logged, e.g. when attributes contain typos.
+
+9) Persisting workers.properties
+================================
+
+Make workers.properties persist from inside status worker.
+
+10) Reduce number of uses of time(NULL)
+=======================================
+
+We use time(NULL) a lot. Since it only has resolution of a second,
+I'm asking myself, if we could update the actual time in only a few
+places and get time out of some variables when needed. The same does
+not hold true for millisecond time, but in several cases we use the time,
+it's not very critical, that it is exact. These cases are related to:
+
+Some of this is already been done, the remaining parts are:
+
+- last_access for usage against timeout value that is ~minutes
+- error_time for usage against retry timeout that is ~minutes
+- uri_worker_map checked for usage against JK_URIMAP_RELOAD=1 minute
+
+So I think, it would suffice to set an actual time at the beginning of
+the request/response cycle (used by everything before the request is being
+sent over the socket) and maybe after the response shows up/ an error occurs
+(for everything else, if there is).
+
+For which cases would it be OK, to use the time before sending to TC:
+- uri_worker_map "checked" (uri map lookup starts early)
+- setting/testing last_access in
+  - jk_ajp_common.c:ajp_connect_to_endpoint()
+  - jk_ajp_common.c:ajp_get_endpoint()
+  - jk_ajp_common.c:ajp_maintain()
+
+What about the others:
+- setting last_access in init should use the actual time in
+  jk_ajp_common.c:ajp_create_endpoint_cache()
+
+- setting last_access again after the service could also use the 
+  actual time in jk_ajp_common.c:ajp_done()
+- setting error_time should better use the actual time
+  jk_lb_worker.c service(): rec->s->error_time = time(NULL);
+
+The last two cases could again use the same time, which then would be needed
+to be generated at the end or directly after service.
+
+11) Access/Modification Time in shm
+===================================
+
+a) [Discussion] What will this generally be used for? At the moment,
+only jk_status "uses" it, but it only sets the values, it never asks for them.
+
+b) [Improvement, minor] jk_shm_set_workers_time() implicitly calls
+jk_shm_sync_access_time(), but jk_status does:
+
+            jk_shm_set_workers_time(time(NULL));
+            /* Since we updated the config no need to reload
+             * on the next request
+             */
+            jk_shm_sync_access_time();
+
+two times. So depending on the idea of the functionality of these calls,
+either set_workers_time and sync_access_time should be independently,
+or the second call in jk_status coulkd be removed.
+
+12) "Destroy" functionality
+===========================
+
+[Hint] Destroy on a worker never seems to free shm,
+but I think that was already a flaw without shm.
+
+13) Locks against shm
+=====================
+
+It might be an intersting experiment to implement an improved locking structure.
+It looks like one would need in fact different types of locks.
+In shm we have as read/write information:
+
+Changed only by status worker:
+- redirect, domain, lb_factor, sticky_session, sticky_session_force,
+  recover_wait_time, retries, status (is_disabled, is_stopped).
+
+These changes need some kind of reconfiguration in the threads after
+change and before the next request/response. Since changes are rare,
+here we would be better of, with a simple detect change and copy from
+shm to process procedure. status updates the data in shm and after that
+the time stamp on the shh. Each process checks the time stamp before
+doing a request, and when the time stamp changed it does a writer CS
+lock and updates it's local copy. All threads always do a reader CS
+lock when doing a request/response cycle. Reader CS locks are concurrent,
+writers are exclusive. So readers are not allowed, when the config data is being updated.
+
+Changed by the threads themselves (and via reset by the status worker):
+- counters needed by routing decisions (lb_value, readed, transferred, busy)
+- timers needed by maintenance functions (error_time, servic_time/maintain_time)
+- status is_busy, in_error_state
+- uncritical data with no influence on routing decisions (max_busy, elected, errors,
+  in_recovering)
+
+Here again we could improve by using reader/writer locks. I have a
+tendency for the PESSIMISTIC side of locking, but I think we could
+shrink the code blocks that should be locked. At the monent they are
+pretty big (most of get_most_suitable_worker).
+
+Read-only: name and id.
+
+By the way: at several places we don't check for errors on getting the lock.
+
+14) What I didn't yet check
+===========================
+
+a) Correctness of is_busy handling
+
+b) Correctness of the reset values after reset by status worker
+
+c) What would be the exact behaviour, if shm does not work (memory case).
+   Will this be a critical failure, or will we only experience a
+   degradation in routing decisions.
+
+d) How complete is mod_proxy_ajp/mod_proxy_balancer.
+   Port changes from mod_jk to them.
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..44fcb8f
--- /dev/null
+++ b/connectors/jk/native/apache-1.3/Makefile.apxs.in
@@ -0,0 +1,25 @@
+## 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 -f *.o *.lo *.a *.la *.so *.so.* *.slo
+	rm -rf .libs
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..98b48a3
--- /dev/null
+++ b/connectors/jk/native/apache-1.3/Makefile.in
@@ -0,0 +1,97 @@
+
+## configure should make the Makefile out of this file.
+
+srcdir=@srcdir@
+top_srcdir=@top_srcdir@
+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@
+
+top_builddir=..
+
+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 -f *.o *.lo *.a *.la *.so *.so.* *.slo
+	rm -rf .libs
+
+#
+# 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
+	../scripts/build/instdso.sh SH_LIBTOOL='$(LIBTOOL)' mod_jk.la `pwd`
+
+#
+# Common part.
+#
+mod_jk.lo: mod_jk.c
+	${SH_COMPILE} -c mod_jk.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..4a810ff
--- /dev/null
+++ b/connectors/jk/native/apache-1.3/Makefile.netware
@@ -0,0 +1,268 @@
+#
+# Makefile for mod_jk (NetWare version - gnu make)
+# created by Guenter Knauf <fuankg@apache.org>
+#
+
+# 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.37
+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-2007 The Apache Software Foundation. All rights reserved.
+COPYR	= Licensed under the Apache License, Version 2.0
+DESCR	= Apache $(AP_VERSION_STR) plugin for Tomcat $(JK_VERSION_STR)
+MTSAFE	= NO
+STACK	= 65536
+#SCREEN	= NONE
+EXPORTS	= jk_module
+#AP_PRE	= YES
+#MODULES	=
+
+# 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
+	GCC_ROOT = $(MINGNLM)
+endif
+# RM	= rm -f
+#CP	= cp -fv
+AWK	= awk
+
+# Global flags for all compilers
+CFLAGS	= $(OPT) -D$(DB) -DNETWARE -nostdinc
+
+ifeq ($(CC),mwccnlm)
+LD	= mwldnlm
+LDFLAGS	= -nostdlib $(OBJS) $(PRELUDE) $(LDLIBS) -o $@ -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"
+	LDLIBS = "$(METROWERKS)/Novell Support/libraries/runtime/mwcrtl.lib"
+#	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
+	PRELUDE = $(NDK_ROOT)/pre/prelude.o
+	LDLIBS = $(GCC_ROOT)/lib/gcc-lib/i586-netware/3.2.3/libgcc.a
+	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 ($(findstring linux,$(OSTYPE)),linux)
+DL	= '
+#-include $(NDKBASE)/nlmconv/ncpfs.inc
+endif
+
+OBJS	= \
+	$(OBJDIR)/$(TARGET).o \
+	$(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_shm.o \
+	$(OBJDIR)/jk_sockbuf.o \
+	$(OBJDIR)/jk_status.o \
+	$(OBJDIR)/jk_uri_worker_map.o \
+	$(OBJDIR)/jk_util.o \
+	$(OBJDIR)/jk_worker.o
+
+#OBJS	+= $(OBJDIR)/ap_snprintf.o
+
+vpath %.c . $(JKCOMMON) $(AP_HOME)/src/os/netware
+
+
+all: $(OBJDIR) $(OBJDIR)/version.inc $(OBJDIR)/$(TARGET).nlm 
+
+$(OBJDIR)/%.o: %.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 ../../support/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) $(PRELUDE)
+$(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 "$*"$(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
+	@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 @$(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) >> $@
+ifdef LDLIBS
+	@echo $(DL)input $(LDLIBS)$(DL) >> $@
+endif
+	@echo $(DL)output $*.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/Makefile.vc b/connectors/jk/native/apache-1.3/Makefile.vc
new file mode 100644
index 0000000..1a63c94
--- /dev/null
+++ b/connectors/jk/native/apache-1.3/Makefile.vc
@@ -0,0 +1,248 @@
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE 
+NULL=nul
+!ENDIF 
+
+!IFDEF EAPI
+OUTDIR=.\ReleaseSSL
+INTDIR=.\ReleaseSSL
+DEF_EAPI=/D "EAPI"
+!ELSE
+OUTDIR=.\Release
+INTDIR=.\Release
+DEF_EAPI=
+!ENDIF
+
+
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+ALL : "$(OUTDIR)\mod_jk.so"
+
+
+CLEAN :
+	-@erase "$(INTDIR)\jk_ajp12_worker.obj"
+	-@erase "$(INTDIR)\jk_ajp13.obj"
+	-@erase "$(INTDIR)\jk_ajp13_worker.obj"
+	-@erase "$(INTDIR)\jk_ajp14.obj"
+	-@erase "$(INTDIR)\jk_ajp14_worker.obj"
+	-@erase "$(INTDIR)\jk_ajp_common.obj"
+	-@erase "$(INTDIR)\jk_connect.obj"
+	-@erase "$(INTDIR)\jk_context.obj"
+	-@erase "$(INTDIR)\jk_jni_worker.obj"
+	-@erase "$(INTDIR)\jk_lb_worker.obj"
+	-@erase "$(INTDIR)\jk_map.obj"
+	-@erase "$(INTDIR)\jk_md5.obj"
+	-@erase "$(INTDIR)\jk_msg_buff.obj"
+	-@erase "$(INTDIR)\jk_pool.obj"
+	-@erase "$(INTDIR)\jk_shm.obj"
+	-@erase "$(INTDIR)\jk_sockbuf.obj"
+	-@erase "$(INTDIR)\jk_status.obj"
+	-@erase "$(INTDIR)\jk_uri_worker_map.obj"
+	-@erase "$(INTDIR)\jk_util.obj"
+	-@erase "$(INTDIR)\jk_worker.obj"
+	-@erase "$(INTDIR)\mod_jk.obj"
+	-@erase "$(INTDIR)\mod_jk_src.idb"
+	-@erase "$(INTDIR)\mod_jk_src.pdb"
+	-@erase "$(OUTDIR)\mod_jk.exp"
+	-@erase "$(OUTDIR)\mod_jk.lib"
+	-@erase "$(OUTDIR)\mod_jk.pdb"
+	-@erase "$(OUTDIR)\mod_jk.so"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_jk.bsc" 
+BSC32_SBRS= \
+	
+LINK32=link.exe
+LINK32_FLAGS=ApacheCore.lib kernel32.lib user32.lib advapi32.lib ws2_32.lib mswsock.lib /nologo /base:"0x6A6B0000" /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_jk.pdb" /debug /machine:I386 /out:"$(OUTDIR)\mod_jk.so" /implib:"$(OUTDIR)\mod_jk.lib" /libpath:"$(APACHE1_HOME)\lib" /libpath:"$(APACHE1_HOME)\libexec" /opt:ref 
+LINK32_OBJS= \
+	"$(INTDIR)\jk_ajp12_worker.obj" \
+	"$(INTDIR)\jk_ajp13.obj" \
+	"$(INTDIR)\jk_ajp13_worker.obj" \
+	"$(INTDIR)\jk_ajp14.obj" \
+	"$(INTDIR)\jk_ajp14_worker.obj" \
+	"$(INTDIR)\jk_ajp_common.obj" \
+	"$(INTDIR)\jk_connect.obj" \
+	"$(INTDIR)\jk_context.obj" \
+	"$(INTDIR)\jk_jni_worker.obj" \
+	"$(INTDIR)\jk_lb_worker.obj" \
+	"$(INTDIR)\jk_map.obj" \
+	"$(INTDIR)\jk_md5.obj" \
+	"$(INTDIR)\jk_msg_buff.obj" \
+	"$(INTDIR)\jk_pool.obj" \
+	"$(INTDIR)\jk_shm.obj" \
+	"$(INTDIR)\jk_sockbuf.obj" \
+	"$(INTDIR)\jk_status.obj" \
+	"$(INTDIR)\jk_uri_worker_map.obj" \
+	"$(INTDIR)\jk_util.obj" \
+	"$(INTDIR)\jk_worker.obj" \
+	"$(INTDIR)\mod_jk.obj"
+
+"$(OUTDIR)\mod_jk.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+CPP_PROJ=/nologo /MD /W3 /Zi /O2 /I "..\common" /I "$(APACHE1_HOME)\include" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" $(DEF_EAPI) /D "MOD_JK_EXPORTS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_jk_src" /FD /c 
+
+.c{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cpp{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cxx{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.c{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cpp{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cxx{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 
+
+
+SOURCE=..\common\jk_ajp12_worker.c
+
+"$(INTDIR)\jk_ajp12_worker.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_ajp13.c
+
+"$(INTDIR)\jk_ajp13.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_ajp13_worker.c
+
+"$(INTDIR)\jk_ajp13_worker.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_ajp14.c
+
+"$(INTDIR)\jk_ajp14.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_ajp14_worker.c
+
+"$(INTDIR)\jk_ajp14_worker.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_ajp_common.c
+
+"$(INTDIR)\jk_ajp_common.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_connect.c
+
+"$(INTDIR)\jk_connect.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_context.c
+
+"$(INTDIR)\jk_context.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_jni_worker.c
+
+"$(INTDIR)\jk_jni_worker.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_lb_worker.c
+
+"$(INTDIR)\jk_lb_worker.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_map.c
+
+"$(INTDIR)\jk_map.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_md5.c
+
+"$(INTDIR)\jk_md5.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_msg_buff.c
+
+"$(INTDIR)\jk_msg_buff.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_pool.c
+
+"$(INTDIR)\jk_pool.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_shm.c
+
+"$(INTDIR)\jk_shm.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_sockbuf.c
+
+"$(INTDIR)\jk_sockbuf.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_status.c
+
+"$(INTDIR)\jk_status.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_uri_worker_map.c
+
+"$(INTDIR)\jk_uri_worker_map.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_util.c
+
+"$(INTDIR)\jk_util.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_worker.c
+
+"$(INTDIR)\jk_worker.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\mod_jk.c
+
+"$(INTDIR)\mod_jk.obj" : $(SOURCE) "$(INTDIR)"
diff --git a/connectors/jk/native/apache-1.3/NWGNUmakefile b/connectors/jk/native/apache-1.3/NWGNUmakefile
new file mode 100644
index 0000000..3625360
--- /dev/null
+++ b/connectors/jk/native/apache-1.3/NWGNUmakefile
@@ -0,0 +1,27 @@
+#
+# 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)\NWGNUhead.inc
+
+#
+# build this level's files
+
+ifeq "$(wildcard NWGNUmakefile.mak)" "NWGNUmakefile.mak" 
+include NWGNUmakefile.mak
+endif
+
+#
+# You can use this target if all that is needed is to copy files to the
+# installation area
+#
+install :: nlms FORCE
+
diff --git a/connectors/jk/native/apache-1.3/NWGNUmakefile.mak b/connectors/jk/native/apache-1.3/NWGNUmakefile.mak
new file mode 100644
index 0000000..43c57a9
--- /dev/null
+++ b/connectors/jk/native/apache-1.3/NWGNUmakefile.mak
@@ -0,0 +1,294 @@
+#
+# Makefile for mod_jk (NetWare version - gnu make)
+# created by Guenter Knauf <fuankg@apache.org>
+#
+
+#
+# Make sure all needed macro's are defined
+#
+
+# Edit the path below to point to the base of your NetWare Java SDK.
+ifndef JAVA_HOME
+JAVA_HOME = c:/projects/sdks/java-nw
+endif
+
+LDLIBS = -l"$(METROWERKS)/Novell Support/libraries/runtime/mwcrtl.lib"
+
+JKCOMMON = ../common
+
+#
+# Get the 'head' of the build environment if necessary.  This includes default
+# targets and paths to tools
+#
+
+ifndef EnvironmentDefined
+include $(AP_WORK)\NWGNUhead.inc
+endif
+
+#
+# These directories will be at the beginning of the include list, followed by
+# INCDIRS
+#
+XINCDIRS	+= \
+			$(JKCOMMON) \
+			$(JAVA_HOME)/include \
+			$(JAVA_HOME)/include/netware \
+			$(SRC)\include \
+			$(NWOS) \
+			$(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		+= \
+			$(LDLIBS) \
+			$(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 $(AP_VERSION_STR) plugin for 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 = Licensed under the Apache License Version 2.0
+
+#
+# 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)\NWGNUenvironment.inc
+#
+NLM_VERSION	= $(JK_VERSION)
+
+#
+# If this is specified, it will override the default of 64K
+#
+NLM_STACK_SIZE	= 65536
+
+#
+# If this is specified it will be used by the link '-entry' directive
+#
+NLM_ENTRY_SYM	= _lib_start
+#NLM_ENTRY_SYM	= _lib_start_ws
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM	= _lib_stop
+#NLM_EXIT_SYM	= _lib_stop_ws
+
+#
+# If this is specified it will be used by the link '-flags' directive
+#
+NLM_FLAGS	=
+
+#
+# Declare all target files (you must add your files here)
+#
+
+#
+# If there is an NLM target, put it here
+#
+TARGET_nlm = \
+	$(OBJDIR)/$(NLM_NAME).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)/$(NLM_NAME).o \
+	$(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_shm.o \
+	$(OBJDIR)/jk_sockbuf.o \
+	$(OBJDIR)/jk_status.o \
+	$(OBJDIR)/jk_uri_worker_map.o \
+	$(OBJDIR)/jk_util.o \
+	$(OBJDIR)/jk_worker.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 = \
+	$(NWOS)/$(OBJDIR)/libpre.o \
+	$(EOLIST)
+
+#	$(NWOS)/$(OBJDIR)/libprews.o
+
+#
+# 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 = \
+	@ApacheCore.imp \
+	@threads.imp \
+	@clib.imp \
+	@nlmlib.imp \
+	@socklib.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)\NWGNUhead.inc for examples)
+#
+install :: nlms FORCE
+	copy $(OBJDIR)\$(NLM_NAME).nlm $(INSTALL)\Apache\modules
+
+#
+# Any specialized rules here
+#
+vpath %.c . $(JKCOMMON) $(SNPRINTF)
+
+$(OBJDIR)/version.inc: $(JKCOMMON)/jk_version.h $(SRC)/include/httpd.h $(OBJDIR)
+	@echo Creating $@
+	@awk -f ../../support/get_ver.awk $<  $(SRC)/include/httpd.h > $@
+
+# 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)/NWGNUtail.inc
+
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..b504d82
--- /dev/null
+++ b/connectors/jk/native/apache-1.3/mod_jk.c
@@ -0,0 +1,2985 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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 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 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"
+
+/*
+ * 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"
+#include "jk_ajp13.h"
+#include "jk_shm.h"
+#include "jk_url.h"
+
+#define JK_LOG_DEF_FILE             ("logs/mod_jk.log")
+#define JK_SHM_DEF_FILE             ("logs/jk-runtime-status")
+#define JK_ENV_HTTPS                ("HTTPS")
+#define JK_ENV_CERTS                ("SSL_CLIENT_CERT")
+#define JK_ENV_CIPHER               ("SSL_CIPHER")
+#define JK_ENV_SESSION              ("SSL_SESSION_ID")
+#define JK_ENV_KEY_SIZE             ("SSL_CIPHER_USEKEYSIZE")
+#define JK_ENV_CERTCHAIN_PREFIX     ("SSL_CLIENT_CERT_CHAIN_")
+#define JK_ENV_WORKER_NAME          ("JK_WORKER_NAME")
+#define JK_NOTE_WORKER_NAME         ("JK_WORKER_NAME")
+#define JK_NOTE_WORKER_TYPE         ("JK_WORKER_TYPE")
+#define JK_NOTE_REQUEST_DURATION    ("JK_REQUEST_DURATION")
+#define JK_NOTE_WORKER_ROUTE        ("JK_WORKER_ROUTE")
+#define JK_HANDLER          ("jakarta-servlet")
+#define JK_MAGIC_TYPE       ("application/x-jakarta-servlet")
+#define NULL_FOR_EMPTY(x)   ((x && !strlen(x)) ? NULL : x)
+#define STRNULL_FOR_NULL(x) ((x) ? (x) : "(null)")
+
+/*
+ * 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;
+#ifdef WIN32
+extern __declspec(dllimport) module dir_module;
+#else
+extern module dir_module;
+#endif
+
+static int xfer_flags = (O_WRONLY | O_APPEND | O_CREAT);
+#if defined(OS2) || defined(WIN32) || defined(NETWARE)
+/* OS/2 dosen't support users and groups */
+static mode_t xfer_mode = (S_IREAD | S_IWRITE);
+#else
+static mode_t xfer_mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+#endif
+
+/*
+ * Environment variable forward object
+ */
+typedef struct
+{
+    int has_default;
+    char *name;
+    char *value;
+} envvar_item;
+
+/*
+ * Configuration object for the mod_jk module.
+ */
+typedef struct
+{
+
+    /*
+     * Log stuff
+     */
+    char *log_file;
+    int log_fd;
+    int log_level;
+    jk_logger_t *log;
+
+    /*
+     * Worker stuff
+     */
+    jk_map_t *worker_properties;
+    char *worker_file;
+    char *mount_file;
+    int mount_file_reload;
+    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 *stamp_format_string;
+    char *format_string;
+    array_header *format;
+
+    /*
+     * Setting target worker via environment
+     */
+   char *worker_indicator;
+
+    /*
+     * SSL Support
+     */
+    int ssl_enable;
+    char *https_indicator;
+    char *certs_indicator;
+    char *cipher_indicator;
+    char *session_indicator;
+    char *key_size_indicator;
+    char *certchain_prefix;     /* Client certificate chain prefix */
+
+    /*
+     * Jk Options
+     */
+    int options;
+    int exclude_options;
+
+    int strip_session;
+    /*
+     * Environment variables support
+     */
+    int envvars_in_use;
+    table *envvars;
+    table *envvars_def;
+    array_header *envvar_items;
+
+    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 table *jk_log_fds = NULL;
+static jk_worker_env_t worker_env;
+static char *jk_shm_file = NULL;
+static size_t jk_shm_size = JK_SHM_DEF_SIZE;
+
+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);
+
+static void JK_METHOD ws_add_log_items(jk_ws_service_t *s,
+                                       const char *const *log_names,
+                                       const char *const *log_values,
+                                       unsigned num_of_log_items);
+
+/* 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;
+            }
+            /* reset timeout after successful read */
+            ap_reset_timeout(p->r);
+            return JK_TRUE;
+        }
+    }
+    return JK_FALSE;
+}
+
+static void JK_METHOD ws_flush(jk_ws_service_t *s)
+{
+    if (s && s->ws_private) {
+        apache_private_data_t *p = s->ws_private;
+        BUFF *bf = p->r->connection->client;
+        ap_bflush(bf);
+    }
+}
+
+/*
+ * 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) {
+            char *buf = (char *)b;
+            int w = (int)len;
+            int r = 0;
+
+            if (!p->response_started) {
+                if (main_log)
+                    jk_log(main_log, JK_LOG_INFO,
+                           "Write without start, starting with defaults");
+                if (!s->start_response(s, 200, NULL, NULL, NULL, 0)) {
+                    return JK_FALSE;
+                }
+            }
+
+            if (p->r->header_only) {
+                BUFF *bf = p->r->connection->client;
+                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;
+                }
+
+            }
+            if (len && p->r->connection->aborted) {
+                /* Fail if there is something left to send and
+                 * the connection was aborted by the client
+                 */
+                return JK_FALSE;
+            }
+        }
+
+        return JK_TRUE;
+    }
+    return JK_FALSE;
+}
+
+static void JK_METHOD ws_add_log_items(jk_ws_service_t *s,
+                                       const char *const *log_names,
+                                       const char *const *log_values,
+                                       unsigned num_of_log_items)
+{
+    unsigned h;
+    apache_private_data_t *p = s->ws_private;
+    request_rec *r = p->r;
+
+    for (h = 0; h < num_of_log_items; h++) {
+        if (log_names[h] && log_values[h]) {
+            ap_table_setn(r->notes, log_names[h], log_values[h]);
+        }
+    }
+}
+
+/* ====================================================================== */
+/* 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);
+    if ( s ) {
+        ap_log_error(file, line, level, NULL, 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->route = NULL;        /* Used for sticky session routing */
+    int size;
+
+    /* Copy in function pointers (which are really methods) */
+    s->start_response = ws_start_response;
+    s->read = ws_read;
+    s->write = ws_write;
+    s->flush = ws_flush;
+    s->add_log_items = ws_add_log_items;
+
+    /* 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);
+
+    if (conf->options & JK_OPT_FWDLOCAL)
+        s->remote_addr = NULL_FOR_EMPTY(r->connection->local_ip);
+    else
+        s->remote_addr = NULL_FOR_EMPTY(r->connection->remote_ip);
+
+    if (conf->options & JK_OPT_FLUSHPACKETS)
+        s->flush_packets = 1;
+    else
+        s->flush_packets = 0;
+    if (conf->options & JK_OPT_FLUSHEADER)
+        s->flush_header = 1;
+    else
+        s->flush_header = 0;
+
+    if (conf->options & JK_OPT_DISABLEREUSE)
+        s->disable_reuse = 1;
+    else
+        s->disable_reuse = 0;
+
+    /* get server name */
+    /* s->server_name  = (char *)(r->hostname ? r->hostname : r->server->server_hostname); */
+    /* XXX : a 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 : a 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;
+
+    /* Dump all connection param so we can trace what's going to
+     * the remote tomcat
+     */
+    if (JK_IS_DEBUG_LEVEL(conf->log)) {
+        jk_log(conf->log, JK_LOG_DEBUG,
+               "Service protocol=%s method=%s host=%s addr=%s name=%s port=%d auth=%s user=%s laddr=%s raddr=%s",
+               STRNULL_FOR_NULL(s->protocol),
+               STRNULL_FOR_NULL(s->method),
+               STRNULL_FOR_NULL(s->remote_host),
+               STRNULL_FOR_NULL(s->remote_addr),
+               STRNULL_FOR_NULL(s->server_name),
+               s->server_port,
+               STRNULL_FOR_NULL(s->auth_type),
+               STRNULL_FOR_NULL(s->remote_user),
+               STRNULL_FOR_NULL(r->connection->local_ip),
+               STRNULL_FOR_NULL(r->connection->remote_ip));
+    }
+
+    /*
+     * 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_FWDURIPROXY:
+        size = strlen(r->uri);
+        s->req_uri = ap_palloc(r->pool, size * 3 + 1);
+        jk_canonenc(s->req_uri, r->uri, size, enc_path, 0,
+                    JK_PROXYREQ_REVERSE);
+        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 (conf->options & JK_OPT_FWDCERTCHAIN) {
+                    array_header *t = ap_table_elts(r->subprocess_env);
+                    if (t && t->nelts) {
+                        int i;
+                        table_entry *elts = (table_entry *) t->elts;
+                        array_header *certs = ap_make_array(r->pool, 1,
+                                                            sizeof(char *));
+                        *(const char **)ap_push_array(certs) = s->ssl_cert;
+                        for (i = 0; i < t->nelts; i++) {
+                            if (!elts[i].key)
+                                continue;
+                            if (!strncasecmp(elts[i].key,
+                                             conf->certchain_prefix,
+                                             strlen(conf->certchain_prefix)))
+                            *(const char **)ap_push_array(certs) = elts[i].val;
+                        }
+                        s->ssl_cert = ap_array_pstrcat(r->pool, certs, '\0');
+                     }
+                }
+
+                if (s->ssl_cert) {
+                    s->ssl_cert_len = strlen(s->ssl_cert);
+                    if (JK_IS_DEBUG_LEVEL(conf->log)) {
+                        jk_log(conf->log, JK_LOG_DEBUG,
+                               "SSL client certificate (%d bytes): %s",
+                               s->ssl_cert_len, 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) {
+            const array_header *t = conf->envvar_items;
+            if (t && t->nelts) {
+                int i;
+                int j = 0;
+                envvar_item *elts = (envvar_item *) 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 - j] = elts[i].name;
+                    s->attributes_values[i - j] =
+                        (char *)ap_table_get(r->subprocess_env, elts[i].name);
+                    if (!s->attributes_values[i - j]) {
+                        if (elts[i].has_default) {
+                            s->attributes_values[i - j] = elts[i].value;
+                        }
+                        else {
+                            s->attributes_values[i - j] = "";
+                            s->attributes_names[i - j] = "";
+                            j++;
+                        }
+                    }
+                }
+
+                s->num_attributes = t->nelts - j;
+            }
+        }
+    }
+
+    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;
+                if (need_content_length_header &&
+                    !strcasecmp(s->headers_names[i], "content-length")) {
+                    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++;
+        }
+    }
+    s->uw_map = conf->uw_map;
+    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)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *) ap_get_module_config(s->module_config,
+                                                  &jk_module);
+    const char *c, *w;
+
+    if (worker != NULL && cmd->path == NULL ) {
+        c = context;
+        w = worker;
+    }
+    else if (worker == NULL && cmd->path != NULL) {
+        c = cmd->path;
+        w = context;
+    }
+    else {
+        if (worker == NULL)
+            return "JkMount needs a path when not defined in a location";
+        else
+            return "JkMount can not have a path when defined in a location";
+    }
+
+    if (c[0] != '/')
+        return "JkMount context should start with /";
+
+    /*
+     * Add the new worker to the alias map.
+     */
+    jk_map_put(conf->uri_to_context, c, w, NULL);
+    return NULL;
+}
+
+/*
+ * JkUnMount directive handling
+ *
+ * JkUnMount URI(context) worker
+ */
+
+static const char *jk_unmount_context(cmd_parms * cmd,
+                                      void *dummy,
+                                      const char *context,
+                                      const char *worker)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *) ap_get_module_config(s->module_config,
+                                                  &jk_module);
+    char *uri;
+    const char *c, *w;
+
+    if (worker != NULL && cmd->path == NULL ) {
+        c = context;
+        w = worker;
+    }
+    else if (worker == NULL && cmd->path != NULL) {
+        c = cmd->path;
+        w = context;
+    }
+    else {
+        if (worker == NULL)
+            return "JkUnMount needs a path when not defined in a location";
+        else
+            return "JkUnMount can not have a path when defined in a location";
+    }
+    if (c[0] != '/')
+        return "JkUnMount context should start with /";
+    uri = ap_pstrcat(cmd->temp_pool, "!", c, NULL);
+    /*
+     * Add the new worker to the alias map.
+     */
+    jk_map_put(conf->uri_to_context, uri, w, NULL);
+    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.
+     */
+    jk_map_put(conf->automount, worker, virtualhost, NULL);
+    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 absolute 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;
+}
+
+/*
+ * JkMountFile Directive Handling
+ *
+ * JkMountFile file
+ */
+
+static const char *jk_set_mount_file(cmd_parms * cmd,
+                                     void *dummy, char *mount_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 absolute path (ap_server_root_relative does the ap_pstrdup) */
+    conf->mount_file = ap_server_root_relative(cmd->pool, mount_file);
+
+#ifdef CHROOTED_APACHE
+    ap_server_strip_chroot(conf->mount_file, 0);
+#endif
+
+    if (conf->mount_file == NULL)
+        return "JkMountFile file name invalid";
+
+    if (stat(conf->mount_file, &statbuf) == -1)
+        return "Can't find the mount file specified";
+
+    return NULL;
+}
+
+/*
+ * JkMountFileReload Directive Handling
+ *
+ * JkMountFileReload seconds
+ */
+
+static const char *jk_set_mount_file_reload(cmd_parms * cmd,
+                                            void *dummy, char *mount_file_reload)
+{
+    server_rec *s = cmd->server;
+    int interval;
+
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *) ap_get_module_config(s->module_config,
+                                                  &jk_module);
+
+    interval = atoi(mount_file_reload);
+    if (interval < 0) {
+        interval = 0;
+    }
+
+    conf->mount_file_reload = interval;
+
+    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 absolute path */
+    if (*log_file != '|') {
+        conf->log_file = ap_server_root_relative(cmd->pool, log_file);
+
+#ifdef CHROOTED_APACHE
+        ap_server_strip_chroot(conf->log_file, 0);
+#endif
+
+    }
+    else
+        conf->log_file = ap_pstrdup(cmd->pool, log_file);
+
+    if (conf->log_file == NULL)
+        return "JkLogFile file name invalid";
+
+    return NULL;
+}
+
+/*
+ * JkShmFile Directive Handling
+ *
+ * JkShmFile file
+ */
+
+static const char *jk_set_shm_file(cmd_parms * cmd,
+                                   void *dummy, char *shm_file)
+{
+
+    /* we need an absolute path */
+    jk_shm_file = ap_server_root_relative(cmd->pool, shm_file);
+
+#ifdef CHROOTED_APACHE
+    ap_server_strip_chroot(jk_shm_file, 0);
+#endif
+
+    if (jk_shm_file == shm_file)
+        jk_shm_file = ap_pstrdup(cmd->pool, shm_file);
+
+    if (jk_shm_file == NULL)
+        return "JkShmFile file name invalid";
+
+    return NULL;
+}
+
+/*
+ * JkShmSize Directive Handling
+ *
+ * JkShmSize size in kilobytes
+ */
+
+static const char *jk_set_shm_size(cmd_parms * cmd,
+                                   void *dummy, const char *shm_size)
+{
+    int sz = 0;
+    /* we need an absolute path */
+    sz = atoi(shm_size) * 1024;
+    if (sz < JK_SHM_DEF_SIZE)
+        sz = JK_SHM_DEF_SIZE;
+    else
+        sz = JK_SHM_ALIGN(sz);
+    jk_shm_size = (size_t)sz;
+    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)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *) ap_get_module_config(s->module_config,
+                                                  &jk_module);
+
+    conf->stamp_format_string = ap_pstrdup(cmd->pool, 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;
+}
+
+/*
+ * JkStripSession directive handling
+ *
+ * JkStripSession On/Off
+ */
+
+static const char *jk_set_strip_session(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->strip_session = flag ? JK_TRUE : JK_FALSE;
+
+    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, 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_NOTE_WORKER_NAME);
+}
+
+static const char *log_worker_route(request_rec * r, char *a)
+{
+    return ap_table_get(r->notes, JK_NOTE_WORKER_ROUTE);
+}
+
+static const char *log_request_duration(request_rec * r, char *a)
+{
+    return ap_table_get(r->notes, JK_NOTE_REQUEST_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}, {
+    'R', log_worker_route}, {
+    '\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;
+        }
+    }
+
+    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)
+{
+    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);
+
+    return NULL;
+}
+
+/*
+ * JkWorkerIndicator Directive Handling
+ *
+ * JkWorkerIndicator JkWorker
+ */
+
+static const char *jk_set_worker_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->worker_indicator = ap_pstrdup(cmd->pool, indicator);
+
+    return NULL;
+}
+
+/*
+ * 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;
+}
+
+/*
+ * JkCERTCHAINPrefix Directive Handling
+ *
+ * JkCERTCHAINPrefix SSL_CLIENT_CERT_CHAIN_
+ */
+
+static const char *jk_set_certchain_prefix(cmd_parms * cmd,
+                                           void *dummy, const char *prefix)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *) ap_get_module_config(s->module_config,
+                                                  &jk_module);
+
+    conf->certchain_prefix = ap_pstrdup(cmd->pool, prefix);
+
+    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
+ * +ForwardSSLCertChain      => Forward SSL certificate chain
+ * -ForwardSSLCertChain      => Don't forward SSL certificate chain
+ */
+
+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 (action == '-' && !strncasecmp(w, "ForwardURI", strlen("ForwardURI")))
+            return ap_pstrcat(cmd->pool, "JkOptions: Illegal option '-", w,
+                               "': ForwardURI* options can not be disabled", NULL);
+
+        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, "ForwardURIProxy")) {
+            opt = JK_OPT_FWDURIPROXY;
+            mask = JK_OPT_FWDURIMASK;
+        }
+        else if (!strcasecmp(w, "ForwardDirectories")) {
+            opt = JK_OPT_FWDDIRS;
+        }
+        else if (!strcasecmp(w, "ForwardLocalAddress")) {
+            opt = JK_OPT_FWDLOCAL;
+        }
+        else if (!strcasecmp(w, "FlushPackets")) {
+            opt = JK_OPT_FLUSHPACKETS;
+        }
+        else if (!strcasecmp(w, "FlushHeader")) {
+            opt = JK_OPT_FLUSHEADER;
+        }
+        else if (!strcasecmp(w, "DisableReuse")) {
+            opt = JK_OPT_DISABLEREUSE;
+        }
+        else if (!strcasecmp(w, "ForwardSSLCertChain")) {
+            opt = JK_OPT_FWDCERTCHAIN;
+        }
+        else
+            return ap_pstrcat(cmd->pool, "JkOptions: Illegal option '", w,
+                              "'", NULL);
+
+        conf->options &= ~mask;
+
+        if (action == '-') {
+            conf->exclude_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;
+
+    /* env_name is mandatory, default_value is optional.
+     * No value means send the attribute only, if the env var is set during runtime.
+     */
+    ap_table_setn(conf->envvars, env_name, default_value ? default_value : "");
+    ap_table_setn(conf->envvars_def, env_name, default_value ? "1" : "0");
+
+    return NULL;
+}
+
+/*
+ * JkWorkerProperty Directive Handling
+ *
+ * JkWorkerProperty name=value
+ */
+
+static const char *jk_set_worker_property(cmd_parms * cmd,
+                                          void *dummy,
+                                          const char *line)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *) ap_get_module_config(s->module_config,
+                                                  &jk_module);
+
+    if (jk_map_read_property(conf->worker_properties, line, 1, conf->log) == JK_FALSE)
+        return ap_pstrcat(cmd->temp_pool, "Invalid JkWorkerProperty ", line, NULL);
+
+    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 Tomcat servlet containers"},
+
+    /*
+     * JkMountFile specifies a full path to the location of the
+     * uriworker properties file.
+     *
+     * This file defines the different mapping for workers used by apache
+     * to redirect servlet requests.
+     */
+    {"JkMountFile", jk_set_mount_file, NULL, RSRC_CONF, TAKE1,
+     "the name of a mount file for the Tomcat servlet uri mappings"},
+
+    /*
+     * JkMountFileReload specifies the reload check interval for the
+     * uriworker properties file.
+     *
+     * Default value is: JK_URIMAP_DEF_RELOAD
+     */
+    {"JkMountFileReload", jk_set_mount_file_reload, NULL, RSRC_CONF, TAKE1,
+     "the reload check interval of the mount file"},
+
+    /*
+     * JkAutoMount specifies that the list of handled URLs must be
+     * 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|ACCESS_CONF, TAKE12,
+     "A mount point from a context to a servlet-engine worker"},
+
+    /*
+     * JkUnMount unmounts a url prefix to a worker (the worker need to be
+     * defined in the worker properties file.
+     */
+    {"JkUnMount", jk_unmount_context, NULL, RSRC_CONF|ACCESS_CONF, TAKE12,
+     "A no 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"},
+
+    /*
+     * JkStripSession specifies if mod_jk should strip the ;jsessionid
+     * from the unmapped urls
+     */
+    {"JkStripSession", jk_set_strip_session, NULL, RSRC_CONF, FLAG,
+     "Should the server strip the jsessionid from unmapped URLs"},
+
+    /*
+     * 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 mod_jk module log file"},
+    {"JkShmFile", jk_set_shm_file, NULL, RSRC_CONF, TAKE1,
+     "Full path to the mod_jk module shared memory file"},
+    {"JkShmSize", jk_set_shm_size, NULL, RSRC_CONF, TAKE1,
+     "Size of the shared memory file in KBytes"},
+    {"JkLogLevel", jk_set_log_level, NULL, RSRC_CONF, TAKE1,
+     "The mod_jk module log level, can be debug, info, request, error, or emerg"},
+    {"JkLogStampFormat", jk_set_log_fmt, NULL, RSRC_CONF, TAKE1,
+     "The mod_jk module log format, follow strftime synthax"},
+    {"JkRequestLogFormat", jk_set_request_log_format, NULL, RSRC_CONF, TAKE1,
+     "The 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 mod_jk module automatic context apache alias directory"},
+
+    /*
+     * Enable worker name to be set in an environment variable.
+     * this way one can use LocationMatch together with mod_end,
+     * mod_setenvif and mod_rewrite to set the target worker.
+     * Use this in combination with SetHandler jakarta-servlet to
+     * make mod_jk the handler for the request.
+     *
+     */
+    {"JkWorkerIndicator", jk_set_worker_indicator, NULL, RSRC_CONF, TAKE1,
+     "Name of the Apache environment that contains the worker name"},
+
+    /*
+     * 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"},
+    {"JkCERTCHAINPrefix", jk_set_certchain_prefix, NULL, RSRC_CONF, TAKE1,
+     "Name of the Apache environment (prefix) that contains SSL client chain certificates"},
+    {"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
+     * +ForwardSSLCertChain      => Forward SSL certificate chain
+     * -ForwardSSLCertChain      => Don't forward SSL certificate chain
+     */
+    {"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, TAKE12,
+     "Adds a name of environment variable and an optional value "
+     "that should be sent to servlet-engine"},
+
+    {"JkWorkerProperty", jk_set_worker_property, NULL, RSRC_CONF, RAW_ARGS,
+     "Set workers.properties formated directive"},
+
+    {NULL}
+};
+
+/* ====================================================================== */
+/* The JK module handlers                                                 */
+/* ====================================================================== */
+
+/*
+ * Called to handle a single request.
+ */
+static int jk_handler(request_rec * r)
+{
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *) ap_get_module_config(r->server->
+                                                  module_config,
+                                                  &jk_module);
+    /* Retrieve the worker name stored by jk_translate() */
+    const char *worker_name = ap_table_get(r->notes, JK_NOTE_WORKER_NAME);
+    int rc;
+
+    JK_TRACE_ENTER(conf->log);
+
+    if (ap_table_get(r->subprocess_env, "no-jk")) {
+        if (JK_IS_DEBUG_LEVEL(conf->log))
+            jk_log(conf->log, JK_LOG_DEBUG,
+                   "Into handler no-jk env var detected for uri=%s, declined",
+                   r->uri);
+        JK_TRACE_EXIT(conf->log);
+        return DECLINED;
+    }
+
+    if (r->proxyreq) {
+        jk_log(conf->log, JK_LOG_ERROR,
+               "Request has proxyreq flag set in mod_jk handler - aborting.");
+        JK_TRACE_EXIT(conf->log);
+        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))) {
+        jk_log(conf->log, JK_LOG_ERROR,
+               "Could not setup client_block for chunked encoding - aborting");
+        JK_TRACE_EXIT(conf->log);
+        return rc;
+    }
+
+    if (worker_name == NULL && r->handler && !strcmp(r->handler, JK_HANDLER)) {
+        /* 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 (JK_IS_DEBUG_LEVEL(conf->log))
+                jk_log(conf->log, JK_LOG_DEBUG,
+                       "Retrieving environment %s", conf->worker_indicator);
+        worker_name = (char *)ap_table_get(r->subprocess_env, conf->worker_indicator);
+        if (worker_name) {
+          /* The JkWorkerIndicator environment variable has
+           * been used to explicitely set the worker without JkMount.
+           * This is useful in combination with LocationMatch or mod_rewrite.
+           */
+            if (JK_IS_DEBUG_LEVEL(conf->log))
+                jk_log(conf->log, JK_LOG_DEBUG,
+                       "Retrieved worker (%s) from env %s for %s",
+                       worker_name, conf->worker_indicator, r->uri);
+        }
+        else 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.worker_list[0];
+            if (JK_IS_DEBUG_LEVEL(conf->log))
+                jk_log(conf->log, JK_LOG_DEBUG,
+                       "Single worker (%s) configuration for %s",
+                       worker_name, r->uri);
+        }
+        else if (worker_env.num_of_workers) {
+            worker_name = worker_env.worker_list[0];
+            if (JK_IS_DEBUG_LEVEL(conf->log))
+                jk_log(conf->log, JK_LOG_DEBUG,
+                       "Using first worker (%s) from %d workers for %s",
+                       worker_name, worker_env.num_of_workers, r->uri);
+        }
+    }
+
+    if (worker_name) {
+        jk_worker_t *worker;
+
+        worker = wc_get_worker_for_name(worker_name, conf->log);
+
+        if (worker) {
+#ifndef NO_GETTIMEOFDAY
+            struct timeval tv_begin, tv_end;
+#endif
+            int rc = JK_FALSE;
+            int is_error = JK_HTTP_SERVER_ERROR;
+            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;
+
+            wc_maintain(conf->log);
+            jk_init_ws_service(&s);
+
+            /* Update retries for this worker */
+            s.retries = worker->retries;
+            s.ws_private = &private_data;
+            s.pool = &private_data.p;
+            ap_table_setn(r->notes, JK_NOTE_WORKER_TYPE,
+                          wc_get_name_for_type(worker->type, conf->log));
+#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, conf->log)) {
+                    rc = end->service(end, &s, conf->log, &is_error);
+                    end->done(&end, conf->log);
+
+                    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;
+                            }
+                        }
+                    }
+                }
+                if (conf->format != NULL) {
+#ifndef NO_GETTIMEOFDAY
+                    long micro, seconds;
+                    char *duration = NULL;
+                    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, "%.1ld.%.6ld", seconds, micro);
+                    ap_table_setn(r->notes, JK_NOTE_REQUEST_DURATION, duration);
+#endif
+                    if (s.route && *s.route)
+                        ap_table_setn(r->notes, JK_NOTE_WORKER_ROUTE, s.route);
+                    request_log_transaction(r, conf);
+                }
+            }
+            else {
+                jk_log(conf->log, JK_LOG_ERROR, "Could not init service"
+                       " for worker=%s",
+                       worker_name);
+                jk_close_pool(&private_data.p);
+                JK_TRACE_EXIT(conf->log);
+                return is_error;
+            }
+            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) {
+                    jk_log(conf->log, JK_LOG_INFO, "No body with status=%d"
+                           " for worker=%s",
+                           r->status, worker_name);
+                    JK_TRACE_EXIT(conf->log);
+                    return r->status;
+                }
+                if (JK_IS_DEBUG_LEVEL(conf->log))
+                    jk_log(conf->log, JK_LOG_DEBUG, "Service finished"
+                           " with status=%d for worker=%s",
+                           r->status, worker_name);
+                JK_TRACE_EXIT(conf->log);
+                return OK;      /* NOT r->status, even if it has changed. */
+            }
+            else if (rc == JK_CLIENT_ERROR) {
+                if (is_error != HTTP_REQUEST_ENTITY_TOO_LARGE)
+                    r->connection->aborted = 1;
+                jk_log(conf->log, JK_LOG_INFO, "Aborting connection"
+                       " for worker=%s",
+                       worker_name);
+                JK_TRACE_EXIT(conf->log);
+                return is_error;
+            }
+            else {
+                jk_log(conf->log, JK_LOG_INFO, "Service error=%d"
+                       " for worker=%s",
+                       rc, worker_name);
+                JK_TRACE_EXIT(conf->log);
+                return is_error;
+            }
+        }
+        else {
+            jk_log(conf->log, JK_LOG_ERROR, "Could not init service"
+                   " for worker=%s",
+                   worker_name);
+            JK_TRACE_EXIT(conf->log);
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+    }
+
+    JK_TRACE_EXIT(conf->log);
+    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;
+    jk_map_alloc(&c->worker_properties);
+    c->worker_file = NULL;
+    c->mount_file = NULL;
+    c->log_file = NULL;
+    c->log_fd = -1;
+    c->log = NULL;
+    c->alias_dir = NULL;
+    c->stamp_format_string = NULL;
+    c->format_string = NULL;
+    c->format = NULL;
+    c->mountcopy = JK_FALSE;
+    c->exclude_options = 0;
+
+    if (s->is_virtual) {
+        c->mount_file_reload = JK_UNSET;
+        c->log_level = JK_UNSET;
+        c->options = 0;
+        c->worker_indicator = NULL;
+        c->ssl_enable = JK_UNSET;
+        c->https_indicator = NULL;
+        c->certs_indicator = NULL;
+        c->cipher_indicator = NULL;
+        c->certchain_prefix = NULL;
+        c->session_indicator = NULL;
+        c->key_size_indicator = NULL;
+        c->strip_session = JK_UNSET;
+    } else {
+        c->mount_file_reload = JK_URIMAP_DEF_RELOAD;
+        c->log_level = JK_LOG_DEF_LEVEL;
+        c->options = JK_OPT_FWDURIDEFAULT;
+        c->worker_indicator = JK_ENV_WORKER_NAME;
+        /*
+         * 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 = JK_ENV_HTTPS;
+        c->certs_indicator = JK_ENV_CERTS;
+        c->cipher_indicator = JK_ENV_CIPHER;
+        c->certchain_prefix = JK_ENV_CERTCHAIN_PREFIX;
+        c->session_indicator = JK_ENV_SESSION;
+        c->key_size_indicator = JK_ENV_KEY_SIZE;
+        c->strip_session = JK_FALSE;
+    }
+
+    if (!jk_map_alloc(&(c->uri_to_context))) {
+        jk_error_exit(APLOG_MARK, APLOG_EMERG, s, p, "Memory error");
+    }
+    if (!jk_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->envvars_def = ap_make_table(p, 0);
+    c->envvar_items = ap_make_array(p, 0, sizeof(envvar_item));
+
+    c->s = s;
+    jk_map_put(c->worker_properties, "ServerRoot", ap_server_root, NULL);
+
+    return c;
+}
+
+
+static void copy_jk_map(ap_pool * p, server_rec * s, jk_map_t *src,
+                        jk_map_t *dst)
+{
+    int sz = jk_map_size(src);
+    int i;
+    for (i = 0; i < sz; i++) {
+        const char *name = jk_map_name_at(src, i);
+        if (jk_map_get(dst, name, NULL) == NULL) {
+            if (!jk_map_put (dst, name,
+                 ap_pstrdup(p, jk_map_get_string(src, name, NULL)),
+                            NULL)) {
+                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 (!overrides->log_file)
+        overrides->log_file = base->log_file;
+    if (overrides->log_level == JK_UNSET)
+        overrides->log_level = base->log_level;
+
+    if (!overrides->stamp_format_string)
+        overrides->stamp_format_string = base->stamp_format_string;
+    if (!overrides->format_string)
+        overrides->format_string = base->format_string;
+
+    if (!overrides->worker_indicator)
+        overrides->worker_indicator = base->worker_indicator;
+
+    if (overrides->ssl_enable == JK_UNSET)
+        overrides->ssl_enable = base->ssl_enable;
+    if (!overrides->https_indicator)
+        overrides->https_indicator = base->https_indicator;
+    if (!overrides->certs_indicator)
+        overrides->certs_indicator = base->certs_indicator;
+    if (!overrides->cipher_indicator)
+        overrides->cipher_indicator = base->cipher_indicator;
+    if (!overrides->certchain_prefix)
+        overrides->certchain_prefix = base->certchain_prefix;
+    if (!overrides->session_indicator)
+        overrides->session_indicator = base->session_indicator;
+    if (!overrides->key_size_indicator)
+        overrides->key_size_indicator = base->key_size_indicator;
+
+    if (!overrides->secret_key)
+        overrides->secret_key = base->secret_key;
+
+    overrides->options |= (base->options & ~base->exclude_options);
+
+    if (base->envvars_in_use) {
+        int i;
+        const array_header *arr;
+        const table_entry *elts;
+
+        arr = ap_table_elts(base->envvars);
+        if (arr) {
+            overrides->envvars_in_use = JK_TRUE;
+            elts = (const table_entry *)arr->elts;
+            for (i = 0; i < arr->nelts; ++i) {
+                if (!ap_table_get(overrides->envvars, elts[i].key)) {
+                    ap_table_setn(overrides->envvars, elts[i].key, elts[i].val);
+                }
+            }
+        }
+        arr = ap_table_elts(base->envvars_def);
+        if (arr) {
+            overrides->envvars_in_use = JK_TRUE;
+            elts = (const table_entry *)arr->elts;
+            for (i = 0; i < arr->nelts; ++i) {
+                if (!ap_table_get(overrides->envvars_def, elts[i].key)) {
+                    ap_table_setn(overrides->envvars_def, elts[i].key, elts[i].val);
+                }
+            }
+        }
+    }
+
+    if (overrides->mount_file_reload == JK_UNSET)
+        overrides->mount_file_reload = base->mount_file_reload;
+    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 (!overrides->mount_file)
+            overrides->mount_file = base->mount_file;
+        if (!overrides->alias_dir)
+            overrides->alias_dir = base->alias_dir;
+    }
+    if (overrides->strip_session == JK_UNSET)
+        overrides->strip_session = base->strip_session;
+
+    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) {
+        jk_file_logger_t *flp = l->logger_private;
+        int log_fd = flp->log_fd;
+        size_t sz = strlen(what);
+        if (log_fd >= 0 && sz) {
+            if (write(log_fd, what, sz) < 0 ) {
+                ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, NULL,
+                             "mod_jk: jk_log_to_file %s failed",
+                             what);
+            }
+            else {
+                char c;
+#if defined(WIN32)
+                c = '\r';
+                write(log_fd, &c, 1);
+#endif
+                c = '\n';
+                write(log_fd, &c, 1);
+            }
+        }
+
+        return JK_TRUE;
+    }
+
+    return JK_FALSE;
+}
+
+static int log_fd_get(char *key)
+{
+    const char *buf=ap_table_get(jk_log_fds, key);
+    if (buf)
+        return atoi(buf);
+    return 0;
+}
+
+static void log_fd_set(pool *p, char *key, int v)
+{
+    char *buf=(char *)ap_pcalloc(p, 8*sizeof(char));
+    ap_snprintf(buf, 8, "%d", v);
+    ap_table_setn(jk_log_fds, key, buf);
+}
+
+static void open_jk_log(server_rec *s, pool *p)
+{
+    const char *fname;
+    int jklogfd;
+    piped_log *pl;
+    jk_logger_t *jkl;
+    jk_file_logger_t *flp;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *) ap_get_module_config(s->module_config,
+                                                  &jk_module);
+
+    if (!s->is_virtual && !conf->log_file) {
+        conf->log_file = ap_server_root_relative(p, JK_LOG_DEF_FILE);
+        if (conf->log_file)
+            ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_NOERRNO, s,
+                         "No JkLogFile defined in httpd.conf. "
+                         "Using default %s", conf->log_file);
+    }
+
+    if (s->is_virtual && conf->log_file == NULL) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, s,
+                     "mod_jk: Invalid JkLogFile NULL");
+        conf->log = main_log;
+        return;
+    }
+    if (s->is_virtual && *(conf->log_file) == '\0') {
+        ap_log_error(APLOG_MARK, APLOG_ERR, s,
+                     "mod_jk: Invalid JkLogFile EMPTY");
+        conf->log = main_log;
+        return;
+    }
+
+#ifdef CHROOTED_APACHE
+    ap_server_strip_chroot(conf->log_file, 0);
+#endif
+
+    jklogfd = log_fd_get(conf->log_file);
+    if (!jklogfd) {
+        if (*conf->log_file == '|') {
+            if ((pl = ap_open_piped_log(p, conf->log_file + 1)) == NULL) {
+                ap_log_error(APLOG_MARK, APLOG_ERR, s,
+                             "mod_jk: could not open reliable pipe "
+                             "to jk log %s", conf->log_file + 1);
+                exit(1);
+            }
+            jklogfd = 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, s,
+                             "mod_jk: Invalid JkLog " "path %s", conf->log_file);
+                exit(1);
+            }
+#if AP_MODULE_MAGIC_AT_LEAST(19990320,14)
+            if ((jklogfd = ap_popenf_ex(p, fname, xfer_flags, xfer_mode, 1))
+                 < 0) {
+#else
+            if ((jklogfd = ap_popenf(p, fname, xfer_flags, xfer_mode))
+                 < 0) {
+#endif
+                ap_log_error(APLOG_MARK, APLOG_ERR, s,
+                             "mod_jk: could not open JkLog " "file %s", fname);
+                exit(1);
+            }
+        }
+        log_fd_set(p, conf->log_file, jklogfd);
+    }
+    conf->log_fd = jklogfd;
+    jkl = (jk_logger_t *)ap_palloc(p, sizeof(jk_logger_t));
+    flp = (jk_file_logger_t *)ap_palloc(p, sizeof(jk_file_logger_t));
+    if (jkl && flp) {
+        jkl->log = jk_log_to_file;
+        jkl->level = conf->log_level;
+        jkl->log_fmt = conf->stamp_format_string;
+        jkl->logger_private = flp;
+        flp->log_fd = conf->log_fd;
+        conf->log = jkl;
+        if (main_log == NULL)
+            main_log = conf->log;
+        return;
+    }
+
+    return;
+}
+
+static void jk_init(server_rec * s, ap_pool * p)
+{
+    int rc;
+    server_rec *srv = s;
+    const char *err_string = NULL;
+    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;
+
+    jk_log_fds = ap_make_table(p, 0);
+
+    /* step through the servers and open each jk logfile
+     * and do additional post config initialization.
+     */
+    for (; srv; srv = srv->next) {
+        jk_server_conf_t *sconf = (jk_server_conf_t *)ap_get_module_config(srv->module_config,
+                                                                           &jk_module);
+        open_jk_log(srv, p);
+        if (sconf) {
+            if (!uri_worker_map_alloc(&(sconf->uw_map),
+                                      sconf->uri_to_context, sconf->log))
+                jk_error_exit(APLOG_MARK, APLOG_EMERG, srv,
+                              p, "Memory error");
+            if (sconf->mount_file) {
+                sconf->uw_map->fname = sconf->mount_file;
+                sconf->uw_map->reload = sconf->mount_file_reload;
+                uri_worker_map_load(sconf->uw_map, sconf->log);
+            }
+            if (sconf->format_string) {
+                sconf->format =
+                    parse_request_log_string(p, sconf->format_string, &err_string);
+                if (sconf->format == NULL)
+                    ap_log_error(APLOG_MARK, APLOG_ERR, srv,
+                                 "JkRequestLogFormat format array NULL");
+            }
+            sconf->options &= ~sconf->exclude_options;
+            if (sconf->envvars_in_use) {
+                int i;
+                const array_header *arr;
+                const table_entry *elts;
+                envvar_item *item;
+                const char *envvar_def;
+
+                arr = ap_table_elts(sconf->envvars);
+                if (arr) {
+                    elts = (const table_entry *)arr->elts;
+                    for (i = 0; i < arr->nelts; ++i) {
+                        item = (envvar_item *)ap_push_array(sconf->envvar_items);
+                        if (!item)
+                            jk_error_exit(APLOG_MARK, APLOG_EMERG, srv,
+                                          p, "Memory error");
+                        item->name = elts[i].key;
+                        envvar_def = ap_table_get(sconf->envvars_def, elts[i].key);
+                        if (envvar_def && !strcmp("1", envvar_def) ) {
+                            item->value = elts[i].val;
+                            item->has_default = 1;
+                        }
+                        else {
+                            item->value = "";
+                            item->has_default = 0;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+#if !defined(WIN32) && !defined(NETWARE)
+    if (!jk_shm_file) {
+        jk_shm_file = ap_server_root_relative(p, JK_SHM_DEF_FILE);
+
+#ifdef CHROOTED_APACHE
+        ap_server_strip_chroot(jk_shm_file, 0);
+#endif
+
+        if (jk_shm_file)
+            ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_NOERRNO, s,
+                         "No JkShmFile defined in httpd.conf. "
+                         "Using default %s", jk_shm_file);
+    }
+#endif
+
+    if ((rc = jk_shm_open(jk_shm_file, jk_shm_size, conf->log)) == 0) {
+        if (JK_IS_DEBUG_LEVEL(conf->log))
+            jk_log(conf->log, JK_LOG_DEBUG, "Initialized shm:%s",
+                   jk_shm_name(), rc);
+    }
+    else
+        jk_log(conf->log, JK_LOG_ERROR,
+               "Initializing shm:%s errno=%d. Load balancing workers will not function properly.",
+               jk_shm_name(), rc);
+
+    /* 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;
+if (JK_IS_DEBUG_LEVEL(conf->log))
+    jk_log(conf->log, JK_LOG_DEBUG, "default secret key = %s", conf->secret_key);
+for (i = 0; i < jk_map_size(conf->automount); i++)
+{
+            char *name = jk_map_name_at(conf->automount, i);
+            if (JK_IS_DEBUG_LEVEL(conf->log))
+                jk_log(conf->log, JK_LOG_DEBUG, "worker = %s and virtualhost = %s", name, map_get_string(conf->automount, name, NULL));
+}
+}
+*/
+
+    if ((conf->worker_file != NULL) &&
+        !jk_map_read_properties(init_map, conf->worker_file, NULL, 1, conf->log)) {
+        jk_error_exit(APLOG_MARK, APLOG_EMERG | APLOG_NOERRNO, s, p,
+                      "Error in reading worker properties from '%s'",
+                      conf->worker_file);
+    }
+
+    if (jk_map_resolve_references(init_map, "worker.", 1, 1, conf->log) == JK_FALSE) {
+        jk_error_exit(APLOG_MARK, APLOG_EMERG | APLOG_NOERRNO, s, p,
+                      "Error in resolving configuration references");
+    }
+
+    /* 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)) {
+#if MODULE_MAGIC_NUMBER >= 19980527
+        /* Tell apache we're here */
+        ap_add_version_component(JK_EXPOSED_VERSION);
+#endif
+        jk_log(conf->log, JK_LOG_INFO,
+               "%s initialized",
+               JK_EXPOSED_VERSION);
+        return;
+    }
+    else {
+        ap_log_error(APLOG_MARK, APLOG_ERR, s,
+                     "Error in creating the workers."
+                     " Please consult your mod_jk log file '%s'.", conf->log_file);
+    }
+
+}
+
+/*
+ * 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) {
+            char *clean_uri = ap_pstrdup(r->pool, r->uri);
+            const char *worker;
+
+            if (ap_table_get(r->subprocess_env, "no-jk")) {
+                if (JK_IS_DEBUG_LEVEL(conf->log))
+                    jk_log(conf->log, JK_LOG_DEBUG,
+                           "Into translate no-jk env var detected for uri=%s, declined",
+                           r->uri);
+                return DECLINED;
+            }
+
+            ap_no2slash(clean_uri);
+            worker = map_uri_to_worker(conf->uw_map, clean_uri, conf->log);
+
+            /* 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 && r->prev->handler &&
+                !strcmp(r->prev->handler, JK_HANDLER) && clean_uri &&
+                strlen(clean_uri) && clean_uri[strlen(clean_uri) - 1] == '/') {
+
+                if (worker_env.num_of_workers) {
+                    /* Nothing here to do but assign the first worker since we
+                     * already tried mapping and it didn't work out */
+                    worker = worker_env.worker_list[0];
+
+                    if (JK_IS_DEBUG_LEVEL(conf->log))
+                        jk_log(conf->log, JK_LOG_DEBUG, "Manual configuration for %s %s",
+                               clean_uri, worker_env.worker_list[0]);
+                }
+            }
+
+            if (worker) {
+                r->handler = ap_pstrdup(r->pool, JK_HANDLER);
+                ap_table_setn(r->notes, JK_NOTE_WORKER_NAME, worker);
+            }
+            else if (conf->alias_dir != NULL) {
+                /* Automatically map uri to a context static file */
+                if (JK_IS_DEBUG_LEVEL(conf->log))
+                    jk_log(conf->log, JK_LOG_DEBUG,
+                           "check alias_dir: %s",
+                           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) {
+                            if (JK_IS_DEBUG_LEVEL(conf->log))
+                                jk_log(conf->log, JK_LOG_DEBUG,
+                                       "AutoAlias child_dir: %s",
+                                       child_dir);
+                            if (!strcasecmp(child_dir, "WEB-INF") ||
+                                !strcasecmp(child_dir, "META-INF")) {
+                                if (JK_IS_DEBUG_LEVEL(conf->log))
+                                    jk_log(conf->log, JK_LOG_DEBUG,
+                                           "AutoAlias HTTP_NOT_FOUND for URI: %s",
+                                           r->uri);
+                                return HTTP_NOT_FOUND;
+                            }
+                        }
+                    }
+                    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) {
+                                if (JK_IS_DEBUG_LEVEL(conf->log))
+                                    jk_log(conf->log, JK_LOG_DEBUG,
+                                           "AutoAlias OK for file: %s",
+                                           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")) {
+                                if (JK_IS_DEBUG_LEVEL(conf->log))
+                                    jk_log(conf->log, JK_LOG_DEBUG,
+                                           "AutoAlias FORBIDDEN for URI: %s",
+                                           r->uri);
+                                return FORBIDDEN;
+                            }
+                        }
+                    }
+                }
+            }
+            else if (conf->strip_session == JK_TRUE) {
+                char *jsessionid;
+                if (r->uri) {
+                    jsessionid = strstr(r->uri, JK_PATH_SESSION_IDENTIFIER);
+                    if (jsessionid) {
+                        if (JK_IS_DEBUG_LEVEL(conf->log))
+                            jk_log(conf->log, JK_LOG_DEBUG,
+                                   "removing session identifier [%s] for non servlet url [%s]",
+                                   jsessionid, r->uri);
+                        *jsessionid = '\0';
+                    }
+                }
+                if (r->filename) {
+                    jsessionid = strstr(r->filename, JK_PATH_SESSION_IDENTIFIER);
+                    if (jsessionid)
+                        *jsessionid = '\0';
+                }
+            }
+        }
+    }
+
+    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_NOTE_WORKER_NAME);
+
+        if (ap_table_get(r->subprocess_env, "no-jk")) {
+            if (JK_IS_DEBUG_LEVEL(conf->log))
+                jk_log(conf->log, JK_LOG_DEBUG,
+                       "Into fixup no-jk env var detected for uri=%s, declined",
+                       r->uri);
+            return DECLINED;
+        }
+
+        /* 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);
+
+            /* 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);
+
+                if (JK_IS_DEBUG_LEVEL(conf->log))
+                    jk_log(conf->log, JK_LOG_DEBUG, "ForwardDirectories on: %s",
+                           r->uri);
+            }
+        }
+    }
+
+    return DECLINED;
+}
+
+static void child_exit_handler(server_rec * s, ap_pool * p)
+{
+    /* srevilak - refactor cleanup body to jk_generic_cleanup() */
+    jk_generic_cleanup(s);
+    jk_shm_close();
+}
+
+static void child_init_handler(server_rec * s, ap_pool * p)
+{
+    int rc;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *) ap_get_module_config(s->module_config,
+                                                  &jk_module);
+
+    JK_TRACE_ENTER(conf->log);
+
+    if ((rc = jk_shm_attach(jk_shm_file, jk_shm_size, conf->log)) == 0) {
+        if (JK_IS_DEBUG_LEVEL(conf->log))
+            jk_log(conf->log, JK_LOG_DEBUG, "Attached shm:%s",
+                   jk_shm_name());
+    }
+    else
+        jk_log(conf->log, JK_LOG_ERROR, "Attaching shm:%s errno=%d",
+               jk_shm_name(), rc);
+
+    JK_TRACE_EXIT(conf->log);
+
+}
+
+
+/** srevilak -- registered as a cleanup handler in jk_init */
+static void jk_server_cleanup(void *data)
+{
+    jk_generic_cleanup((server_rec *) data);
+    jk_shm_close();
+}
+
+
+/** 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 (conf) {
+            wc_close(NULL);
+            uri_worker_map_free(&(conf->uw_map), NULL);
+            jk_map_free(&(conf->uri_to_context));
+            jk_map_free(&(conf->worker_properties));
+            jk_map_free(&(conf->automount));
+        }
+        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 */
+    child_init_handler,         /* apache child process initializer */
+    child_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..2145627
--- /dev/null
+++ b/connectors/jk/native/apache-1.3/mod_jk.dsp
@@ -0,0 +1,287 @@
+# 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 /MD /W3 /O2 /Oy- /Zi /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MOD_JK_EXPORTS" /FD /c
+# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "..\common" /I "$(APACHE1_HOME)\include" /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" /Fd"Release\mod_jk_src" /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 kernel32.lib user32.lib advapi32.lib ws2_32.lib mswsock.lib /nologo /base:"0x6A6B0000" /subsystem:windows /dll /debug /machine:I386 /out:"Release/mod_jk.so" /libpath:"$(APACHE1_HOME)\libexec" /libpath:"$(APACHE1_HOME)\src\CoreR" /libpath:"$(APACHE1_HOME)\src\Release" /opt:ref
+
+!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 /MDd /W3 /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MOD_JK_EXPORTS" /FD /c
+# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "..\common" /I "$(APACHE1_HOME)\include" /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" /Fd"Debug\mod_jk_src" /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 ApacheCore.lib kernel32.lib user32.lib advapi32.lib ws2_32.lib mswsock.lib /nologo /base:"0x6A6B0000" /subsystem:windows /dll /incremental:no /debug /machine:I386 /out:"Debug/mod_jk.so" /libpath:"$(APACHE1_HOME)\libexec" /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_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_shm.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_sockbuf.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_status.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_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_shm.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_sockbuf.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_status.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
+# 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..8f7a3a5
--- /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 *.lo *.a *.la *.so *.so.* *.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..852ee7a
--- /dev/null
+++ b/connectors/jk/native/apache-2.0/Makefile.in
@@ -0,0 +1,81 @@
+## 
+
+APXS=@APXS@
+OS=@OS@
+JAVA_HOME=@JAVA_HOME@
+CP=@CP@
+APACHE_DIR=@APACHE_DIR@
+MKDIR=@MKDIR@
+APXSCFLAGS=@APXSCFLAGS@
+APXSCPPFLAGS=@APXSCPPFLAGS@
+CC=@CC@
+
+# 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} `${APXS} -q LDFLAGS` -o $@ -module -rpath ${libexecdir} -avoid-version mod_jk.lo $(APACHE_OBJECTS)
+
+mod_jk.so: mod_jk.la
+	../scripts/build/instdso.sh SH_LIBTOOL='$(LIBTOOL)' mod_jk.la `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 *.lo *.a *.la *.so *.so.* *.slo
+	rm -rf .libs
diff --git a/connectors/jk/native/apache-2.0/Makefile.vc b/connectors/jk/native/apache-2.0/Makefile.vc
new file mode 100644
index 0000000..140b515
--- /dev/null
+++ b/connectors/jk/native/apache-2.0/Makefile.vc
@@ -0,0 +1,251 @@
+!IFDEF APACHE22
+APACHE2_HOME=$(APACHE22_HOME)
+APR_LIB=libapr-1.lib
+APU_LIB=libaprutil-1.lib
+OUTDIR=.\Release22
+INTDIR=.\Release22
+!ELSE
+APR_LIB=libapr.lib
+APU_LIB=libaprutil.lib
+OUTDIR=.\Release20
+INTDIR=.\Release20
+!IFDEF APACHE20_HOME
+APACHE2_HOME=$(APACHE20_HOME)
+!ENDIF
+!ENDIF
+
+!IFNDEF APACHE2_HOME
+!ERROR Missing APACHE2_HOME environment variable.
+!ENDIF
+
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+ALL : "$(OUTDIR)\mod_jk.so"
+
+
+CLEAN :
+	-@erase "$(INTDIR)\jk_ajp12_worker.obj"
+	-@erase "$(INTDIR)\jk_ajp13.obj"
+	-@erase "$(INTDIR)\jk_ajp13_worker.obj"
+	-@erase "$(INTDIR)\jk_ajp14.obj"
+	-@erase "$(INTDIR)\jk_ajp14_worker.obj"
+	-@erase "$(INTDIR)\jk_ajp_common.obj"
+	-@erase "$(INTDIR)\jk_connect.obj"
+	-@erase "$(INTDIR)\jk_context.obj"
+	-@erase "$(INTDIR)\jk_jni_worker.obj"
+	-@erase "$(INTDIR)\jk_lb_worker.obj"
+	-@erase "$(INTDIR)\jk_map.obj"
+	-@erase "$(INTDIR)\jk_md5.obj"
+	-@erase "$(INTDIR)\jk_msg_buff.obj"
+	-@erase "$(INTDIR)\jk_pool.obj"
+	-@erase "$(INTDIR)\jk_shm.obj"
+	-@erase "$(INTDIR)\jk_sockbuf.obj"
+	-@erase "$(INTDIR)\jk_status.obj"
+	-@erase "$(INTDIR)\jk_uri_worker_map.obj"
+	-@erase "$(INTDIR)\jk_util.obj"
+	-@erase "$(INTDIR)\jk_worker.obj"
+	-@erase "$(INTDIR)\mod_jk.obj"
+	-@erase "$(INTDIR)\mod_jk_src.idb"
+	-@erase "$(INTDIR)\mod_jk_src.pdb"
+	-@erase "$(OUTDIR)\mod_jk.exp"
+	-@erase "$(OUTDIR)\mod_jk.lib"
+	-@erase "$(OUTDIR)\mod_jk.pdb"
+	-@erase "$(OUTDIR)\mod_jk.so"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_jk.bsc" 
+BSC32_SBRS= \
+	
+LINK32=link.exe
+LINK32_FLAGS=libhttpd.lib $(APR_LIB) $(APU_LIB) kernel32.lib user32.lib advapi32.lib wsock32.lib /nologo /base:"0x6A6B0000" /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_jk.pdb" /debug /machine:I386 /out:"$(OUTDIR)\mod_jk.so" /implib:"$(OUTDIR)\mod_jk.lib" /libpath:"$(APACHE2_HOME)\lib" /opt:ref 
+LINK32_OBJS= \
+	"$(INTDIR)\jk_ajp12_worker.obj" \
+	"$(INTDIR)\jk_ajp13.obj" \
+	"$(INTDIR)\jk_ajp13_worker.obj" \
+	"$(INTDIR)\jk_ajp14.obj" \
+	"$(INTDIR)\jk_ajp14_worker.obj" \
+	"$(INTDIR)\jk_ajp_common.obj" \
+	"$(INTDIR)\jk_connect.obj" \
+	"$(INTDIR)\jk_context.obj" \
+	"$(INTDIR)\jk_jni_worker.obj" \
+	"$(INTDIR)\jk_lb_worker.obj" \
+	"$(INTDIR)\jk_map.obj" \
+	"$(INTDIR)\jk_md5.obj" \
+	"$(INTDIR)\jk_msg_buff.obj" \
+	"$(INTDIR)\jk_pool.obj" \
+	"$(INTDIR)\jk_shm.obj" \
+	"$(INTDIR)\jk_sockbuf.obj" \
+	"$(INTDIR)\jk_status.obj" \
+	"$(INTDIR)\jk_uri_worker_map.obj" \
+	"$(INTDIR)\jk_util.obj" \
+	"$(INTDIR)\jk_worker.obj" \
+	"$(INTDIR)\mod_jk.obj"
+
+"$(OUTDIR)\mod_jk.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+CPP_PROJ=/nologo /MD /W3 /Zi /O2 /I "..\common" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /I "$(APACHE2_HOME)\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_jk_src" /FD /c 
+
+.c{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cpp{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cxx{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.c{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cpp{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cxx{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 
+
+SOURCE=..\common\jk_ajp12_worker.c
+
+"$(INTDIR)\jk_ajp12_worker.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_ajp13.c
+
+"$(INTDIR)\jk_ajp13.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_ajp13_worker.c
+
+"$(INTDIR)\jk_ajp13_worker.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_ajp14.c
+
+"$(INTDIR)\jk_ajp14.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_ajp14_worker.c
+
+"$(INTDIR)\jk_ajp14_worker.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_ajp_common.c
+
+"$(INTDIR)\jk_ajp_common.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_connect.c
+
+"$(INTDIR)\jk_connect.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_context.c
+
+"$(INTDIR)\jk_context.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_jni_worker.c
+
+"$(INTDIR)\jk_jni_worker.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_lb_worker.c
+
+"$(INTDIR)\jk_lb_worker.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_map.c
+
+"$(INTDIR)\jk_map.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_md5.c
+
+"$(INTDIR)\jk_md5.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_msg_buff.c
+
+"$(INTDIR)\jk_msg_buff.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_pool.c
+
+"$(INTDIR)\jk_pool.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_shm.c
+
+"$(INTDIR)\jk_shm.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_sockbuf.c
+
+"$(INTDIR)\jk_sockbuf.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_status.c
+
+"$(INTDIR)\jk_status.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_uri_worker_map.c
+
+"$(INTDIR)\jk_uri_worker_map.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_util.c
+
+"$(INTDIR)\jk_util.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_worker.c
+
+"$(INTDIR)\jk_worker.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\mod_jk.c
+
+"$(INTDIR)\mod_jk.obj" : $(SOURCE) "$(INTDIR)"
+
diff --git a/connectors/jk/native/apache-2.0/NWGNUmakefile b/connectors/jk/native/apache-2.0/NWGNUmakefile
new file mode 100644
index 0000000..a680acb
--- /dev/null
+++ b/connectors/jk/native/apache-2.0/NWGNUmakefile
@@ -0,0 +1,316 @@
+#
+# 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 \
+			$(APR)/include \
+			$(APRUTIL)/include \
+			$(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__ \
+			-D_POSIX_SOURCE \
+			$(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 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_shm.o \
+	$(OBJDIR)/jk_sockbuf.o \
+	$(OBJDIR)/jk_status.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 ../../support/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..ece3382
--- /dev/null
+++ b/connectors/jk/native/apache-2.0/bldjk.qclsrc
@@ -0,0 +1,278 @@
+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) +
+    LOCALETYPE(*LOCALE) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TGTRLS(*CURRENT) +
+    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) +
+    LOCALETYPE(*LOCALE) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TGTRLS(*CURRENT) +
+    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) +
+    LOCALETYPE(*LOCALE) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TGTRLS(*CURRENT) +
+    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) +
+    LOCALETYPE(*LOCALE) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TGTRLS(*CURRENT) +
+    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) +
+    LOCALETYPE(*LOCALE) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TGTRLS(*CURRENT) +
+    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) +
+    LOCALETYPE(*LOCALE) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TGTRLS(*CURRENT) +
+    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) +
+    LOCALETYPE(*LOCALE) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TGTRLS(*CURRENT) +
+    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' 'USE_SO_RCVTIMEO' +
+           'USE_SO_SNDTIMEO' ) +
+    TEXT('jk_connect.c') +
+    OPTIMIZE(40) +
+    LOCALETYPE(*LOCALE) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TGTRLS(*CURRENT) +
+    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) +
+    LOCALETYPE(*LOCALE) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TGTRLS(*CURRENT) +
+    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) +
+    LOCALETYPE(*LOCALE) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TGTRLS(*CURRENT) +
+    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) +
+    LOCALETYPE(*LOCALE) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TGTRLS(*CURRENT) +
+    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) +
+    LOCALETYPE(*LOCALE) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TGTRLS(*CURRENT) +
+    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) +
+    LOCALETYPE(*LOCALE) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TGTRLS(*CURRENT) +
+    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) +
+    LOCALETYPE(*LOCALE) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TGTRLS(*CURRENT) +
+    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) +
+    LOCALETYPE(*LOCALE) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TGTRLS(*CURRENT) +
+    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) +
+    LOCALETYPE(*LOCALE) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TGTRLS(*CURRENT) +
+    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) +
+    LOCALETYPE(*LOCALE) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TGTRLS(*CURRENT) +
+    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) +
+    LOCALETYPE(*LOCALE) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TGTRLS(*CURRENT) +
+    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) +
+    LOCALETYPE(*LOCALE) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TGTRLS(*CURRENT) +
+    INCDIR('/home/apache/jk/native/common' '/QIBM/ProdData/HTTPA/Include')
+
+CRTCMOD MODULE(MOD_JK/JK_STATUS) +
+    SRCSTMF('/home/apache/jk/native/common/jk_status.c') +
+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' '_XOPEN_SOURCE=520' +
+           'USE_APACHE_MD5' '_REENTRANT') +
+    TEXT('jk_status.c') +
+    OPTIMIZE(40) +
+    LOCALETYPE(*LOCALE) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TGTRLS(*CURRENT) +
+    INCDIR('/home/apache/jk/native/common' '/QIBM/ProdData/HTTPA/Include')
+
+CRTCMOD MODULE(MOD_JK/JK_SHM) +
+    SRCSTMF('/home/apache/jk/native/common/jk_shm.c') +
+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' '_XOPEN_SOURCE=520' +
+           'USE_APACHE_MD5' '_REENTRANT') +
+    TEXT('jk_shm.c') +
+    OPTIMIZE(40) +
+    LOCALETYPE(*LOCALE) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TGTRLS(*CURRENT) +
+    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 +
+             MOD_JK/JK_STATUS MOD_JK/JK_SHM) +
+      EXPORT(*SRCFILE) +
+      BNDDIR() +
+      TGTRLS(*CURRENT) +
+      SRCFILE(MOD_JK/QSRVSRC) +
+      SRCMBR(MOD_JK) +
+      USRPRF(*USER) +
+      BNDSRVPGM(QHTTPSVR/QZSRAPR QHTTPSVR/QZSRCORE +
+                QHTTPSVR/QZSRXMLP QHTTPSVR/QZSRSDBM) +
+      TEXT('Apache Tomcat mod_jk connector module')
+
+ENDPGM
diff --git a/connectors/jk/native/apache-2.0/bldjk54.qclsrc b/connectors/jk/native/apache-2.0/bldjk54.qclsrc
new file mode 100644
index 0000000..43fbfeb
--- /dev/null
+++ b/connectors/jk/native/apache-2.0/bldjk54.qclsrc
@@ -0,0 +1,299 @@
+PGM
+CRTCMOD MODULE(MOD_JK/MOD_JK) +
+    SRCSTMF('/home/apache/jk/native/apache-2.0/mod_jk.c') +
+    DEFINE('AS400' 'AS400_UTF8' 'HAVE_JNI' 'HAVE_APR' +
+          '_XOPEN_SOURCE=520' + 'USE_APACHE_MD5' '_REENTRANT') +
+    TEXT('mod_jk.c') +
+    OPTIMIZE(40) +
+    LOCALETYPE(*LOCALEUTF) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TERASPACE(*YES) +
+    TGTRLS(*CURRENT) +
+    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' 'AS400_UTF8' 'HAVE_JNI' 'HAVE_APR' +
+           '_XOPEN_SOURCE=520' + 'USE_APACHE_MD5' '_REENTRANT') +
+    TEXT('jk_ajp_common.c') +
+    OPTIMIZE(40) +
+    LOCALETYPE(*LOCALEUTF) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TERASPACE(*YES) +
+    TGTRLS(*CURRENT) +
+    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' 'AS400_UTF8' 'HAVE_JNI' 'HAVE_APR' +
+           '_XOPEN_SOURCE=520' + 'USE_APACHE_MD5' '_REENTRANT') +
+    TEXT('jk_ajp12_worker.c') +
+    OPTIMIZE(40) +
+    LOCALETYPE(*LOCALEUTF) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TERASPACE(*YES) +
+    TGTRLS(*CURRENT) +
+    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' 'AS400_UTF8' 'HAVE_JNI' 'HAVE_APR' +
+           '_XOPEN_SOURCE=520' + 'USE_APACHE_MD5' '_REENTRANT') +
+    TEXT('jk_ajp13.c') +
+    OPTIMIZE(40) +
+    LOCALETYPE(*LOCALEUTF) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TERASPACE(*YES) +
+    TGTRLS(*CURRENT) +
+    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' 'AS400_UTF8' 'HAVE_JNI' 'HAVE_APR' +
+           '_XOPEN_SOURCE=520' 'USE_APACHE_MD5' '_REENTRANT') +
+    TEXT('jk_ajp13_worker.c') +
+    OPTIMIZE(40) +
+    LOCALETYPE(*LOCALEUTF) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TERASPACE(*YES) +
+    TGTRLS(*CURRENT) +
+    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' 'AS400_UTF8' 'HAVE_JNI' 'HAVE_APR' +
+           '_XOPEN_SOURCE=520' 'USE_APACHE_MD5' '_REENTRANT') +
+    TEXT('jk_ajp14.c') +
+    OPTIMIZE(40) +
+    LOCALETYPE(*LOCALEUTF) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TERASPACE(*YES) +
+    TGTRLS(*CURRENT) +
+    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' 'AS400_UTF8' 'HAVE_JNI' 'HAVE_APR' +
+           '_XOPEN_SOURCE=520' 'USE_APACHE_MD5' '_REENTRANT') +
+    TEXT('jk_ajp14_worker.c') +
+    OPTIMIZE(40) +
+    LOCALETYPE(*LOCALEUTF) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TERASPACE(*YES) +
+    TGTRLS(*CURRENT) +
+    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' 'AS400_UTF8' 'HAVE_JNI' 'HAVE_APR' +
+           '_XOPEN_SOURCE=520' 'USE_APACHE_MD5' '_REENTRANT' +
+           'USE_SO_RCVTIMEO' + 'USE_SO_SNDTIMEO' ) +
+    TEXT('jk_connect.c') +
+    OPTIMIZE(40) +
+    LOCALETYPE(*LOCALEUTF) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TERASPACE(*YES) +
+    TGTRLS(*CURRENT) +
+    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' 'AS400_UTF8' 'HAVE_JNI' 'HAVE_APR' +
+           '_XOPEN_SOURCE=520' 'USE_APACHE_MD5' '_REENTRANT') +
+    TEXT('jk_context.c') +
+    OPTIMIZE(40) +
+    LOCALETYPE(*LOCALEUTF) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TERASPACE(*YES) +
+    TGTRLS(*CURRENT) +
+    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' 'AS400_UTF8' 'HAVE_JNI' 'HAVE_APR' 'OS400_JVM_12' +
+           '_XOPEN_SOURCE=520' + 'USE_APACHE_MD5' +
+    '_REENTRANT') +
+    TEXT('jk_jni_worker.c') +
+    OPTIMIZE(40) +
+    LOCALETYPE(*LOCALEUTF) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TERASPACE(*YES) +
+    TGTRLS(*CURRENT) +
+    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' 'AS400_UTF8' 'HAVE_JNI' 'HAVE_APR' +
+           '_XOPEN_SOURCE=520' 'USE_APACHE_MD5' '_REENTRANT') +
+    TEXT('jk_lb_worker.c') +
+    OPTIMIZE(40) +
+    LOCALETYPE(*LOCALEUTF) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TERASPACE(*YES) +
+    TGTRLS(*CURRENT) +
+    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' 'AS400_UTF8' 'HAVE_JNI' 'HAVE_APR' +
+           '_XOPEN_SOURCE=520' 'USE_APACHE_MD5' '_REENTRANT') +
+    TEXT('jk_map.c') +
+    OPTIMIZE(40) +
+    LOCALETYPE(*LOCALEUTF) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TERASPACE(*YES) +
+    TGTRLS(*CURRENT) +
+    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' 'AS400_UTF8' 'HAVE_JNI' 'HAVE_APR' +
+           '_XOPEN_SOURCE=520' 'USE_APACHE_MD5' '_REENTRANT') +
+    TEXT('jk_md5.c') +
+    OPTIMIZE(40) +
+    LOCALETYPE(*LOCALEUTF) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TERASPACE(*YES) +
+    TGTRLS(*CURRENT) +
+    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' 'AS400_UTF8' 'HAVE_JNI' 'HAVE_APR' +
+           '_XOPEN_SOURCE=520' 'USE_APACHE_MD5' '_REENTRANT') +
+    TEXT('jk_msg_buff.c') +
+    OPTIMIZE(40) +
+    LOCALETYPE(*LOCALEUTF) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TERASPACE(*YES) +
+    TGTRLS(*CURRENT) +
+    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' 'AS400_UTF8' 'HAVE_JNI' 'HAVE_APR' +
+           '_XOPEN_SOURCE=520' 'USE_APACHE_MD5' '_REENTRANT') +
+    TEXT('jk_pool.c') +
+    OPTIMIZE(40) +
+    LOCALETYPE(*LOCALEUTF) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TERASPACE(*YES) +
+    TGTRLS(*CURRENT) +
+    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' 'AS400_UTF8' 'HAVE_JNI' 'HAVE_APR' +
+           '_XOPEN_SOURCE=520' 'USE_APACHE_MD5' '_REENTRANT') +
+    TEXT('jk_sockbuf.c') +
+    OPTIMIZE(40) +
+    LOCALETYPE(*LOCALEUTF) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TERASPACE(*YES) +
+    TGTRLS(*CURRENT) +
+    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' 'AS400_UTF8' 'HAVE_JNI' 'HAVE_APR' +
+           '_XOPEN_SOURCE=520' 'USE_APACHE_MD5' '_REENTRANT') +
+    TEXT('jk_uri_worker_map.c') +
+    OPTIMIZE(40) +
+    LOCALETYPE(*LOCALEUTF) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TERASPACE(*YES) +
+    TGTRLS(*CURRENT) +
+    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' 'AS400_UTF8' 'HAVE_JNI' 'HAVE_APR' +
+           '_XOPEN_SOURCE=520' 'USE_APACHE_MD5' '_REENTRANT') +
+    TEXT('jk_util.c') +
+    OPTIMIZE(40) +
+    LOCALETYPE(*LOCALEUTF) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TERASPACE(*YES) +
+    TGTRLS(*CURRENT) +
+    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' 'AS400_UTF8' 'HAVE_JNI' 'HAVE_APR' +
+           '_XOPEN_SOURCE=520' 'USE_APACHE_MD5' '_REENTRANT') +
+    TEXT('jk_worker.c') +
+    OPTIMIZE(40) +
+    LOCALETYPE(*LOCALEUTF) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TERASPACE(*YES) +
+    TGTRLS(*CURRENT) +
+    INCDIR('/home/apache/jk/native/common' '/QIBM/ProdData/HTTPA/Include')
+
+CRTCMOD MODULE(MOD_JK/JK_STATUS) +
+    SRCSTMF('/home/apache/jk/native/common/jk_status.c') +
+    DEFINE('AS400' 'AS400_UTF8' 'HAVE_JNI' 'HAVE_APR' +
+           '_XOPEN_SOURCE=520' 'USE_APACHE_MD5' '_REENTRANT') +
+    TEXT('jk_status.c') +
+    OPTIMIZE(40) +
+    LOCALETYPE(*LOCALEUTF) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TERASPACE(*YES) +
+    TGTRLS(*CURRENT) +
+    INCDIR('/home/apache/jk/native/common' '/QIBM/ProdData/HTTPA/Include')
+
+CRTCMOD MODULE(MOD_JK/JK_SHM) +
+    SRCSTMF('/home/apache/jk/native/common/jk_shm.c') +
+    DEFINE('AS400' 'AS400_UTF8' 'HAVE_JNI' 'HAVE_APR' +
+           '_XOPEN_SOURCE=520' 'USE_APACHE_MD5' '_REENTRANT') +
+    TEXT('jk_shm.c') +
+    OPTIMIZE(40) +
+    LOCALETYPE(*LOCALEUTF) +
+    SYSIFCOPT(*IFSIO) +
+    LANGLVL(*EXTENDED) +
+    TERASPACE(*YES) +
+    TGTRLS(*CURRENT) +
+    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 +
+             MOD_JK/JK_STATUS MOD_JK/JK_SHM) +
+      EXPORT(*SRCFILE) +
+      BNDDIR() +
+      TGTRLS(*CURRENT) +
+      SRCFILE(MOD_JK/QSRVSRC) +
+      SRCMBR(MOD_JK) +
+      USRPRF(*USER) +
+      BNDSRVPGM(QHTTPSVR/QZSRAPR QHTTPSVR/QZSRCORE +
+                QHTTPSVR/QZSRXMLP QHTTPSVR/QZSRSDBM) +
+      TEXT('Apache Tomcat mod_jk 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..6097937
--- /dev/null
+++ b/connectors/jk/native/apache-2.0/mod_jk.c
@@ -0,0 +1,3179 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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 Tomcat                                 *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ *              Henri Gomez <hgomez@apache.org>                            *
+ * Version:     $Revision$                                          *
+ ***************************************************************************/
+
+/*
+ * mod_jk: keeps all servlet 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"
+#include "ap_mpm.h"
+
+#if defined(AS400) && !defined(AS400_UTF8)
+#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 */
+
+#include "apr_version.h"
+#if (APR_MAJOR_VERSION == 0) && \
+    (APR_MINOR_VERSION <= 9) && \
+    (APR_PATCH_VERSION < 3)
+#define apr_filepath_name_get apr_filename_of_pathname
+#endif
+
+#include "apr_strings.h"
+
+/* Yes; sorta sucks - with luck we will clean this up before httpd-2.2
+ * ships, leaving AP_NEED_SET_MUTEX_PERMS def'd as 1 or 0 on all platforms.
+ */
+#ifdef AP_NEED_SET_MUTEX_PERMS
+# define JK_NEED_SET_MUTEX_PERMS AP_NEED_SET_MUTEX_PERMS
+#else
+  /* A special case for httpd-2.0 */
+# if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE) && !defined(AS400)
+#  define JK_NEED_SET_MUTEX_PERMS 1
+# else
+#  define JK_NEED_SET_MUTEX_PERMS 0
+# endif
+#endif
+
+#if JK_NEED_SET_MUTEX_PERMS
+#include "unixd.h"      /* for unixd_set_global_mutex_perms */
+#endif
+/*
+ * 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_global.h"
+#include "jk_ajp13.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"
+#include "jk_shm.h"
+#include "jk_url.h"
+
+#define JK_LOG_DEF_FILE             ("logs/mod_jk.log")
+#define JK_SHM_DEF_FILE             ("logs/jk-runtime-status")
+#define JK_ENV_HTTPS                ("HTTPS")
+#define JK_ENV_CERTS                ("SSL_CLIENT_CERT")
+#define JK_ENV_CIPHER               ("SSL_CIPHER")
+#define JK_ENV_SESSION              ("SSL_SESSION_ID")
+#define JK_ENV_KEY_SIZE             ("SSL_CIPHER_USEKEYSIZE")
+#define JK_ENV_CERTCHAIN_PREFIX     ("SSL_CLIENT_CERT_CHAIN_")
+#define JK_ENV_WORKER_NAME          ("JK_WORKER_NAME")
+#define JK_NOTE_WORKER_NAME         ("JK_WORKER_NAME")
+#define JK_NOTE_WORKER_TYPE         ("JK_WORKER_TYPE")
+#define JK_NOTE_REQUEST_DURATION    ("JK_REQUEST_DURATION")
+#define JK_NOTE_WORKER_ROUTE        ("JK_WORKER_ROUTE")
+#define JK_HANDLER          ("jakarta-servlet")
+#define JK_MAGIC_TYPE       ("application/x-jakarta-servlet")
+#define NULL_FOR_EMPTY(x)   ((x && !strlen(x)) ? NULL : x)
+#define STRNULL_FOR_NULL(x) ((x) ? (x) : "(null)")
+/*
+ * 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;
+
+/*
+ * Environment variable forward object
+ */
+typedef struct
+{
+    int has_default;
+    char *name;
+    char *value;
+} envvar_item;
+
+/*
+ * Configuration object for the mod_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;
+    char *mount_file;
+    int mount_file_reload;
+    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 *stamp_format_string;
+    char *format_string;
+    apr_array_header_t *format;
+
+    /*
+     * Setting target worker via environment
+     */
+    char *worker_indicator;
+
+    /*
+     * 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 */
+    char *certchain_prefix;     /* Client certificate chain prefix */
+
+    /*
+     * Jk Options
+     */
+    int options;
+    int exclude_options;
+
+    int strip_session;
+    /*
+     * Environment variables support
+     */
+    int envvars_in_use;
+    apr_table_t *envvars;
+    apr_table_t *envvars_def;
+    apr_array_header_t *envvar_items;
+
+    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 apr_hash_t *jk_log_fps = NULL;
+static jk_worker_env_t worker_env;
+static apr_global_mutex_t *jk_log_lock = NULL;
+static char *jk_shm_file = NULL;
+static size_t jk_shm_size = JK_SHM_DEF_SIZE;
+
+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 int 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);
+
+static void JK_METHOD ws_add_log_items(jk_ws_service_t *s,
+                                       const char *const *log_names,
+                                       const char *const *log_values,
+                                       unsigned num_of_log_items);
+
+/* ========================================================================= */
+/* 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")) {
+#if defined(AS400) && !defined(AS400_UTF8)
+            /* 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;
+
+	/* hgomez@20070516: under i5/OS this flag is not set correctly */
+	/* We should check with Rochester Labs what could be the problem */
+#ifdef AS400
+        r->sent_bodyct = 1;
+#endif
+
+    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) {
+#if defined(AS400) && !defined(AS400_UTF8)
+            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_CRIT, 0, NULL,
+                             "mod_jk: Error on ap_change_request_body_xlate, rc=%d",
+                             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;
+}
+
+static void JK_METHOD ws_flush(jk_ws_service_t *s)
+{
+#if defined(AS400) && !defined(AS400_UTF8)
+    if (s && s->ws_private) {
+        apache_private_data_t *p = s->ws_private;
+        ap_rflush(p->r);
+    }
+#endif
+}
+
+/*
+ * 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 int l)
+{
+#if defined(AS400) && !defined(AS400_UTF8)
+    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; */
+            int r = 0;
+            int ll = l;
+            const char *bb = (const char *)b;
+
+            if (!p->response_started) {
+                if (main_log)
+                    jk_log(main_log, JK_LOG_INFO,
+                           "Write without start, starting with defaults");
+                if (!s->start_response(s, 200, NULL, NULL, NULL, 0)) {
+                    return JK_FALSE;
+                }
+            }
+            if (p->r->header_only) {
+#if defined(AS400) && !defined(AS400_UTF8)
+                ap_rflush(p->r);
+#endif
+                return JK_TRUE;
+            }
+#if defined(AS400) && !defined(AS400_UTF8)
+            /* turn off response body translation */
+            rc = ap_change_response_body_xlate(p->r, 65535, 65535);
+            if (rc) {
+                ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_CRIT, 0, NULL,
+                             "mod_jk: Error on ap_change_response_body_xlate, rc=%d",
+                             rc);
+                return JK_FALSE;
+            }
+#endif
+
+            while (ll > 0 && !p->r->connection->aborted) {
+#if 0
+                /* Apache 2 output filter does not write
+                 * directly to the wire.
+                 */
+                int toSend = (ll > CHUNK_SIZE) ? CHUNK_SIZE : ll;
+                r = ap_rwrite(bb, toSend, p->r);
+#else
+                r = ap_rwrite(bb, ll, p->r);
+#endif
+                if (JK_IS_DEBUG_LEVEL(main_log))
+                    jk_log(main_log, JK_LOG_DEBUG,
+                           "written %d out of %d", r, ll);
+
+                if (r < 0)
+                    return JK_FALSE;
+                ll -= r;
+                bb += r;
+            }
+            if (ll && p->r->connection->aborted) {
+                /* Fail if there is something left to send and
+                 * the connection was aborted by the client
+                 */
+                return JK_FALSE;
+            }
+        }
+
+        return JK_TRUE;
+    }
+    return JK_FALSE;
+}
+
+static void JK_METHOD ws_add_log_items(jk_ws_service_t *s,
+                                       const char *const *log_names,
+                                       const char *const *log_values,
+                                       unsigned num_of_log_items)
+{
+    unsigned h;
+    apache_private_data_t *p = s->ws_private;
+    request_rec *r = p->r;
+
+    for (h = 0; h < num_of_log_items; h++) {
+        if (log_names[h] && log_values[h]) {
+            apr_table_setn(r->notes, log_names[h], log_values[h]);
+        }
+    }
+}
+
+/* ========================================================================= */
+/* 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);
+    if ( s ) {
+        ap_log_error(file, line, level, 0, NULL, 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;
+    int size;
+    s->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;
+    s->flush = ws_flush;
+    s->add_log_items = ws_add_log_items;
+
+    /* 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);
+    if (conf->options & JK_OPT_FWDLOCAL)
+        s->remote_addr = NULL_FOR_EMPTY(r->connection->local_ip);
+    else
+        s->remote_addr = NULL_FOR_EMPTY(r->connection->remote_ip);
+    if (conf->options & JK_OPT_FLUSHPACKETS)
+        s->flush_packets = 1;
+    else
+        s->flush_packets = 0;
+    if (conf->options & JK_OPT_FLUSHEADER)
+        s->flush_header = 1;
+    else
+        s->flush_header = 0;
+
+    if (conf->options & JK_OPT_DISABLEREUSE)
+        s->disable_reuse = 1;
+    else
+        s->disable_reuse = 0;
+
+    /* get server name */
+    s->server_name = (char *)ap_get_server_name(r);
+
+    /* get the real port (otherwise redirect failed) */
+    /* XXX: use apache API for getting server port
+     *
+     * Pre 1.2.7 versions used:
+     * s->server_port = r->connection->local_addr->port;
+     */
+    s->server_port  = ap_get_server_port(r);
+
+#if (AP_MODULE_MAGIC_AT_LEAST(20060905,0))
+    s->server_software = (char *)ap_get_server_description();
+#else
+    s->server_software = (char *)ap_get_server_version();
+#endif
+    s->method = (char *)r->method;
+    s->content_length = get_content_length(r);
+    s->is_chunked = r->read_chunked;
+    s->no_more_chunks = 0;
+#if defined(AS400) && !defined(AS400_UTF8)
+    /* 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
+
+    /* Dump all connection param so we can trace what's going to
+     * the remote tomcat
+     */
+    if (JK_IS_DEBUG_LEVEL(conf->log)) {
+        jk_log(conf->log, JK_LOG_DEBUG,
+               "Service protocol=%s method=%s host=%s addr=%s name=%s port=%d auth=%s user=%s laddr=%s raddr=%s",
+               STRNULL_FOR_NULL(s->protocol),
+               STRNULL_FOR_NULL(s->method),
+               STRNULL_FOR_NULL(s->remote_host),
+               STRNULL_FOR_NULL(s->remote_addr),
+               STRNULL_FOR_NULL(s->server_name),
+               s->server_port,
+               STRNULL_FOR_NULL(s->auth_type),
+               STRNULL_FOR_NULL(s->remote_user),
+               STRNULL_FOR_NULL(r->connection->local_ip),
+               STRNULL_FOR_NULL(r->connection->remote_ip));
+    }
+
+    /*
+     * 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_FWDURIPROXY:
+        size = strlen(r->uri);
+        s->req_uri = apr_palloc(r->pool, size * 3 + 1);
+        jk_canonenc(s->req_uri, r->uri, size, enc_path, 0, 
+                    JK_PROXYREQ_REVERSE);
+        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 (conf->options & JK_OPT_FWDCERTCHAIN) {
+                    const apr_array_header_t *t = apr_table_elts(r->subprocess_env);
+                    if (t && t->nelts) {
+                        int i;
+                        const apr_table_entry_t *elts = (const apr_table_entry_t *) t->elts;
+                        apr_array_header_t *certs = apr_array_make(r->pool, 1, sizeof(char *));
+                        *(const char **)apr_array_push(certs) = s->ssl_cert;
+                        for (i = 0; i < t->nelts; i++) {
+                            if (!elts[i].key)
+                                continue;
+                            if (!strncasecmp(elts[i].key, conf->certchain_prefix,
+                                             strlen(conf->certchain_prefix)))
+                                *(const char **)apr_array_push(certs) = elts[i].val;
+                        }
+                        s->ssl_cert = apr_array_pstrcat(r->pool, certs, '\0');
+                    }
+                }
+
+                if (s->ssl_cert) {
+                    s->ssl_cert_len = strlen(s->ssl_cert);
+                    if (JK_IS_DEBUG_LEVEL(conf->log)) {
+                        jk_log(conf->log, JK_LOG_DEBUG,
+                               "SSL client certificate (%d bytes): %s",
+                               s->ssl_cert_len, 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 = conf->envvar_items;
+            if (t && t->nelts) {
+                int i;
+                int j = 0;
+                envvar_item *elts = (envvar_item *) 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 - j] = elts[i].name;
+                    s->attributes_values[i - j] =
+                        (char *)apr_table_get(r->subprocess_env, elts[i].name);
+                    if (!s->attributes_values[i - j]) {
+                        if (elts[i].has_default) {
+                            s->attributes_values[i - j] = elts[i].value;
+                        }
+                        else {
+                            s->attributes_values[i - j] = "";
+                            s->attributes_names[i - j] = "";
+                            j++;
+                        }
+                    }
+                }
+
+                s->num_attributes = t->nelts - j;
+            }
+        }
+    }
+
+    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;
+                if (need_content_length_header &&
+                    !strcasecmp(s->headers_names[i], "content-length")) {
+                    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++;
+        }
+    }
+    s->uw_map = conf->uw_map;
+    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)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *) ap_get_module_config(s->module_config,
+                                                  &jk_module);
+    const char *c, *w;
+
+    if (worker != NULL && cmd->path == NULL ) {
+        c = context;
+        w = worker;
+    }
+    else if (worker == NULL && cmd->path != NULL) {
+        c = cmd->path;
+        w = context;
+    }
+    else {
+        if (worker == NULL)
+            return "JkMount needs a path when not defined in a location";
+        else
+            return "JkMount can not have a path when defined in a location";
+    }
+
+    if (c[0] != '/')
+        return "JkMount context should start with /";
+
+    /*
+     * Add the new worker to the alias map.
+     */
+    jk_map_put(conf->uri_to_context, c, w, NULL);
+    return NULL;
+}
+
+/*
+ * JkUnMount directive handling
+ *
+ * JkUnMount URI(context) worker
+ */
+
+static const char *jk_unmount_context(cmd_parms * cmd,
+                                      void *dummy,
+                                      const char *context,
+                                      const char *worker)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *) ap_get_module_config(s->module_config,
+                                                  &jk_module);
+    char *uri;
+    const char *c, *w;
+
+    if (worker != NULL && cmd->path == NULL ) {
+        c = context;
+        w = worker;
+    }
+    else if (worker == NULL && cmd->path != NULL) {
+        c = cmd->path;
+        w = context;
+    }
+    else {
+        if (worker == NULL)
+            return "JkUnMount needs a path when not defined in a location";
+        else
+            return "JkUnMount can not have a path when defined in a location";
+    }
+
+    if (c[0] != '/')
+        return "JkUnMount context should start with /";
+
+    uri = apr_pstrcat(cmd->temp_pool, "!", c, NULL);
+    /*
+     * Add the new worker to the alias map.
+     */
+    jk_map_put(conf->uri_to_context, uri, w, NULL);
+    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.
+     */
+    jk_map_put(conf->automount, worker, virtualhost, NULL);
+    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);
+
+    const char *err_string = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err_string != NULL) {
+        return err_string;
+    }
+
+    /* we need an absolute 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 (jk_file_exists(conf->worker_file) != JK_TRUE)
+        return "Can't find the workers file specified";
+
+    return NULL;
+}
+
+/*
+ * JkMountFile Directive Handling
+ *
+ * JkMountFile file
+ */
+
+static const char *jk_set_mount_file(cmd_parms * cmd,
+                                     void *dummy, const char *mount_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 absolute path (ap_server_root_relative does the ap_pstrdup) */
+    conf->mount_file = ap_server_root_relative(cmd->pool, mount_file);
+
+    if (conf->mount_file == NULL)
+        return "JkMountFile file name invalid";
+
+    if (jk_file_exists(conf->mount_file) != JK_TRUE)
+        return "Can't find the mount file specified";
+
+    return NULL;
+}
+
+/*
+ * JkMountFileReload Directive Handling
+ *
+ * JkMountFileReload seconds
+ */
+
+static const char *jk_set_mount_file_reload(cmd_parms * cmd,
+                                            void *dummy, const char *mount_file_reload)
+{
+    server_rec *s = cmd->server;
+    int interval;
+
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *) ap_get_module_config(s->module_config,
+                                                  &jk_module);
+
+    interval = atoi(mount_file_reload);
+    if (interval < 0) {
+        interval = 0;
+    }
+
+    conf->mount_file_reload = interval;
+
+    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;
+}
+
+/*
+ * JkShmFile Directive Handling
+ *
+ * JkShmFile file
+ */
+
+static const char *jk_set_shm_file(cmd_parms * cmd,
+                                   void *dummy, const char *shm_file)
+{
+    const char *err_string = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err_string != NULL) {
+        return err_string;
+    }
+
+    /* we need an absolute path */
+    jk_shm_file = ap_server_root_relative(cmd->pool, shm_file);
+    if (jk_shm_file == NULL)
+        return "JkShmFile file name invalid";
+
+    return NULL;
+}
+
+/*
+ * JkShmSize Directive Handling
+ *
+ * JkShmSize size in kilobytes
+ */
+
+static const char *jk_set_shm_size(cmd_parms * cmd,
+                                   void *dummy, const char *shm_size)
+{
+    int sz = 0;
+    const char *err_string = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err_string != NULL) {
+        return err_string;
+    }
+
+    sz = atoi(shm_size) * 1024;
+    if (sz < JK_SHM_DEF_SIZE)
+        sz = JK_SHM_DEF_SIZE;
+    else
+        sz = JK_SHM_ALIGN(sz);
+    jk_shm_size = (size_t)sz;
+    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)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *) ap_get_module_config(s->module_config,
+                                                  &jk_module);
+
+    conf->stamp_format_string = apr_pstrdup(cmd->pool, log_format);
+
+    return NULL;
+}
+
+
+/*
+ * JkAutoAlias Directive Handling
+ *
+ * JkAutoAlias application directory
+ */
+
+static const char *jk_set_auto_alias(cmd_parms * cmd,
+                                     void *dummy, const 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 = apr_pstrdup(cmd->pool, directory);
+
+    if (conf->alias_dir == NULL)
+        return "JkAutoAlias directory invalid";
+
+    return NULL;
+}
+
+/*
+ * JkStripSession directive handling
+ *
+ * JkStripSession On/Off
+ */
+
+static const char *jk_set_strip_session(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->strip_session = flag ? JK_TRUE : JK_FALSE;
+
+    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_NOTE_WORKER_NAME);
+}
+
+static const char *log_worker_route(request_rec * r, char *a)
+{
+    return apr_table_get(r->notes, JK_NOTE_WORKER_ROUTE);
+}
+
+
+static const char *log_request_duration(request_rec * r, char *a)
+{
+    return apr_table_get(r->notes, JK_NOTE_REQUEST_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}, {
+    'R', log_worker_route}, {
+    '\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;
+        }
+    }
+
+    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, const char *format)
+{
+    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);
+
+    return NULL;
+}
+
+
+/*
+ * JkWorkerIndicator Directive Handling
+ *
+ * JkWorkerIndicator JkWorker
+ */
+
+static const char *jk_set_worker_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->worker_indicator = apr_pstrdup(cmd->pool, indicator);
+
+    return NULL;
+}
+
+/*
+ * 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;
+}
+
+/*
+ * JkCERTCHAINPrefix Directive Handling
+ *
+ * JkCERTCHAINPrefix SSL_CLIENT_CERT_CHAIN_
+ */
+
+static const char *jk_set_certchain_prefix(cmd_parms * cmd,
+                                           void *dummy, const char *prefix)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *) ap_get_module_config(s->module_config,
+                                                  &jk_module);
+
+    conf->certchain_prefix = apr_pstrdup(cmd->pool, prefix);
+
+    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
+ * +ForwardSSLCertChain      => Forward SSL Cert Chain
+ * -ForwardSSLCertChain      => Don't Forward SSL Cert Chain (default)
+ */
+
+static 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 (action == '-' && !strncasecmp(w, "ForwardURI", strlen("ForwardURI")))
+            return apr_pstrcat(cmd->pool, "JkOptions: Illegal option '-", w,
+                               "': ForwardURI* options can not be disabled", NULL);
+
+        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, "ForwardURIProxy")) {
+            opt = JK_OPT_FWDURIPROXY;
+            mask = JK_OPT_FWDURIMASK;
+        }
+        else if (!strcasecmp(w, "ForwardDirectories")) {
+            opt = JK_OPT_FWDDIRS;
+        }
+        else if (!strcasecmp(w, "ForwardLocalAddress")) {
+            opt = JK_OPT_FWDLOCAL;
+        }
+        else if (!strcasecmp(w, "FlushPackets")) {
+            opt = JK_OPT_FLUSHPACKETS;
+        }
+        else if (!strcasecmp(w, "FlushHeader")) {
+            opt = JK_OPT_FLUSHEADER;
+        }
+        else if (!strcasecmp(w, "DisableReuse")) {
+            opt = JK_OPT_DISABLEREUSE;
+        }
+        else if (!strcasecmp(w, "ForwardSSLCertChain")) {
+            opt = JK_OPT_FWDCERTCHAIN;
+        }
+        else
+            return apr_pstrcat(cmd->pool, "JkOptions: Illegal option '", w,
+                               "'", NULL);
+
+        conf->options &= ~mask;
+
+        if (action == '-') {
+            conf->exclude_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;
+
+    /* env_name is mandatory, default_value is optional.
+     * No value means send the attribute only, if the env var is set during runtime.
+     */
+    apr_table_setn(conf->envvars, env_name, default_value ? default_value : "");
+    apr_table_setn(conf->envvars_def, env_name, default_value ? "1" : "0");
+
+    return NULL;
+}
+
+/*
+ * JkWorkerProperty Directive Handling
+ *
+ * JkWorkerProperty name=value
+ */
+
+static const char *jk_set_worker_property(cmd_parms * cmd,
+                                          void *dummy,
+                                          const char *line)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *) ap_get_module_config(s->module_config,
+                                                  &jk_module);
+
+    const char *err_string = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err_string != NULL) {
+        return err_string;
+    }
+
+    if (jk_map_read_property(conf->worker_properties, line, 1, conf->log) == JK_FALSE)
+        return apr_pstrcat(cmd->temp_pool, "Invalid JkWorkerProperty ", line, NULL);
+
+    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 Tomcat servlet containers"),
+
+    /*
+     * JkMountFile specifies a full path to the location of the
+     * uriworker properties file.
+     *
+     * This file defines the different mapping for workers used by apache
+     * to redirect servlet requests.
+     */
+    AP_INIT_TAKE1("JkMountFile", jk_set_mount_file, NULL, RSRC_CONF,
+                  "the name of a mount file for the Tomcat servlet uri mapping"),
+
+    /*
+     * JkMountFileReload specifies the reload check interval for the
+     * uriworker properties file.
+     *
+     * Default value is: JK_URIMAP_DEF_RELOAD
+     */
+    AP_INIT_TAKE1("JkMountFileReload", jk_set_mount_file_reload, NULL, RSRC_CONF,
+                  "the reload check interval of the mount file"),
+
+    /*
+     * 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_TAKE12("JkMount", jk_mount_context, NULL, RSRC_CONF|ACCESS_CONF,
+                   "A mount point from a context to a Tomcat worker"),
+
+    /*
+     * JkUnMount unmounts a url prefix to a worker (the worker need to be
+     * defined in the worker properties file.
+     */
+    AP_INIT_TAKE12("JkUnMount", jk_unmount_context, NULL, RSRC_CONF|ACCESS_CONF,
+                   "A no 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"),
+
+    /*
+     * JkStripSession specifies if mod_jk should strip the ;jsessionid
+     * from the unmapped urls
+     */
+    AP_INIT_FLAG("JkStripSession", jk_set_strip_session, NULL, RSRC_CONF,
+                 "Should the server strip the jsessionid from unmapped URLs"),
+
+    /*
+     * 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 Tomcat module log file"),
+
+    AP_INIT_TAKE1("JkShmFile", jk_set_shm_file, NULL, RSRC_CONF,
+                  "Full path to the Tomcat module shared memory file"),
+
+    AP_INIT_TAKE1("JkShmSize", jk_set_shm_size, NULL, RSRC_CONF,
+                  "Size of the shared memory file in KBytes"),
+
+    AP_INIT_TAKE1("JkLogLevel", jk_set_log_level, NULL, RSRC_CONF,
+                  "The Tomcat module log level, can be debug, "
+                  "info, error or emerg"),
+    AP_INIT_TAKE1("JkLogStampFormat", jk_set_log_fmt, NULL, RSRC_CONF,
+                  "The Tomcat module log format, follow strftime synthax"),
+    AP_INIT_TAKE1("JkRequestLogFormat", jk_set_request_log_format, NULL,
+                  RSRC_CONF,
+                  "The 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 mod_jk module automatic context apache alias directory"),
+
+    /*
+     * Enable worker name to be set in an environment variable.
+     * this way one can use LocationMatch together with mod_end,
+     * mod_setenvif and mod_rewrite to set the target worker.
+     * Use this in combination with SetHandler jakarta-servlet to
+     * make mod_jk the handler for the request.
+     *
+     */
+    AP_INIT_TAKE1("JkWorkerIndicator", jk_set_worker_indicator, NULL, RSRC_CONF,
+                  "Name of the Apache environment that contains the worker name"),
+
+    /*
+     * 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_TAKE1("JkCERTCHAINPrefix", jk_set_certchain_prefix, NULL, RSRC_CONF,
+                  "Name of the Apache environment (prefix) that contains SSL client chain certificates"),
+    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
+     * +ForwardSSLCertChain      => Forward SSL certificate chain
+     * -ForwardSSLCertChain      => Don't forward SSL certificate chain
+     */
+    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_TAKE12("JkEnvVar", jk_add_env_var, NULL, RSRC_CONF,
+                  "Adds a name of environment variable and an optional value "
+                  "that should be sent to servlet-engine"),
+
+    AP_INIT_RAW_ARGS("JkWorkerProperty", jk_set_worker_property,
+                     NULL, RSRC_CONF,
+                     "Set workers.properties formated directive"),
+
+    {NULL}
+};
+
+/* ========================================================================= */
+/* The JK module handlers                                                    */
+/* ========================================================================= */
+
+/** Util - cleanup shmem.
+ */
+static apr_status_t jk_cleanup_shmem(void *data)
+{
+    jk_shm_close();
+    return APR_SUCCESS;
+}
+
+/** 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;
+    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);
+    JK_TRACE_ENTER(xconf->log);
+    if (apr_table_get(r->subprocess_env, "no-jk")) {
+        if (JK_IS_DEBUG_LEVEL(xconf->log))
+            jk_log(xconf->log, JK_LOG_DEBUG,
+                   "Into handler no-jk env var detected for uri=%s, declined",
+                   r->uri);
+
+        JK_TRACE_EXIT(xconf->log);
+        return DECLINED;
+    }
+
+    /* Was the option to forward directories to Tomcat set? */
+    if (!dmt && !(xconf->options & JK_OPT_FWDDIRS)) {
+        JK_TRACE_EXIT(xconf->log);
+        return DECLINED;
+    }
+    worker_name = apr_table_get(r->notes, JK_NOTE_WORKER_NAME);
+
+    /* Set up r->read_chunked flags for chunked encoding, if present */
+    if ((rc = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK)) != APR_SUCCESS) {
+        JK_TRACE_EXIT(xconf->log);
+        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.
+         */
+        worker_name = apr_table_get(r->subprocess_env, xconf->worker_indicator);
+        if (worker_name) {
+          /* The JkWorkerIndicator environment variable has
+           * been used to explicitely set the worker without JkMount.
+           * This is useful in combination with LocationMatch or mod_rewrite.
+           */
+            if (JK_IS_DEBUG_LEVEL(xconf->log))
+                jk_log(xconf->log, JK_LOG_DEBUG,
+                       "Retrieved worker (%s) from env %s for %s",
+                       worker_name, xconf->worker_indicator, r->uri);
+        }
+        else 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.worker_list[0];
+            if (JK_IS_DEBUG_LEVEL(xconf->log))
+                jk_log(xconf->log, JK_LOG_DEBUG,
+                       "Single worker (%s) configuration for %s",
+                       worker_name, r->uri);
+        }
+        else {
+            worker_name = map_uri_to_worker(xconf->uw_map, r->uri, xconf->log);
+            if (worker_name == NULL && worker_env.num_of_workers) {
+                worker_name = worker_env.worker_list[0];
+                if (JK_IS_DEBUG_LEVEL(xconf->log))
+                    jk_log(xconf->log, JK_LOG_DEBUG,
+                           "Using first worker (%s) from %d workers for %s",
+                           worker_name, worker_env.num_of_workers, r->uri);
+            }
+        }
+        if (worker_name)
+            apr_table_setn(r->notes, JK_NOTE_WORKER_NAME, worker_name);
+    }
+
+    if (JK_IS_DEBUG_LEVEL(xconf->log))
+       jk_log(xconf->log, JK_LOG_DEBUG, "Into handler %s worker=%s"
+              " r->proxyreq=%d",
+              r->handler, worker_name, r->proxyreq);
+
+    /* If this is a proxy request, we'll notify an error */
+    if (r->proxyreq) {
+        jk_log(xconf->log, JK_LOG_INFO, "Proxy request for worker=%s"
+              " is not allowed",
+              worker_name);
+        JK_TRACE_EXIT(xconf->log);
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    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) {
+            jk_log(xconf->log, JK_LOG_INFO, "Client connection aborted for"
+                   " worker=%s",
+                   worker_name);
+            JK_TRACE_EXIT(xconf->log);
+            return OK;
+        }
+
+        if (worker) {
+            apr_time_t request_begin = 0;
+            int is_error = HTTP_INTERNAL_SERVER_ERROR;
+            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;
+
+            wc_maintain(xconf->log);
+
+            jk_init_ws_service(&s);
+            /* Update retries for this worker */
+            s.retries = worker->retries;
+            s.ws_private = &private_data;
+            s.pool = &private_data.p;
+            apr_table_setn(r->notes, JK_NOTE_WORKER_TYPE,
+                           wc_get_name_for_type(worker->type, xconf->log));
+
+            if (xconf->format != NULL) {
+                request_begin = apr_time_now();
+            }
+
+            if (init_ws_service(&private_data, &s, xconf)) {
+                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
+                 */
+
+                /* worker->get_endpoint might fail if we are out of memory so check */
+                /* and handle it */
+                if (worker->get_endpoint(worker, &end, xconf->log)) {
+                    rc = end->service(end, &s, xconf->log,
+                                      &is_error);
+                    end->done(&end, xconf->log);
+                    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;
+                            }
+                        }
+                    }
+                }
+                else {            /* this means we couldn't get an endpoint */
+                    jk_log(xconf->log, JK_LOG_ERROR, "Could not get endpoint"
+                           " for worker=%s",
+                           worker_name);
+                    rc = 0;       /* just to make sure that we know we've failed */
+                }
+            }
+            else {
+                jk_log(xconf->log, JK_LOG_ERROR, "Could not init service"
+                       " for worker=%s",
+                       worker_name);
+                jk_close_pool(&private_data.p);
+                JK_TRACE_EXIT(xconf->log);
+                return HTTP_INTERNAL_SERVER_ERROR;
+            }
+            if (xconf->format != NULL) {
+                long micro, seconds;
+                char *duration = NULL;
+                apr_time_t rd = apr_time_now() - request_begin;
+                seconds = (long)apr_time_sec(rd);
+                micro = (long)(rd - apr_time_from_sec(seconds));
+
+                duration = apr_psprintf(r->pool, "%.1ld.%.6ld", seconds, micro);
+                apr_table_setn(r->notes, JK_NOTE_REQUEST_DURATION, duration);
+                if (s.route && *s.route)
+                    apr_table_setn(r->notes, JK_NOTE_WORKER_ROUTE, s.route);
+
+                request_log_transaction(r, xconf);
+            }
+
+            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 */
+
+/* hgomez@20070516 : under i5/OS sent_bodyct is not set correctly */
+/*                   check for header_only to see if there was a body */
+                if (!r->sent_bodyct && r->status >= HTTP_BAD_REQUEST) {
+                    jk_log(xconf->log, JK_LOG_INFO, "No body with status=%d"
+                           " for worker=%s",
+                           r->status, worker_name);
+                    JK_TRACE_EXIT(xconf->log);
+                    return r->status;
+                }
+                if (JK_IS_DEBUG_LEVEL(xconf->log))
+                    jk_log(xconf->log, JK_LOG_DEBUG, "Service finished"
+                           " with status=%d for worker=%s",
+                           r->status, worker_name);
+                JK_TRACE_EXIT(xconf->log);
+                return OK;      /* NOT r->status, even if it has changed. */
+            }
+            else if (rc == JK_CLIENT_ERROR) {
+                if (is_error != HTTP_REQUEST_ENTITY_TOO_LARGE)
+                    r->connection->aborted = 1;
+                jk_log(xconf->log, JK_LOG_INFO, "Aborting connection"
+                       " for worker=%s",
+                       worker_name);
+                JK_TRACE_EXIT(xconf->log);
+                return is_error;
+            }
+            else {
+                jk_log(xconf->log, JK_LOG_INFO, "Service error=%d"
+                       " for worker=%s",
+                       rc, worker_name);
+                JK_TRACE_EXIT(xconf->log);
+                return is_error;
+            }
+        }
+        else {
+            jk_log(xconf->log, JK_LOG_INFO, "Could not find a worker"
+                   " for worker name=%s",
+                   worker_name);
+            JK_TRACE_EXIT(xconf->log);
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+    }
+
+    JK_TRACE_EXIT(xconf->log);
+    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 && conf->worker_properties) {
+            /* 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. */
+            if (conf->was_initialized)
+                wc_close(NULL);
+            if (conf->worker_properties)
+                jk_map_free(&conf->worker_properties);
+            if (conf->uri_to_context)
+                jk_map_free(&conf->uri_to_context);
+            if (conf->automount)
+                jk_map_free(&conf->automount);
+            if (conf->uw_map)
+                uri_worker_map_free(&conf->uw_map, NULL);
+            conf->was_initialized   = JK_FALSE;
+            conf->worker_properties = 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;
+    jk_map_alloc(&c->worker_properties);
+    c->worker_file = NULL;
+    c->mount_file = NULL;
+    c->log_file = NULL;
+    c->log = NULL;
+    c->alias_dir = NULL;
+    c->stamp_format_string = NULL;
+    c->format_string = NULL;
+    c->format = NULL;
+    c->mountcopy = JK_FALSE;
+    c->exclude_options = 0;
+    c->was_initialized = JK_FALSE;
+
+    if (s->is_virtual) {
+        c->mount_file_reload = JK_UNSET;
+        c->log_level = JK_UNSET;
+        c->options = 0;
+        c->worker_indicator = NULL;
+        c->ssl_enable = JK_UNSET;
+        c->https_indicator = NULL;
+        c->certs_indicator = NULL;
+        c->cipher_indicator = NULL;
+        c->certchain_prefix = NULL;
+        c->session_indicator = NULL;
+        c->key_size_indicator = NULL;
+        c->strip_session = JK_UNSET;
+    } else {
+        c->mount_file_reload = JK_URIMAP_DEF_RELOAD;
+        c->log_level = JK_LOG_DEF_LEVEL;
+        c->options = JK_OPT_FWDURIDEFAULT;
+        c->worker_indicator = JK_ENV_WORKER_NAME;
+        /*
+         * 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 = JK_ENV_HTTPS;
+        c->certs_indicator = JK_ENV_CERTS;
+        c->cipher_indicator = JK_ENV_CIPHER;
+        c->certchain_prefix = JK_ENV_CERTCHAIN_PREFIX;
+        c->session_indicator = JK_ENV_SESSION;
+        c->key_size_indicator = JK_ENV_KEY_SIZE;
+        c->strip_session = JK_FALSE;
+    }
+
+    if (!jk_map_alloc(&(c->uri_to_context))) {
+        jk_error_exit(APLOG_MARK, APLOG_EMERG, s, p, "Memory error");
+    }
+    if (!jk_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->envvars_def = apr_table_make(p, 0);
+    c->envvar_items = apr_array_make(p, 0, sizeof(envvar_item));
+
+    c->s = s;
+    jk_map_put(c->worker_properties, "ServerRoot", ap_server_root, NULL);
+    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 = jk_map_size(src);
+    int i;
+    for (i = 0; i < sz; i++) {
+        const char *name = jk_map_name_at(src, i);
+        if (jk_map_get(dst, name, NULL) == NULL) {
+            if (!jk_map_put(dst, name,
+                            apr_pstrdup(p, jk_map_get_string(src, name, NULL)),
+                            NULL)) {
+                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 (!overrides->log_file)
+        overrides->log_file = base->log_file;
+    if (overrides->log_level == JK_UNSET)
+        overrides->log_level = base->log_level;
+
+    if (!overrides->stamp_format_string)
+        overrides->stamp_format_string = base->stamp_format_string;
+    if (!overrides->format_string)
+        overrides->format_string = base->format_string;
+
+    if (!overrides->worker_indicator)
+        overrides->worker_indicator = base->worker_indicator;
+
+    if (overrides->ssl_enable == JK_UNSET)
+        overrides->ssl_enable = base->ssl_enable;
+    if (!overrides->https_indicator)
+        overrides->https_indicator = base->https_indicator;
+    if (!overrides->certs_indicator)
+        overrides->certs_indicator = base->certs_indicator;
+    if (!overrides->cipher_indicator)
+        overrides->cipher_indicator = base->cipher_indicator;
+    if (!overrides->certchain_prefix)
+        overrides->certchain_prefix = base->certchain_prefix;
+    if (!overrides->session_indicator)
+        overrides->session_indicator = base->session_indicator;
+    if (!overrides->key_size_indicator)
+        overrides->key_size_indicator = base->key_size_indicator;
+
+    if (!overrides->secret_key)
+        overrides->secret_key = base->secret_key;
+
+    overrides->options |= (base->options & ~base->exclude_options);
+
+    if (base->envvars_in_use) {
+        int i;
+        const apr_array_header_t *arr;
+        const apr_table_entry_t *elts;
+
+        arr = apr_table_elts(base->envvars);
+        if (arr) {
+            overrides->envvars_in_use = JK_TRUE;
+            elts = (const apr_table_entry_t *)arr->elts;
+            for (i = 0; i < arr->nelts; ++i) {
+                if (!apr_table_get(overrides->envvars, elts[i].key)) {
+                    apr_table_setn(overrides->envvars, elts[i].key, elts[i].val);
+                }
+            }
+        }
+        arr = apr_table_elts(base->envvars_def);
+        if (arr) {
+            overrides->envvars_in_use = JK_TRUE;
+            elts = (const apr_table_entry_t *)arr->elts;
+            for (i = 0; i < arr->nelts; ++i) {
+                if (!apr_table_get(overrides->envvars_def, elts[i].key)) {
+                    apr_table_setn(overrides->envvars_def, elts[i].key, elts[i].val);
+                }
+            }
+        }
+    }
+
+    if (overrides->mount_file_reload == JK_UNSET)
+        overrides->mount_file_reload = base->mount_file_reload;
+    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 (!overrides->mount_file)
+            overrides->mount_file = base->mount_file;
+        if (!overrides->alias_dir)
+            overrides->alias_dir = base->alias_dir;
+    }
+    if (overrides->strip_session == JK_UNSET)
+        overrides->strip_session = base->strip_session;
+
+    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;
+            jk_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_ERR, 0, NULL,
+                                 "mod_jk: jk_log_to_file %s failed: %s",
+                                 what, error);
+                }
+#if defined(WIN32)
+                apr_file_putc('\r', p->jklogfp);
+#endif
+                apr_file_putc('\n', p->jklogfp);
+                rv = apr_global_mutex_unlock(jk_log_lock);
+                if (rv != APR_SUCCESS) {
+                    ap_log_error(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)
+{
+    /* hgomez@20070425 */
+    /* Clean up pointer content */
+    if (d != NULL)
+        *(jk_logger_t **)d = 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;
+    apr_file_t *jklogfp;
+    piped_log *pl;
+    jk_logger_t *jkl;
+    jk_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 (conf->log_file == NULL) {
+        conf->log_file = ap_server_root_relative(p, JK_LOG_DEF_FILE);
+        if (conf->log_file)
+            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
+                         "No JkLogFile defined in httpd.conf. "
+                         "Using default %s", conf->log_file);
+    }
+    if (*(conf->log_file) == '\0') {
+        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EBADPATH, s,
+                     "mod_jk: Invalid JkLogFile EMPTY");
+        conf->log = main_log;
+        return 0;
+    }
+
+    jklogfp = apr_hash_get(jk_log_fps, conf->log_file, APR_HASH_KEY_STRING);
+    if (!jklogfp) {
+        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;
+            }
+            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(&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(jklogfp);
+        apr_hash_set(jk_log_fps, conf->log_file, APR_HASH_KEY_STRING, jklogfp);
+    }
+    conf->jklogfp = jklogfp;
+    jkl = (jk_logger_t *)apr_palloc(p, sizeof(jk_logger_t));
+    flp = (jk_file_logger_t *) apr_palloc(p, sizeof(jk_file_logger_t));
+    if (jkl && flp) {
+        jkl->log = jk_log_to_file;
+        jkl->level = conf->log_level;
+        jkl->log_fmt = conf->stamp_format_string;
+        jkl->logger_private = flp;
+        flp->jklogfp = conf->jklogfp;
+        conf->log = jkl;
+        if (main_log == NULL) {
+            main_log = conf->log;
+
+            /* hgomez@20070425 */
+            /* Shouldn't we clean both conf->log and main_log ?                   */
+            /* Also should we pass pointer (ie: main_log) or handle (*main_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)
+{
+    jk_server_conf_t *conf;
+    apr_status_t rv;
+    int rc;
+
+    conf = ap_get_module_config(s->module_config, &jk_module);
+
+    rv = apr_global_mutex_child_init(&jk_log_lock, NULL, pconf);
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
+                     "mod_jk: could not init JK log lock in child");
+    }
+
+    JK_TRACE_ENTER(conf->log);
+
+    if ((rc = jk_shm_attach(jk_shm_file, jk_shm_size, conf->log)) == 0) {
+        if (JK_IS_DEBUG_LEVEL(conf->log))
+            jk_log(conf->log, JK_LOG_DEBUG, "Attached shm:%s",
+                   jk_shm_name());
+            apr_pool_cleanup_register(pconf, conf->log, jk_cleanup_shmem,
+                                     jk_cleanup_shmem);
+    }
+    else
+        jk_log(conf->log, JK_LOG_ERROR, "Attaching shm:%s errno=%d",
+               jk_shm_name(), rc);
+
+    if (JK_IS_DEBUG_LEVEL(conf->log))
+        jk_log(conf->log, JK_LOG_DEBUG, "Initialized %s", JK_EXPOSED_VERSION);
+    JK_TRACE_EXIT(conf->log);
+}
+
+/** 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 int init_jk(apr_pool_t * pconf, jk_server_conf_t * conf,
+                    server_rec * s)
+{
+    int rc;
+    int is_threaded;
+    int mpm_threads = 1;
+
+    /*     jk_map_t *init_map = NULL; */
+    jk_map_t *init_map = conf->worker_properties;
+
+#if !defined(WIN32) && !defined(NETWARE)
+    if (!jk_shm_file) {
+        jk_shm_file = ap_server_root_relative(pconf, JK_SHM_DEF_FILE);
+        if (jk_shm_file)
+            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
+                         "No JkShmFile defined in httpd.conf. "
+                         "Using default %s", jk_shm_file);
+    }
+#endif
+    if ((rc = jk_shm_open(jk_shm_file, jk_shm_size, conf->log)) == 0) {
+        if (JK_IS_DEBUG_LEVEL(conf->log))
+            jk_log(conf->log, JK_LOG_DEBUG, "Initialized shm:%s",
+                   jk_shm_name(), rc);
+            apr_pool_cleanup_register(pconf, conf->log, jk_cleanup_shmem,
+                                      jk_cleanup_shmem);
+    }
+    else
+        jk_log(conf->log, JK_LOG_ERROR,
+               "Initializing shm:%s errno=%d. Load balancing workers will not function properly.",
+               jk_shm_name(), rc);
+
+    /* Set default connection cache size for multi-threaded MPMs */
+    if (ap_mpm_query(AP_MPMQ_IS_THREADED, &is_threaded) == APR_SUCCESS &&
+        is_threaded != AP_MPMQ_NOT_SUPPORTED) {
+        if (ap_mpm_query(AP_MPMQ_MAX_THREADS, &mpm_threads) != APR_SUCCESS)
+            mpm_threads = 1;
+    }
+    if (JK_IS_DEBUG_LEVEL(conf->log))
+        jk_log(conf->log, JK_LOG_DEBUG,
+               "Setting default connection pool max size to %d",
+               mpm_threads);
+    jk_set_worker_def_cache_size(mpm_threads);
+
+    if ((conf->worker_file != NULL) &&
+        !jk_map_read_properties(init_map, conf->worker_file, NULL, 1, conf->log)) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s,
+                     "Error in reading worker properties from '%s'",
+                     conf->worker_file);
+        return JK_FALSE;
+    }
+
+    if (jk_map_resolve_references(init_map, "worker.", 1, 1, conf->log) == JK_FALSE) {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s,
+                     "Error in resolving configuration references");
+        return JK_FALSE;
+    }
+
+    /* 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 */
+#if (AP_MODULE_MAGIC_AT_LEAST(20060905,0))
+    worker_env.server_name = (char *)ap_get_server_description();
+#else
+    worker_env.server_name = (char *)ap_get_server_version();
+#endif
+
+    if (wc_open(init_map, &worker_env, conf->log)) {
+        ap_add_version_component(pconf, JK_EXPOSED_VERSION);
+        jk_log(conf->log, JK_LOG_INFO,
+               "%s initialized",
+               JK_EXPOSED_VERSION);
+    }
+    else {
+        ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s,
+                     "Error in creating the workers."
+                     " Please consult your mod_jk log file '%s'.", conf->log_file);
+        return JK_FALSE;
+    }
+    return JK_TRUE;
+}
+
+static int jk_post_config(apr_pool_t * pconf,
+                          apr_pool_t * plog,
+                          apr_pool_t * ptemp, server_rec * s)
+{
+    apr_status_t rv;
+    jk_server_conf_t *conf;
+    server_rec *srv = s;
+    const char *err_string = NULL;
+
+    /* 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 JK_NEED_SET_MUTEX_PERMS
+    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
+
+    jk_log_fps = apr_hash_make(pconf);
+
+    if (!s->is_virtual) {
+        conf = (jk_server_conf_t *)ap_get_module_config(s->module_config,
+                                                        &jk_module);
+        if (!conf->was_initialized) {
+            conf->was_initialized = JK_TRUE;
+            /* step through the servers and open each jk logfile
+             * and do additional post config initialization.
+             */
+            for (; srv; srv = srv->next) {
+                jk_server_conf_t *sconf = (jk_server_conf_t *)ap_get_module_config(srv->module_config,
+                                                                                   &jk_module);
+                if (open_jklog(srv, pconf))
+                    return HTTP_INTERNAL_SERVER_ERROR;
+                if (sconf) {
+                    if (!uri_worker_map_alloc(&(sconf->uw_map),
+                                              sconf->uri_to_context, sconf->log))
+                        jk_error_exit(APLOG_MARK, APLOG_EMERG, srv,
+                                      srv->process->pool, "Memory error");
+                    if (sconf->mount_file) {
+                        sconf->uw_map->fname = sconf->mount_file;
+                        sconf->uw_map->reload = sconf->mount_file_reload;
+                        uri_worker_map_load(sconf->uw_map, sconf->log);
+                    }
+                    if (sconf->format_string) {
+                        sconf->format =
+                            parse_request_log_string(pconf, sconf->format_string, &err_string);
+                        if (sconf->format == NULL)
+                            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+                                         "JkRequestLogFormat format array NULL");
+                    }
+                    sconf->options &= ~sconf->exclude_options;
+                    if (sconf->envvars_in_use) {
+                        int i;
+                        const apr_array_header_t *arr;
+                        const apr_table_entry_t *elts;
+                        envvar_item *item;
+                        const char *envvar_def;
+
+                        arr = apr_table_elts(sconf->envvars);
+                        if (arr) {
+                            elts = (const apr_table_entry_t *)arr->elts;
+                            for (i = 0; i < arr->nelts; ++i) {
+                                item = (envvar_item *)apr_array_push(sconf->envvar_items);
+                                if (!item)
+                                    return HTTP_INTERNAL_SERVER_ERROR;
+                                item->name = elts[i].key;
+                                envvar_def = apr_table_get(sconf->envvars_def, elts[i].key);
+                                if (envvar_def && !strcmp("1", envvar_def) ) {
+                                    item->value = elts[i].val;
+                                    item->has_default = 1;
+                                }
+                                else {
+                                    item->value = "";
+                                    item->has_default = 0;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            if (init_jk(pconf, conf, s) == JK_FALSE)
+                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) {
+            const char *worker;
+            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
+                 */
+                if (JK_IS_DEBUG_LEVEL(conf->log))
+                    jk_log(conf->log, JK_LOG_DEBUG,
+                           "Manually mapped, no need to call uri_to_worker");
+                return DECLINED;
+            }
+
+            if (apr_table_get(r->subprocess_env, "no-jk")) {
+                if (JK_IS_DEBUG_LEVEL(conf->log))
+                    jk_log(conf->log, JK_LOG_DEBUG,
+                           "Into translate no-jk env var detected for uri=%s, declined",
+                           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 && r->main->handler != 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) {
+                    if (JK_IS_DEBUG_LEVEL(conf->log))
+                        jk_log(conf->log, JK_LOG_DEBUG,
+                               "JkAutoAlias, no DirectoryIndex file for URI %s",
+                               r->uri);
+                    return DECLINED;
+                }
+            }
+
+            worker = map_uri_to_worker(conf->uw_map, r->uri, conf->log);
+
+            if (worker) {
+                r->handler = apr_pstrdup(r->pool, JK_HANDLER);
+                apr_table_setn(r->notes, JK_NOTE_WORKER_NAME, worker);
+
+                /* This could be a sub-request, possibly from mod_dir */
+                /* Also add the the HANDLER to the main request */
+                if (r->main) {
+                    r->main->handler = apr_pstrdup(r->main->pool, JK_HANDLER);
+                    apr_table_setn(r->main->notes, JK_NOTE_WORKER_NAME, 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 */
+                if (JK_IS_DEBUG_LEVEL(conf->log))
+                    jk_log(conf->log, JK_LOG_DEBUG,
+                           "check alias_dir: %s",
+                           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) {
+                            if (JK_IS_DEBUG_LEVEL(conf->log))
+                                jk_log(conf->log, JK_LOG_DEBUG,
+                                       "AutoAlias child_dir: %s",
+                                       child_dir);
+                            if (!strcasecmp(child_dir, "WEB-INF")
+                                || !strcasecmp(child_dir, "META-INF")) {
+                                if (JK_IS_DEBUG_LEVEL(conf->log))
+                                    jk_log(conf->log, JK_LOG_DEBUG,
+                                           "AutoAlias HTTP_NOT_FOUND for URI: %s",
+                                           r->uri);
+                                return HTTP_NOT_FOUND;
+                            }
+                        }
+                    }
+                    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) {
+                                if (JK_IS_DEBUG_LEVEL(conf->log))
+                                    jk_log(conf->log, JK_LOG_DEBUG,
+                                           "AutoAlias OK for file: %s",
+                                           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")) {
+                                if (JK_IS_DEBUG_LEVEL(conf->log))
+                                    jk_log(conf->log, JK_LOG_DEBUG,
+                                           "AutoAlias HTTP_FORBIDDEN for URI: %s",
+                                           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_NOTE_WORKER_NAME)) {
+        jk_server_conf_t *conf =
+            (jk_server_conf_t *) ap_get_module_config(r->server->
+                                                      module_config,
+                                                      &jk_module);
+
+        if (conf) {
+            const char *worker;
+            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
+                 */
+                if (JK_IS_DEBUG_LEVEL(conf->log))
+                    jk_log(conf->log, JK_LOG_DEBUG,
+                           "Manually mapped, no need to call uri_to_worker");
+                return DECLINED;
+            }
+
+            if (apr_table_get(r->subprocess_env, "no-jk")) {
+                if (JK_IS_DEBUG_LEVEL(conf->log))
+                    jk_log(conf->log, JK_LOG_DEBUG,
+                           "Into map_to_storage no-jk env var detected for uri=%s, declined",
+                           r->uri);
+
+                return DECLINED;
+            }
+
+            worker = map_uri_to_worker(conf->uw_map, r->uri, conf->log);
+
+            if (worker) {
+                r->handler = apr_pstrdup(r->pool, JK_HANDLER);
+                apr_table_setn(r->notes, JK_NOTE_WORKER_NAME, worker);
+
+                /* This could be a sub-request, possibly from mod_dir */
+                if (r->main)
+                    apr_table_setn(r->main->notes, JK_NOTE_WORKER_NAME, worker);
+
+            }
+            else if (conf->strip_session == JK_TRUE) {
+                char *jsessionid;
+                if (r->uri) {
+                    jsessionid = strstr(r->uri, JK_PATH_SESSION_IDENTIFIER);
+                    if (jsessionid) {
+                        if (JK_IS_DEBUG_LEVEL(conf->log))
+                            jk_log(conf->log, JK_LOG_DEBUG,
+                                   "removing session identifier [%s] for non servlet url [%s]",
+                                   jsessionid, r->uri);
+                        *jsessionid = '\0';
+                    }
+                }
+                if (r->filename) {
+                    jsessionid = strstr(r->filename, JK_PATH_SESSION_IDENTIFIER);
+                    if (jsessionid)
+                        *jsessionid = '\0';
+                }
+                return DECLINED;
+            }
+        }
+    }
+
+    if (apr_table_get(r->notes, JK_NOTE_WORKER_NAME)) {
+
+        /* 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) ||
+             !strlen(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..2ebc327
--- /dev/null
+++ b/connectors/jk/native/apache-2.0/mod_jk.dsp
@@ -0,0 +1,287 @@
+# 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 /Oy- /Zi /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /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" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "HAVE_APR" /Fd"Release/mod_jk_src" /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 ws2_32.lib mswsock.lib /nologo /base:"0x6A6B0000" /subsystem:windows /dll /debug /machine:I386 /out:"Release/mod_jk.so" /libpath:"$(APACHE2_HOME)\Release" /libpath:"$(APACHE2_HOME)\srclib\apr\Release" /libpath:"$(APACHE2_HOME)\srclib\apr-util\Release" /libpath:"$(APACHE2_HOME)\lib" /opt:ref
+
+!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 /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" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "HAVE_APR" /Fd"Debug/mod_jk_src" /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 ws2_32.lib mswsock.lib /nologo /base:"0x6A6B0000" /subsystem:windows /dll /incremental:no /debug /machine:I386 /out:"Debug/mod_jk.so" /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_shm.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_sockbuf.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_status.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_shm.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_sockbuf.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_status.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/.indent.pro b/connectors/jk/native/common/.indent.pro
new file mode 100644
index 0000000..87ca419
--- /dev/null
+++ b/connectors/jk/native/common/.indent.pro
@@ -0,0 +1,18 @@
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1 -nut -ncs
+-Tjk_env_t
+-Tjk_worker_t
+-Tjk_worker_env_t
+-Tjk_endpoint_t
+-Tjk_channel_t
+-Tjk_sockbuf_t
+-Tjk_msg_t
+-Tjk_msg_buf_t
+-Tjk_map_t
+-Tjk_uri_worker_map_t
+-Tjk_pool_t
+-Tjk_pool_atom_t
+-Tjk_logger_t
+-Tjk_ws_service_t
+-Tjk_context_item_t
+-Tjk_context_t
+-Tjk_login_service_t
diff --git a/connectors/jk/native/common/Makefile.in b/connectors/jk/native/common/Makefile.in
new file mode 100644
index 0000000..6570d68
--- /dev/null
+++ b/connectors/jk/native/common/Makefile.in
@@ -0,0 +1,32 @@
+#### 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 *.lo *.a *.la *.so *.so.* *.slo
+	rm -rf .libs
diff --git a/connectors/jk/native/common/ap_snprintf.c b/connectors/jk/native/common/ap_snprintf.c
new file mode 100644
index 0000000..3068a10
--- /dev/null
+++ b/connectors/jk/native/common/ap_snprintf.c
@@ -0,0 +1,1178 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 code is based on, and used with the permission of, the
+ * SIO stdio-replacement strx_* functions by Panos Tsirigotis
+ * <panos@alumni.cs.colorado.edu> for xinetd.
+ */
+#define BUILD_STANDALONE
+
+#ifndef BUILD_STANDALONE
+#include "httpd.h"
+#else
+#include "ap_snprintf.h"
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#ifndef NETWARE
+#include <sys/types.h>
+#endif
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#ifdef WIN32
+#include <float.h>
+#endif
+
+typedef enum {
+    NO = 0, YES = 1
+} boolean_e;
+
+#ifndef FALSE
+#define FALSE                   0
+#endif
+#ifndef TRUE
+#define TRUE                    1
+#endif
+#ifndef AP_LONGEST_LONG
+#define AP_LONGEST_LONG         long
+#endif
+#define NUL                     '\0'
+#define WIDE_INT                long
+#define WIDEST_INT              AP_LONGEST_LONG
+
+typedef WIDE_INT wide_int;
+typedef unsigned WIDE_INT u_wide_int;
+typedef WIDEST_INT widest_int;
+#ifdef __TANDEM
+/* Although Tandem supports "long long" there is no unsigned variant. */
+typedef unsigned long       u_widest_int;
+#else
+typedef unsigned WIDEST_INT u_widest_int;
+#endif
+typedef int bool_int;
+
+#define S_NULL                  "(null)"
+#define S_NULL_LEN              6
+
+#define FLOAT_DIGITS            6
+#define EXPONENT_LENGTH         10
+
+/*
+ * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions
+ *
+ * XXX: this is a magic number; do not decrease it
+ */
+#define NUM_BUF_SIZE            512
+
+/*
+ * cvt.c - IEEE floating point formatting routines for FreeBSD
+ * from GNU libc-4.6.27.  Modified to be thread safe.
+ */
+
+/*
+ *    ap_ecvt converts to decimal
+ *      the number of digits is specified by ndigit
+ *      decpt is set to the position of the decimal point
+ *      sign is set to 0 for positive, 1 for negative
+ */
+
+#define NDIG    80
+
+/* buf must have at least NDIG bytes */
+static char *ap_cvt(double arg, int ndigits, int *decpt, int *sign, int eflag, char *buf)
+{
+    register int r2;
+    double fi, fj;
+    register char *p, *p1;
+    
+    if (ndigits >= NDIG - 1)
+        ndigits = NDIG - 2;
+    r2 = 0;
+    *sign = 0;
+    p = &buf[0];
+    if (arg < 0) {
+        *sign = 1;
+        arg = -arg;
+    }
+    arg = modf(arg, &fi);
+    p1 = &buf[NDIG];
+    /*
+     * Do integer part
+     */
+    if (fi != 0) {
+        p1 = &buf[NDIG];
+        while (p1 > &buf[0] && fi != 0) {
+            fj = modf(fi / 10, &fi);
+            *--p1 = (int) ((fj + .03) * 10) + '0';
+            r2++;
+        }
+        while (p1 < &buf[NDIG])
+            *p++ = *p1++;
+    }
+    else if (arg > 0) {
+        while ((fj = arg * 10) < 1) {
+            arg = fj;
+            r2--;
+        }
+    }
+    p1 = &buf[ndigits];
+    if (eflag == 0)
+        p1 += r2;
+    *decpt = r2;
+    if (p1 < &buf[0]) {
+        buf[0] = '\0';
+        return (buf);
+    }
+    while (p <= p1 && p < &buf[NDIG]) {
+        arg *= 10;
+        arg = modf(arg, &fj);
+        *p++ = (int) fj + '0';
+    }
+    if (p1 >= &buf[NDIG]) {
+        buf[NDIG - 1] = '\0';
+        return (buf);
+    }
+    p = p1;
+    *p1 += 5;
+    while (*p1 > '9') {
+        *p1 = '0';
+        if (p1 > buf)
+            ++ * --p1;
+        else {
+            *p1 = '1';
+            (*decpt)++;
+            if (eflag == 0) {
+                if (p > buf)
+                    *p = '0';
+                p++;
+            }
+        }
+    }
+    *p = '\0';
+    return (buf);
+}
+
+static char *ap_ecvt(double arg, int ndigits, int *decpt, int *sign, char *buf)
+{
+    return (ap_cvt(arg, ndigits, decpt, sign, 1, buf));
+}
+
+static char *ap_fcvt(double arg, int ndigits, int *decpt, int *sign, char *buf)
+{
+    return (ap_cvt(arg, ndigits, decpt, sign, 0, buf));
+}
+
+/*
+ * ap_gcvt  - Floating output conversion to
+ * minimal length string
+ */
+
+static char *ap_gcvt(double number, int ndigit, char *buf, boolean_e altform)
+{
+    int sign, decpt;
+    register char *p1, *p2;
+    register int i;
+    char buf1[NDIG];
+
+    p1 = ap_ecvt(number, ndigit, &decpt, &sign, buf1);
+    p2 = buf;
+    if (sign)
+        *p2++ = '-';
+    for (i = ndigit - 1; i > 0 && p1[i] == '0'; i--)
+        ndigit--;
+    if ((decpt >= 0 && decpt - ndigit > 4)
+        || (decpt < 0 && decpt < -3)) {         /* use E-style */
+        decpt--;
+        *p2++ = *p1++;
+        *p2++ = '.';
+        for (i = 1; i < ndigit; i++)
+            *p2++ = *p1++;
+        *p2++ = 'e';
+        if (decpt < 0) {
+            decpt = -decpt;
+            *p2++ = '-';
+        }
+        else
+            *p2++ = '+';
+        if (decpt / 100 > 0)
+            *p2++ = decpt / 100 + '0';
+        if (decpt / 10 > 0)
+            *p2++ = (decpt % 100) / 10 + '0';
+        *p2++ = decpt % 10 + '0';
+    }
+    else {
+        if (decpt <= 0) {
+            if (*p1 != '0')
+                *p2++ = '.';
+            while (decpt < 0) {
+                decpt++;
+                *p2++ = '0';
+            }
+        }
+        for (i = 1; i <= ndigit; i++) {
+            *p2++ = *p1++;
+            if (i == decpt)
+                *p2++ = '.';
+        }
+        if (ndigit < decpt) {
+            while (ndigit++ < decpt)
+                *p2++ = '0';
+            *p2++ = '.';
+        }
+    }
+    if (p2[-1] == '.' && !altform)
+        p2--;
+    *p2 = '\0';
+    return (buf);
+}
+
+/*
+ * The INS_CHAR macro inserts a character in the buffer and writes
+ * the buffer back to disk if necessary
+ * It uses the char pointers sp and bep:
+ *      sp points to the next available character in the buffer
+ *      bep points to the end-of-buffer+1
+ * While using this macro, note that the nextb pointer is NOT updated.
+ *
+ * NOTE: Evaluation of the c argument should not have any side-effects
+ */
+#define INS_CHAR(c, sp, bep, cc)                                \
+            {                                                   \
+                if (sp >= bep) {                                \
+                    vbuff->curpos = sp;                         \
+                    if (flush_func(vbuff))                      \
+                        return -1;                              \
+                    sp = vbuff->curpos;                         \
+                    bep = vbuff->endpos;                        \
+                }                                               \
+                *sp++ = (c);                                    \
+                cc++;                                           \
+            }
+
+#define NUM( c )                        ( c - '0' )
+
+#define STR_TO_DEC( str, num )          \
+    num = NUM( *str++ ) ;               \
+    while ( ap_isdigit( *str ) )                \
+    {                                   \
+        num *= 10 ;                     \
+        num += NUM( *str++ ) ;          \
+    }
+
+/*
+ * This macro does zero padding so that the precision
+ * requirement is satisfied. The padding is done by
+ * adding '0's to the left of the string that is going
+ * to be printed. We don't allow precision to be large
+ * enough that we continue past the start of s.
+ *
+ * NOTE: this makes use of the magic info that s is
+ * always based on num_buf with a size of NUM_BUF_SIZE.
+ */
+#define FIX_PRECISION( adjust, precision, s, s_len )    \
+    if ( adjust ) {                                     \
+        int p = precision < NUM_BUF_SIZE - 1 ? precision : NUM_BUF_SIZE - 1; \
+        while ( s_len < p )                             \
+        {                                               \
+            *--s = '0' ;                                \
+            s_len++ ;                                   \
+        }                                               \
+    }
+
+/*
+ * Macro that does padding. The padding is done by printing
+ * the character ch.
+ */
+#define PAD( width, len, ch )   do              \
+        {                                       \
+            INS_CHAR( ch, sp, bep, cc ) ;       \
+            width-- ;                           \
+        }                                       \
+        while ( width > len )
+
+/*
+ * Prefix the character ch to the string str
+ * Increase length
+ * Set the has_prefix flag
+ */
+#define PREFIX( str, length, ch )        *--str = ch ; length++ ; has_prefix = YES
+
+
+/*
+ * Convert num to its decimal format.
+ * Return value:
+ *   - a pointer to a string containing the number (no sign)
+ *   - len contains the length of the string
+ *   - is_negative is set to TRUE or FALSE depending on the sign
+ *     of the number (always set to FALSE if is_unsigned is TRUE)
+ *
+ * The caller provides a buffer for the string: that is the buf_end argument
+ * which is a pointer to the END of the buffer + 1 (i.e. if the buffer
+ * is declared as buf[ 100 ], buf_end should be &buf[ 100 ])
+ *
+ * Note: we have 2 versions. One is used when we need to use quads
+ * (conv_10_quad), the other when we don't (conv_10). We're assuming the
+ * latter is faster.
+ */
+static char *conv_10(register wide_int num, register bool_int is_unsigned,
+                     register bool_int *is_negative, char *buf_end,
+                     register int *len)
+{
+    register char *p = buf_end;
+    register u_wide_int magnitude;
+
+    if (is_unsigned) {
+        magnitude = (u_wide_int) num;
+        *is_negative = FALSE;
+    }
+    else {
+        *is_negative = (num < 0);
+
+        /*
+         * On a 2's complement machine, negating the most negative integer 
+         * results in a number that cannot be represented as a signed integer.
+         * Here is what we do to obtain the number's magnitude:
+         *      a. add 1 to the number
+         *      b. negate it (becomes positive)
+         *      c. convert it to unsigned
+         *      d. add 1
+         */
+        if (*is_negative) {
+            wide_int t = num + 1;
+
+            magnitude = ((u_wide_int) -t) + 1;
+        }
+        else
+            magnitude = (u_wide_int) num;
+    }
+
+    /*
+     * We use a do-while loop so that we write at least 1 digit 
+     */
+    do {
+        register u_wide_int new_magnitude = magnitude / 10;
+
+        *--p = (char) (magnitude - new_magnitude * 10 + '0');
+        magnitude = new_magnitude;
+    }
+    while (magnitude);
+
+    *len = buf_end - p;
+    return (p);
+}
+
+static char *conv_10_quad(widest_int num, register bool_int is_unsigned,
+                     register bool_int *is_negative, char *buf_end,
+                     register int *len)
+{
+    register char *p = buf_end;
+    u_widest_int magnitude;
+
+    /*
+     * We see if we can use the faster non-quad version by checking the
+     * number against the largest long value it can be. If <=, we
+     * punt to the quicker version.
+     */
+    if ((num <= ULONG_MAX && is_unsigned) || (num <= LONG_MAX && !is_unsigned))
+        return(conv_10( (wide_int)num, is_unsigned, is_negative,
+               buf_end, len));
+
+    if (is_unsigned) {
+        magnitude = (u_widest_int) num;
+        *is_negative = FALSE;
+    }
+    else {
+        *is_negative = (num < 0);
+
+        /*
+         * On a 2's complement machine, negating the most negative integer 
+         * results in a number that cannot be represented as a signed integer.
+         * Here is what we do to obtain the number's magnitude:
+         *      a. add 1 to the number
+         *      b. negate it (becomes positive)
+         *      c. convert it to unsigned
+         *      d. add 1
+         */
+        if (*is_negative) {
+            widest_int t = num + 1;
+
+            magnitude = ((u_widest_int) -t) + 1;
+        }
+        else
+            magnitude = (u_widest_int) num;
+    }
+
+    /*
+     * We use a do-while loop so that we write at least 1 digit 
+     */
+    do {
+        u_widest_int new_magnitude = magnitude / 10;
+
+        *--p = (char) (magnitude - new_magnitude * 10 + '0');
+        magnitude = new_magnitude;
+    }
+    while (magnitude);
+
+    *len = buf_end - p;
+    return (p);
+}
+
+
+
+#ifndef BUILD_STANDALONE
+static char *conv_in_addr(struct in_addr *ia, char *buf_end, int *len)
+{
+    unsigned addr = ntohl(ia->s_addr);
+    char *p = buf_end;
+    bool_int is_negative;
+    int sub_len;
+
+    p = conv_10((addr & 0x000000FF)      , TRUE, &is_negative, p, &sub_len);
+    *--p = '.';
+    p = conv_10((addr & 0x0000FF00) >>  8, TRUE, &is_negative, p, &sub_len);
+    *--p = '.';
+    p = conv_10((addr & 0x00FF0000) >> 16, TRUE, &is_negative, p, &sub_len);
+    *--p = '.';
+    p = conv_10((addr & 0xFF000000) >> 24, TRUE, &is_negative, p, &sub_len);
+
+    *len = buf_end - p;
+    return (p);
+}
+
+
+
+static char *conv_sockaddr_in(struct sockaddr_in *si, char *buf_end, int *len)
+{
+    char *p = buf_end;
+    bool_int is_negative;
+    int sub_len;
+
+    p = conv_10(ntohs(si->sin_port), TRUE, &is_negative, p, &sub_len);
+    *--p = ':';
+    p = conv_in_addr(&si->sin_addr, p, &sub_len);
+
+    *len = buf_end - p;
+    return (p);
+}
+#endif
+
+
+/*
+ * Convert a floating point number to a string formats 'f', 'e' or 'E'.
+ * The result is placed in buf, and len denotes the length of the string
+ * The sign is returned in the is_negative argument (and is not placed
+ * in buf).
+ */
+static char *conv_fp(register char format, register double num,
+    boolean_e add_dp, int precision, bool_int *is_negative,
+    char *buf, int *len)
+{
+    register char *s = buf;
+    register char *p;
+    int decimal_point;
+    char buf1[NDIG];
+
+    if (format == 'f')
+        p = ap_fcvt(num, precision, &decimal_point, is_negative, buf1);
+    else                        /* either e or E format */
+        p = ap_ecvt(num, precision + 1, &decimal_point, is_negative, buf1);
+
+    /*
+     * Check for Infinity and NaN
+     */
+    if (ap_isalpha(*p)) {
+        *len = strlen(strcpy(buf, p));
+        *is_negative = FALSE;
+        return (buf);
+    }
+
+    if (format == 'f') {
+        if (decimal_point <= 0) {
+            *s++ = '0';
+            if (precision > 0) {
+                *s++ = '.';
+                while (decimal_point++ < 0)
+                    *s++ = '0';
+            }
+            else if (add_dp)
+                *s++ = '.';
+        }
+        else {
+            while (decimal_point-- > 0)
+                *s++ = *p++;
+            if (precision > 0 || add_dp)
+                *s++ = '.';
+        }
+    }
+    else {
+        *s++ = *p++;
+        if (precision > 0 || add_dp)
+            *s++ = '.';
+    }
+
+    /*
+     * copy the rest of p, the NUL is NOT copied
+     */
+    while (*p)
+        *s++ = *p++;
+
+    if (format != 'f') {
+        char temp[EXPONENT_LENGTH];     /* for exponent conversion */
+        int t_len;
+        bool_int exponent_is_negative;
+
+        *s++ = format;          /* either e or E */
+        decimal_point--;
+        if (decimal_point != 0) {
+            p = conv_10((wide_int) decimal_point, FALSE, &exponent_is_negative,
+                        &temp[EXPONENT_LENGTH], &t_len);
+            *s++ = exponent_is_negative ? '-' : '+';
+
+            /*
+             * Make sure the exponent has at least 2 digits
+             */
+            if (t_len == 1)
+                *s++ = '0';
+            while (t_len--)
+                *s++ = *p++;
+        }
+        else {
+            *s++ = '+';
+            *s++ = '0';
+            *s++ = '0';
+        }
+    }
+
+    *len = s - buf;
+    return (buf);
+}
+
+
+/*
+ * Convert num to a base X number where X is a power of 2. nbits determines X.
+ * For example, if nbits is 3, we do base 8 conversion
+ * Return value:
+ *      a pointer to a string containing the number
+ *
+ * The caller provides a buffer for the string: that is the buf_end argument
+ * which is a pointer to the END of the buffer + 1 (i.e. if the buffer
+ * is declared as buf[ 100 ], buf_end should be &buf[ 100 ])
+ *
+ * As with conv_10, we have a faster version which is used when
+ * the number isn't quad size.
+ */
+static char *conv_p2(register u_wide_int num, register int nbits,
+                     char format, char *buf_end, register int *len)
+{
+    register int mask = (1 << nbits) - 1;
+    register char *p = buf_end;
+    static const char low_digits[] = "0123456789abcdef";
+    static const char upper_digits[] = "0123456789ABCDEF";
+    register const char *digits = (format == 'X') ? upper_digits : low_digits;
+
+    do {
+        *--p = digits[num & mask];
+        num >>= nbits;
+    }
+    while (num);
+
+    *len = buf_end - p;
+    return (p);
+}
+
+static char *conv_p2_quad(u_widest_int num, register int nbits,
+                     char format, char *buf_end, register int *len)
+{
+    register int mask = (1 << nbits) - 1;
+    register char *p = buf_end;
+    static const char low_digits[] = "0123456789abcdef";
+    static const char upper_digits[] = "0123456789ABCDEF";
+    register const char *digits = (format == 'X') ? upper_digits : low_digits;
+
+    if (num <= ULONG_MAX)
+        return(conv_p2( (u_wide_int)num, nbits, format, buf_end, len));
+
+    do {
+        *--p = digits[num & mask];
+        num >>= nbits;
+    }
+    while (num);
+
+    *len = buf_end - p;
+    return (p);
+}
+
+
+/*
+ * Do format conversion placing the output in buffer
+ */
+API_EXPORT(int) ap_vformatter(int (*flush_func)(ap_vformatter_buff *),
+    ap_vformatter_buff *vbuff, const char *fmt, va_list ap)
+{
+    register char *sp;
+    register char *bep;
+    register int cc = 0;
+    register int i;
+
+    register char *s = NULL;
+    char *q;
+    int s_len;
+
+    register int min_width = 0;
+    int precision = 0;
+    enum {
+        LEFT, RIGHT
+    } adjust;
+    char pad_char;
+    char prefix_char;
+
+    double fp_num;
+    widest_int i_quad = (widest_int) 0;
+    u_widest_int ui_quad;
+    wide_int i_num = (wide_int) 0;
+    u_wide_int ui_num;
+
+    char num_buf[NUM_BUF_SIZE];
+    char char_buf[2];           /* for printing %% and %<unknown> */
+
+    enum var_type_enum {
+        IS_QUAD, IS_LONG, IS_SHORT, IS_INT
+    };
+    enum var_type_enum var_type = IS_INT;
+
+    /*
+     * Flag variables
+     */
+    boolean_e alternate_form;
+    boolean_e print_sign;
+    boolean_e print_blank;
+    boolean_e adjust_precision;
+    boolean_e adjust_width;
+    bool_int is_negative;
+
+    sp = vbuff->curpos;
+    bep = vbuff->endpos;
+
+    while (*fmt) {
+        if (*fmt != '%') {
+            INS_CHAR(*fmt, sp, bep, cc);
+        }
+        else {
+            /*
+             * Default variable settings
+             */
+            adjust = RIGHT;
+            alternate_form = print_sign = print_blank = NO;
+            pad_char = ' ';
+            prefix_char = NUL;
+
+            fmt++;
+
+            /*
+             * Try to avoid checking for flags, width or precision
+             */
+            if (!ap_islower(*fmt)) {
+                /*
+                 * Recognize flags: -, #, BLANK, +
+                 */
+                for (;; fmt++) {
+                    if (*fmt == '-')
+                        adjust = LEFT;
+                    else if (*fmt == '+')
+                        print_sign = YES;
+                    else if (*fmt == '#')
+                        alternate_form = YES;
+                    else if (*fmt == ' ')
+                        print_blank = YES;
+                    else if (*fmt == '0')
+                        pad_char = '0';
+                    else
+                        break;
+                }
+
+                /*
+                 * Check if a width was specified
+                 */
+                if (ap_isdigit(*fmt)) {
+                    STR_TO_DEC(fmt, min_width);
+                    adjust_width = YES;
+                }
+                else if (*fmt == '*') {
+                    min_width = va_arg(ap, int);
+                    fmt++;
+                    adjust_width = YES;
+                    if (min_width < 0) {
+                        adjust = LEFT;
+                        min_width = -min_width;
+                    }
+                }
+                else
+                    adjust_width = NO;
+
+                /*
+                 * Check if a precision was specified
+                 */
+                if (*fmt == '.') {
+                    adjust_precision = YES;
+                    fmt++;
+                    if (ap_isdigit(*fmt)) {
+                        STR_TO_DEC(fmt, precision);
+                    }
+                    else if (*fmt == '*') {
+                        precision = va_arg(ap, int);
+                        fmt++;
+                        if (precision < 0)
+                            precision = 0;
+                    }
+                    else
+                        precision = 0;
+                }
+                else
+                    adjust_precision = NO;
+            }
+            else
+                adjust_precision = adjust_width = NO;
+
+            /*
+             * Modifier check
+             */
+            if (*fmt == 'q') {
+                var_type = IS_QUAD;
+                fmt++;
+            }
+            else if (*fmt == 'l') {
+                var_type = IS_LONG;
+                fmt++;
+            }
+            else if (*fmt == 'h') {
+                var_type = IS_SHORT;
+                fmt++;
+            }
+            else {
+                var_type = IS_INT;
+            }
+
+            /*
+             * Argument extraction and printing.
+             * First we determine the argument type.
+             * Then, we convert the argument to a string.
+             * On exit from the switch, s points to the string that
+             * must be printed, s_len has the length of the string
+             * The precision requirements, if any, are reflected in s_len.
+             *
+             * NOTE: pad_char may be set to '0' because of the 0 flag.
+             *   It is reset to ' ' by non-numeric formats
+             */
+            switch (*fmt) {
+            case 'u':
+                if (var_type == IS_QUAD) {
+                    i_quad = va_arg(ap, u_widest_int);
+                    s = conv_10_quad(i_quad, 1, &is_negative,
+                            &num_buf[NUM_BUF_SIZE], &s_len);
+                }
+                else {
+                    if (var_type == IS_LONG)
+                        i_num = (wide_int) va_arg(ap, u_wide_int);
+                    else if (var_type == IS_SHORT)
+                        i_num = (wide_int) (unsigned short) va_arg(ap, unsigned int);
+                    else
+                        i_num = (wide_int) va_arg(ap, unsigned int);
+                    s = conv_10(i_num, 1, &is_negative,
+                            &num_buf[NUM_BUF_SIZE], &s_len);
+                }
+                FIX_PRECISION(adjust_precision, precision, s, s_len);
+                break;
+
+            case 'd':
+            case 'i':
+                if (var_type == IS_QUAD) {
+                    i_quad = va_arg(ap, widest_int);
+                    s = conv_10_quad(i_quad, 0, &is_negative,
+                            &num_buf[NUM_BUF_SIZE], &s_len);
+                }
+                else {
+                    if (var_type == IS_LONG)
+                        i_num = (wide_int) va_arg(ap, wide_int);
+                    else if (var_type == IS_SHORT)
+                        i_num = (wide_int) (short) va_arg(ap, int);
+                    else
+                        i_num = (wide_int) va_arg(ap, int);
+                    s = conv_10(i_num, 0, &is_negative,
+                            &num_buf[NUM_BUF_SIZE], &s_len);
+                }
+                FIX_PRECISION(adjust_precision, precision, s, s_len);
+
+                if (is_negative)
+                    prefix_char = '-';
+                else if (print_sign)
+                    prefix_char = '+';
+                else if (print_blank)
+                    prefix_char = ' ';
+                break;
+
+
+            case 'o':
+                if (var_type == IS_QUAD) {
+                    ui_quad = va_arg(ap, u_widest_int);
+                    s = conv_p2_quad(ui_quad, 3, *fmt,
+                            &num_buf[NUM_BUF_SIZE], &s_len);
+                }
+                else {
+                    if (var_type == IS_LONG)
+                        ui_num = (u_wide_int) va_arg(ap, u_wide_int);
+                    else if (var_type == IS_SHORT)
+                        ui_num = (u_wide_int) (unsigned short) va_arg(ap, unsigned int);
+                    else
+                        ui_num = (u_wide_int) va_arg(ap, unsigned int);
+                    s = conv_p2(ui_num, 3, *fmt,
+                            &num_buf[NUM_BUF_SIZE], &s_len);
+                }
+                FIX_PRECISION(adjust_precision, precision, s, s_len);
+                if (alternate_form && *s != '0') {
+                    *--s = '0';
+                    s_len++;
+                }
+                break;
+
+
+            case 'x':
+            case 'X':
+                if (var_type == IS_QUAD) {
+                    ui_quad = va_arg(ap, u_widest_int);
+                    s = conv_p2_quad(ui_quad, 4, *fmt,
+                            &num_buf[NUM_BUF_SIZE], &s_len);
+                }
+                else {
+                    if (var_type == IS_LONG)
+                        ui_num = (u_wide_int) va_arg(ap, u_wide_int);
+                    else if (var_type == IS_SHORT)
+                        ui_num = (u_wide_int) (unsigned short) va_arg(ap, unsigned int);
+                    else
+                        ui_num = (u_wide_int) va_arg(ap, unsigned int);
+                    s = conv_p2(ui_num, 4, *fmt,
+                            &num_buf[NUM_BUF_SIZE], &s_len);
+                }
+                FIX_PRECISION(adjust_precision, precision, s, s_len);
+                if (alternate_form && i_num != 0) {
+                    *--s = *fmt;        /* 'x' or 'X' */
+                    *--s = '0';
+                    s_len += 2;
+                }
+                break;
+
+
+            case 's':
+                s = va_arg(ap, char *);
+                if (s != NULL) {
+                    s_len = strlen(s);
+                    if (adjust_precision && precision < s_len)
+                        s_len = precision;
+                }
+                else {
+                    s = S_NULL;
+                    s_len = S_NULL_LEN;
+                }
+                pad_char = ' ';
+                break;
+
+
+            case 'f':
+            case 'e':
+            case 'E':
+                fp_num = va_arg(ap, double);
+                /*
+                 * * We use &num_buf[ 1 ], so that we have room for the sign
+                 */
+#ifdef HAVE_ISNAN
+                if (isnan(fp_num)) {
+                    s = "nan";
+                    s_len = 3;
+                }
+                else
+#endif
+#ifdef HAVE_ISINF
+                if (isinf(fp_num)) {
+                    s = "inf";
+                    s_len = 3;
+                }
+                else
+#endif
+                {
+                    s = conv_fp(*fmt, fp_num, alternate_form,
+                            (adjust_precision == NO) ? FLOAT_DIGITS : precision,
+                                &is_negative, &num_buf[1], &s_len);
+                    if (is_negative)
+                        prefix_char = '-';
+                    else if (print_sign)
+                        prefix_char = '+';
+                    else if (print_blank)
+                        prefix_char = ' ';
+                }
+                break;
+
+
+            case 'g':
+            case 'G':
+                if (adjust_precision == NO)
+                    precision = FLOAT_DIGITS;
+                else if (precision == 0)
+                    precision = 1;
+                /*
+                 * * We use &num_buf[ 1 ], so that we have room for the sign
+                 */
+                s = ap_gcvt(va_arg(ap, double), precision, &num_buf[1],
+                            alternate_form);
+                if (*s == '-')
+                    prefix_char = *s++;
+                else if (print_sign)
+                    prefix_char = '+';
+                else if (print_blank)
+                    prefix_char = ' ';
+
+                s_len = strlen(s);
+
+                if (alternate_form && (q = strchr(s, '.')) == NULL) {
+                    s[s_len++] = '.';
+                    s[s_len] = '\0'; /* delimit for following strchr() */
+                }
+                if (*fmt == 'G' && (q = strchr(s, 'e')) != NULL)
+                    *q = 'E';
+                break;
+
+
+            case 'c':
+                char_buf[0] = (char) (va_arg(ap, int));
+                s = &char_buf[0];
+                s_len = 1;
+                pad_char = ' ';
+                break;
+
+
+            case '%':
+                char_buf[0] = '%';
+                s = &char_buf[0];
+                s_len = 1;
+                pad_char = ' ';
+                break;
+
+
+            case 'n':
+                if (var_type == IS_QUAD)
+                    *(va_arg(ap, widest_int *)) = cc;
+                else if (var_type == IS_LONG)
+                    *(va_arg(ap, long *)) = cc;
+                else if (var_type == IS_SHORT)
+                    *(va_arg(ap, short *)) = cc;
+                else
+                    *(va_arg(ap, int *)) = cc;
+                break;
+
+                /*
+                 * This is where we extend the printf format, with a second
+                 * type specifier
+                 */
+            case 'p':
+                switch(*++fmt) {
+                    /*
+                     * If the pointer size is equal to or smaller than the size
+                     * of the largest unsigned int, we convert the pointer to a
+                     * hex number, otherwise we print "%p" to indicate that we
+                     * don't handle "%p".
+                     */
+                case 'p':
+#ifdef AP_VOID_P_IS_QUAD
+                    if (sizeof(void *) <= sizeof(u_widest_int)) {
+                        ui_quad = (u_widest_int) va_arg(ap, void *);
+                        s = conv_p2_quad(ui_quad, 4, 'x',
+                                &num_buf[NUM_BUF_SIZE], &s_len);
+                    }
+#else
+                    if (sizeof(void *) <= sizeof(u_wide_int)) {
+                        ui_num = (u_wide_int) va_arg(ap, void *);
+                        s = conv_p2(ui_num, 4, 'x',
+                                &num_buf[NUM_BUF_SIZE], &s_len);
+                    }
+#endif
+                    else {
+                        s = "%p";
+                        s_len = 2;
+                        prefix_char = NUL;
+                    }
+                    pad_char = ' ';
+                    break;
+
+#ifndef BUILD_STANDALONE
+                    /* print a struct sockaddr_in as a.b.c.d:port */
+                case 'I':
+                    {
+                        struct sockaddr_in *si;
+
+                        si = va_arg(ap, struct sockaddr_in *);
+                        if (si != NULL) {
+                            s = conv_sockaddr_in(si, &num_buf[NUM_BUF_SIZE], &s_len);
+                            if (adjust_precision && precision < s_len)
+                                s_len = precision;
+                        }
+                        else {
+                            s = S_NULL;
+                            s_len = S_NULL_LEN;
+                        }
+                        pad_char = ' ';
+                    }
+                    break;
+
+                    /* print a struct in_addr as a.b.c.d */
+                case 'A':
+                    {
+                        struct in_addr *ia;
+
+                        ia = va_arg(ap, struct in_addr *);
+                        if (ia != NULL) {
+                            s = conv_in_addr(ia, &num_buf[NUM_BUF_SIZE], &s_len);
+                            if (adjust_precision && precision < s_len)
+                                s_len = precision;
+                        }
+                        else {
+                            s = S_NULL;
+                            s_len = S_NULL_LEN;
+                        }
+                        pad_char = ' ';
+                    }
+                    break;
+#endif
+
+                case NUL:
+                    /* if %p ends the string, oh well ignore it */
+                    continue;
+
+                default:
+                    s = "bogus %p";
+                    s_len = 8;
+                    prefix_char = NUL;
+                    break;
+                }
+                break;
+
+            case NUL:
+                /*
+                 * The last character of the format string was %.
+                 * We ignore it.
+                 */
+                continue;
+
+
+                /*
+                 * The default case is for unrecognized %'s.
+                 * We print %<char> to help the user identify what
+                 * option is not understood.
+                 * This is also useful in case the user wants to pass
+                 * the output of format_converter to another function
+                 * that understands some other %<char> (like syslog).
+                 * Note that we can't point s inside fmt because the
+                 * unknown <char> could be preceded by width etc.
+                 */
+            default:
+                char_buf[0] = '%';
+                char_buf[1] = *fmt;
+                s = char_buf;
+                s_len = 2;
+                pad_char = ' ';
+                break;
+            }
+
+            if (prefix_char != NUL && s != S_NULL && s != char_buf) {
+                *--s = prefix_char;
+                s_len++;
+            }
+
+            if (adjust_width && adjust == RIGHT && min_width > s_len) {
+                if (pad_char == '0' && prefix_char != NUL) {
+                    INS_CHAR(*s, sp, bep, cc);
+                    s++;
+                    s_len--;
+                    min_width--;
+                }
+                PAD(min_width, s_len, pad_char);
+            }
+
+            /*
+             * Print the string s. 
+             */
+            for (i = s_len; i != 0; i--) {
+                INS_CHAR(*s, sp, bep, cc);
+                s++;
+            }
+
+            if (adjust_width && adjust == LEFT && min_width > s_len)
+                PAD(min_width, s_len, pad_char);
+        }
+        fmt++;
+    }
+    vbuff->curpos = sp;
+
+    return cc;
+}
+
+
+static int snprintf_flush(ap_vformatter_buff *vbuff)
+{
+    /* if the buffer fills we have to abort immediately, there is no way
+     * to "flush" an ap_snprintf... there's nowhere to flush it to.
+     */
+    return -1;
+}
+
+
+API_EXPORT_NONSTD(int) ap_snprintf(char *buf, size_t len, const char *format,...)
+{
+    int cc;
+    va_list ap;
+    ap_vformatter_buff vbuff;
+
+    if (len == 0)
+        return 0;
+
+    /* save one byte for nul terminator */
+    vbuff.curpos = buf;
+    vbuff.endpos = buf + len - 1;
+    va_start(ap, format);
+    cc = ap_vformatter(snprintf_flush, &vbuff, format, ap);
+    va_end(ap);
+    *vbuff.curpos = '\0';
+    return (cc == -1) ? len : cc;
+}
+
+
+API_EXPORT(int) ap_vsnprintf(char *buf, size_t len, const char *format,
+                             va_list ap)
+{
+    int cc;
+    ap_vformatter_buff vbuff;
+
+    if (len == 0)
+        return 0;
+
+    /* save one byte for nul terminator */
+    vbuff.curpos = buf;
+    vbuff.endpos = buf + len - 1;
+    cc = ap_vformatter(snprintf_flush, &vbuff, format, ap);
+    *vbuff.curpos = '\0';
+    return (cc == -1) ? len : cc;
+}
diff --git a/connectors/jk/native/common/ap_snprintf.h b/connectors/jk/native/common/ap_snprintf.h
new file mode 100644
index 0000000..ad9ef92
--- /dev/null
+++ b/connectors/jk/native/common/ap_snprintf.h
@@ -0,0 +1,150 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 ap_vsnprintf/ap_snprintf functions are based on, and used with the
+ * permission of, the  SIO stdio-replacement strx_* functions by Panos
+ * Tsirigotis <panos@alumni.cs.colorado.edu> for xinetd.
+ */
+
+#ifndef APACHE_AP_SNPRINTF_H
+#define APACHE_AP_SNPRINTF_H
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <limits.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* stuff marked API_EXPORT is part of the API, and intended for use
+ * by modules
+ */
+#ifndef API_EXPORT
+#define API_EXPORT(type)    type
+#endif
+
+/* Stuff marked API_EXPORT_NONSTD is part of the API, and intended for
+ * use by modules.  The difference between API_EXPORT and
+ * API_EXPORT_NONSTD is that the latter is required for any functions
+ * which use varargs or are used via indirect function call.  This
+ * is to accomodate the two calling conventions in windows dlls.
+ */
+#ifndef API_EXPORT_NONSTD
+#define API_EXPORT_NONSTD(type)    type
+#endif
+
+#if !defined(__GNUC__) || __GNUC__ < 2 || \
+    (__GNUC__ == 2 && __GNUC_MINOR__ < 7) ||\
+    defined(NEXT)
+#define __attribute__(__x)
+#endif
+
+/* These macros allow correct support of 8-bit characters on systems which
+ * support 8-bit characters.  Pretty dumb how the cast is required, but
+ * that's legacy libc for ya.  These new macros do not support EOF like
+ * the standard macros do.  Tough.
+ */
+#define ap_isalpha(c) (isalpha(((unsigned char)(c))))
+#define ap_isdigit(c) (isdigit(((unsigned char)(c))))
+#define ap_islower(c) (islower(((unsigned char)(c))))
+
+
+/* ap_vformatter() is a generic printf-style formatting routine
+ * with some extensions.  The extensions are:
+ *
+ * %pA	takes a struct in_addr *, and prints it as a.b.c.d
+ * %pI	takes a struct sockaddr_in * and prints it as a.b.c.d:port
+ * %pp  takes a void * and outputs it in hex
+ *
+ * The %p hacks are to force gcc's printf warning code to skip
+ * over a pointer argument without complaining.  This does
+ * mean that the ANSI-style %p (output a void * in hex format) won't
+ * work as expected at all, but that seems to be a fair trade-off
+ * for the increased robustness of having printf-warnings work.
+ *
+ * Additionally, ap_vformatter allows for arbitrary output methods
+ * using the ap_vformatter_buff and flush_func.
+ *
+ * The ap_vformatter_buff has two elements curpos and endpos.
+ * curpos is where ap_vformatter will write the next byte of output.
+ * It proceeds writing output to curpos, and updating curpos, until
+ * either the end of output is reached, or curpos == endpos (i.e. the
+ * buffer is full).
+ *
+ * If the end of output is reached, ap_vformatter returns the
+ * number of bytes written.
+ *
+ * When the buffer is full, the flush_func is called.  The flush_func
+ * can return -1 to indicate that no further output should be attempted,
+ * and ap_vformatter will return immediately with -1.  Otherwise
+ * the flush_func should flush the buffer in whatever manner is
+ * appropriate, re-initialize curpos and endpos, and return 0.
+ *
+ * Note that flush_func is only invoked as a result of attempting to
+ * write another byte at curpos when curpos >= endpos.  So for
+ * example, it's possible when the output exactly matches the buffer
+ * space available that curpos == endpos will be true when
+ * ap_vformatter returns.
+ *
+ * ap_vformatter does not call out to any other code, it is entirely
+ * self-contained.  This allows the callers to do things which are
+ * otherwise "unsafe".  For example, ap_psprintf uses the "scratch"
+ * space at the unallocated end of a block, and doesn't actually
+ * complete the allocation until ap_vformatter returns.  ap_psprintf
+ * would be completely broken if ap_vformatter were to call anything
+ * that used a pool.  Similarly http_bprintf() uses the "scratch"
+ * space at the end of its output buffer, and doesn't actually note
+ * that the space is in use until it either has to flush the buffer
+ * or until ap_vformatter returns.
+ */
+
+typedef struct {
+    char *curpos;
+    char *endpos;
+} ap_vformatter_buff;
+
+API_EXPORT(int) ap_vformatter(int (*flush_func)(ap_vformatter_buff *),
+    ap_vformatter_buff *, const char *fmt, va_list ap);
+
+/* These are snprintf implementations based on ap_vformatter().
+ *
+ * Note that various standards and implementations disagree on the return
+ * value of snprintf, and side-effects due to %n in the formatting string.
+ * ap_snprintf behaves as follows:
+ *
+ * Process the format string until the entire string is exhausted, or
+ * the buffer fills.  If the buffer fills then stop processing immediately
+ * (so no further %n arguments are processed), and return the buffer
+ * length.  In all cases the buffer is NUL terminated. The return value
+ * is the number of characters placed in the buffer, excluding the
+ * terminating NUL. All this implies that, at most, (len-1) characters
+ * will be copied over; if the return value is >= len, then truncation
+ * occured.
+ *
+ * In no event does ap_snprintf return a negative number.
+ */
+API_EXPORT_NONSTD(int) ap_snprintf(char *buf, size_t len, const char *format,...)
+          __attribute__((format(printf,3,4)));
+API_EXPORT(int) ap_vsnprintf(char *buf, size_t len, const char *format,
+           va_list ap);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* !APACHE_AP_SNPRINTF_H */
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..dd2a65b
--- /dev/null
+++ b/connectors/jk/native/common/jk_ajp12_worker.c
@@ -0,0 +1,661 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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   *
+ *              This worker is deprecated                                  *
+ * 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"
+#if defined(AS400) && !defined(AS400_UTF8)
+#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;
+
+    jk_sock_t 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);
+
+#if defined(AS400) && !defined(AS400_UTF8)
+static int ajpv12_sendasciistring(ajp12_endpoint_t * p, char *buffer);
+#endif
+
+#if defined(AS400) && !defined(AS400_UTF8)
+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_error)
+{
+    ajp12_endpoint_t *p = e->endpoint_private;
+    unsigned int attempt;
+    int rc = -1;
+    /*
+     * AJP12 protocol is not recoverable.
+     */
+
+    JK_TRACE_ENTER(l);
+
+    if (is_error)
+        *is_error = JK_HTTP_SERVER_ERROR;
+    if (!e || !e->endpoint_private || !s || !is_error) {
+        JK_LOG_NULL_PARAMS(l);
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    for (attempt = 0; attempt < p->worker->connect_retry_attempts;
+         attempt++) {
+        p->sd =
+            jk_open_socket(&p->worker->worker_inet_addr,
+                           JK_FALSE, -1, 0, l);
+
+        jk_log(l, JK_LOG_DEBUG, "In jk_endpoint_t::service, sd = %d",
+               p->sd);
+        if (IS_VALID_SOCKET(p->sd)) {
+            break;
+        }
+    }
+    if (IS_VALID_SOCKET(p->sd)) {
+
+        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");
+            rc = ajpv12_handle_response(p, s, l);
+            JK_TRACE_EXIT(l);
+            return rc;
+        }
+    }
+    jk_log(l, JK_LOG_ERROR, "In jk_endpoint_t::service, Error sd = %d",
+           p->sd);
+
+    JK_TRACE_EXIT(l);
+    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");
+    if (e && *e && (*e)->endpoint_private) {
+        ajp12_endpoint_t *p = (*e)->endpoint_private;
+        if (IS_VALID_SOCKET(p->sd)) {
+            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");
+    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");
+
+    if (pThis && pThis->worker_private) {
+        ajp12_worker_t *p = pThis->worker_private;
+        int port = jk_get_worker_port(props,
+                                      p->name,
+                                      AJP_DEF_PORT);
+
+        const 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",
+               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");
+        }
+        jk_log(l, JK_LOG_ERROR, "In jk_worker_t::validate, Error %s %d",
+               host, port);
+    }
+    else {
+        jk_log(l, JK_LOG_ERROR,
+               "In jk_worker_t::validate, NULL parameters");
+    }
+
+    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");
+
+    if (pThis && pThis->worker_private && pend) {
+        ajp12_endpoint_t *p =
+            (ajp12_endpoint_t *) malloc(sizeof(ajp12_endpoint_t));
+        if (p) {
+            p->sd = JK_INVALID_SOCKET;
+            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");
+    }
+    else {
+        jk_log(l, JK_LOG_ERROR,
+               "In jk_worker_t::get_endpoint, NULL parameters");
+    }
+
+    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");
+    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");
+    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");
+    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;
+                private_data->worker.maintain = NULL;
+                private_data->worker.retries = JK_RETRIES;
+
+                *w = &private_data->worker;
+                return JK_AJP12_WORKER_TYPE;
+            }
+
+            free(private_data);
+        }
+        jk_log(l, JK_LOG_ERROR, "In ajp12_worker_factory, malloc failed");
+    }
+    else {
+        jk_log(l, JK_LOG_ERROR, "In ajp12_worker_factory, NULL parameters");
+    }
+
+    return 0;
+}
+
+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);
+    }
+}
+
+#if defined(AS400) && !defined(AS400_UTF8)
+static int ajpv12_sendasciistring(ajp12_endpoint_t * p, const char *buffer)
+{
+    int bufferlen;
+
+    if (buffer && (bufferlen = strlen(buffer))) {
+        return ajpv12_sendnbytes(p, buffer, bufferlen);
+    }
+    else {
+        return ajpv12_sendnbytes(p, NULL, 0);
+    }
+}
+#endif
+
+static int ajpv12_sendstring(ajp12_endpoint_t * p, const char *buffer)
+{
+    int bufferlen;
+
+    if (buffer && (bufferlen = (int)strlen(buffer))) {
+#if (defined(AS400) && !defined(AS400_UTF8)) || defined(_OSD_POSIX)
+        char buf[2048];
+        if (bufferlen < 2048) {
+            memcpy(buf, buffer, bufferlen);
+            jk_xlate_to_ascii(buf, bufferlen);
+            return ajpv12_sendnbytes(p, buf, bufferlen);
+        }
+        else
+            return -1;
+#else
+        return ajpv12_sendnbytes(p, buffer, bufferlen);
+#endif
+    }
+    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");
+    /*
+     * Start the ajp 12 service sequence
+     */
+    jk_log(l, JK_LOG_DEBUG,
+           "ajpv12_handle_request, sending the ajp12 start sequence");
+
+    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 */
+#if defined(AS400) && !defined(AS400_UTF8)
+           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) &&
+#if defined(AS400) && !defined(AS400_UTF8)
+           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 */
+#if defined(AS400) && !defined(AS400_UTF8)
+           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->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");
+        return JK_FALSE;
+    }
+
+    if (s->num_attributes > 0) {
+        unsigned i;
+        jk_log(l, JK_LOG_DEBUG,
+               "ajpv12_handle_request, sending the environment variables");
+
+        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");
+                return JK_FALSE;
+            }
+        }
+    }
+
+    jk_log(l, JK_LOG_DEBUG, "ajpv12_handle_request, sending the headers");
+
+    /* 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");
+                return JK_FALSE;
+            }
+        }
+    }
+
+    jk_log(l, JK_LOG_DEBUG,
+           "ajpv12_handle_request, sending the terminating mark");
+
+    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");
+        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");
+
+        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");
+                return JK_FALSE;
+            }
+            jk_log(l, JK_LOG_DEBUG, "ajpv12_handle_request, read %d bytes",
+                   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");
+                    return JK_FALSE;
+                }
+                jk_log(l, JK_LOG_DEBUG,
+                       "ajpv12_handle_request, sent %d bytes", 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",
+                       s->content_length, so_far);
+                return JK_FALSE;
+            }
+        }
+    }
+
+    jk_log(l, JK_LOG_DEBUG, "ajpv12_handle_request done");
+    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");
+    /*
+     * Read headers ...
+     */
+    while (1) {
+        char *line = NULL;
+        char *name = NULL;
+        char *value = NULL;
+#ifdef _REENTRANT
+        char *lasts;
+#endif
+
+        if (!jk_sb_gets(&p->sb, &line)) {
+            jk_log(l, JK_LOG_ERROR,
+                   "ajpv12_handle_response, error reading header line");
+            return JK_FALSE;
+        }
+#if (defined(AS400) && !defined(AS400_UTF8)) || defined(_OSD_POSIX)
+        jk_xlate_from_ascii(line, strlen(line));
+#endif
+
+        jk_log(l, JK_LOG_DEBUG, "ajpv12_handle_response, read %s", line);
+        if (0 == strlen(line)) {
+            jk_log(l, JK_LOG_DEBUG,
+                   "ajpv12_handle_response, headers are done");
+            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");
+            return JK_FALSE;
+        }
+        if (!(value = strchr(name, ':'))) {
+            jk_log(l, JK_LOG_ERROR,
+                   "ajpv12_handle_response, no value supplied");
+            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");
+            return JK_FALSE;
+        }
+
+        jk_log(l, JK_LOG_DEBUG, "ajpv12_handle_response, read %s=%s", name,
+               value);
+        if (0 == strcmp("Status", name)) {
+#ifdef _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");
+                return JK_FALSE;
+            }
+#ifdef _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");
+                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");
+                    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");
+    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");
+        return JK_FALSE;
+    }
+
+    jk_log(l, JK_LOG_DEBUG,
+           "ajpv12_handle_response, reading response body");
+    /*
+     * 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 ");
+            return JK_FALSE;
+        }
+
+        if (!acc) {
+            jk_log(l, JK_LOG_DEBUG,
+                   "ajpv12_handle_response, response body is done");
+            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");
+                write_to_ws = JK_FALSE;
+            }
+        }
+    }
+
+    jk_log(l, JK_LOG_DEBUG, "ajpv12_handle_response done");
+    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..aeeb70e
--- /dev/null
+++ b/connectors/jk/native/common/jk_ajp12_worker.h
@@ -0,0 +1,45 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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")
+#define JK_AJP12_WORKER_TYPE (1)
+
+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..3e36627
--- /dev/null
+++ b/connectors/jk/native/common/jk_ajp13.c
@@ -0,0 +1,50 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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_TRACE_ENTER(l);
+    /* 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,
+               "failed appending shutdown message");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    JK_TRACE_EXIT(l);
+    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..acf1fa8
--- /dev/null
+++ b/connectors/jk/native/common/jk_ajp13.h
@@ -0,0 +1,123 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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 JK_SERVER_ERROR             (-5)
+#define JK_CLIENT_RD_ERROR          (-6)
+#define JK_CLIENT_WR_ERROR          (-7)
+#define JK_STATUS_ERROR             (-8)
+
+#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..cf8d51f
--- /dev/null
+++ b/connectors/jk/native/common/jk_ajp13_worker.c
@@ -0,0 +1,120 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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)
+{
+    int rc;
+    JK_TRACE_ENTER(l);
+    rc = ajp_validate(pThis, props, we, l, AJP13_PROTO);
+    JK_TRACE_EXIT(l);
+    return rc;
+}
+
+
+static int JK_METHOD init(jk_worker_t *pThis,
+                          jk_map_t *props,
+                          jk_worker_env_t *we, jk_logger_t *l)
+{
+    int rc;
+    ajp_worker_t *aw = ( ajp_worker_t *)pThis->worker_private;
+    JK_TRACE_ENTER(l);
+
+    pThis->retries = jk_get_worker_retries(props, aw->name,
+                                           JK_RETRIES);
+
+    rc = ajp_init(pThis, props, we, l, AJP13_PROTO);
+    JK_TRACE_EXIT(l);
+    return rc;
+}
+
+
+static int JK_METHOD destroy(jk_worker_t **pThis, jk_logger_t *l)
+{
+    int rc;
+    JK_TRACE_ENTER(l);
+    rc = ajp_destroy(pThis, l, AJP13_PROTO);
+    JK_TRACE_EXIT(l);
+    return rc;
+}
+
+
+static int JK_METHOD get_endpoint(jk_worker_t *pThis,
+                                  jk_endpoint_t **pend, jk_logger_t *l)
+{
+    int rc;
+    JK_TRACE_ENTER(l);
+    rc = ajp_get_endpoint(pThis, pend, l, AJP13_PROTO);
+    JK_TRACE_EXIT(l);
+    return rc;
+}
+
+int JK_METHOD ajp13_worker_factory(jk_worker_t **w,
+                                   const char *name, jk_logger_t *l)
+{
+    ajp_worker_t *aw;
+
+    JK_TRACE_ENTER(l);
+    if (name == NULL || w == NULL) {
+        JK_LOG_NULL_PARAMS(l);
+        JK_TRACE_EXIT(l);
+        return 0;
+    }
+
+    aw = (ajp_worker_t *) calloc(1, sizeof(ajp_worker_t));
+    if (!aw) {
+        jk_log(l, JK_LOG_ERROR,
+               "malloc of private_data failed");
+        JK_TRACE_EXIT(l);
+        return 0;
+    }
+
+    aw->name = name;
+    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->worker.maintain = ajp_maintain;
+    aw->worker.retries = JK_RETRIES;
+
+    aw->logon = NULL;           /* No Logon on AJP13 */
+
+    *w = &aw->worker;
+    JK_TRACE_EXIT(l);
+    return JK_AJP13_WORKER_TYPE;
+}
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..606bf3d
--- /dev/null
+++ b/connectors/jk/native/common/jk_ajp13_worker.h
@@ -0,0 +1,50 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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")
+#define JK_AJP13_WORKER_TYPE (2)
+
+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..0f27dd4
--- /dev/null
+++ b/connectors/jk/native/common/jk_ajp14.c
@@ -0,0 +1,695 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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_TRACE_ENTER(l);
+    jk_md5((const unsigned char *)s->entropy,
+           (const unsigned char *)s->secret_key, s->computed_key);
+
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG, "(%s/%s) -> (%s)",
+               s->entropy, s->secret_key, s->computed_key);
+    JK_TRACE_EXIT(l);
+}
+
+
+/*
+ * 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_TRACE_ENTER(l);
+    /* To be on the safe side */
+    jk_b_reset(msg);
+
+    /*
+     * LOGIN
+     */
+    if (jk_b_append_byte(msg, AJP14_LOGINIT_CMD)) {
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    /*
+     * NEGOCIATION FLAGS
+     */
+    if (jk_b_append_long(msg, s->negociation)) {
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    /*
+     * WEB-SERVER NAME
+     */
+    if (jk_b_append_string(msg, s->web_server_name)) {
+        jk_log(l, JK_LOG_ERROR,
+               "failed appending the web_server_name string");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    JK_TRACE_EXIT(l);
+    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)
+{
+    JK_TRACE_ENTER(l);
+
+    if (jk_b_get_bytes
+        (msg, (unsigned char *)s->entropy, AJP14_ENTROPY_SEED_LEN) < 0) {
+        jk_log(l, JK_LOG_ERROR,
+               "can't get seed");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    s->entropy[AJP14_ENTROPY_SEED_LEN] = 0;     /* Just to have a CString */
+    JK_TRACE_EXIT(l);
+    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_TRACE_ENTER(l);
+
+    /* To be on the safe side */
+    jk_b_reset(msg);
+
+    /*
+     * LOGIN
+     */
+    if (jk_b_append_byte(msg, AJP14_LOGCOMP_CMD)) {
+        JK_TRACE_EXIT(l);
+        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,
+               "failed appending the COMPUTED MD5 bytes");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    JK_TRACE_EXIT(l);
+    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;
+
+    JK_TRACE_ENTER(l);
+
+    nego = jk_b_get_long(msg);
+
+    if (nego == 0xFFFFFFFF) {
+        jk_log(l, JK_LOG_ERROR,
+               "can't get negociated data");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    sname = (char *)jk_b_get_string(msg);
+
+    if (!sname) {
+        jk_log(l, JK_LOG_ERROR,
+               "can't get servlet engine name");
+        JK_TRACE_EXIT(l);
+        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,
+               "can't malloc servlet engine name");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    JK_TRACE_EXIT(l);
+    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_TRACE_ENTER(l);
+
+    status = jk_b_get_long(msg);
+
+    if (status == 0xFFFFFFFF) {
+        jk_log(l, JK_LOG_ERROR,
+               "can't get failure code");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    jk_log(l, JK_LOG_INFO, "Can't Log with servlet engine - code %08lx",
+           status);
+    JK_TRACE_EXIT(l);
+    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_TRACE_ENTER(l);
+
+    /* To be on the safe side */
+    jk_b_reset(msg);
+
+    /*
+     * SHUTDOWN CMD
+     */
+    if (jk_b_append_byte(msg, AJP14_SHUTDOWN_CMD)) {
+        JK_TRACE_EXIT(l);
+        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,
+               "failed appending the COMPUTED MD5 bytes");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    JK_TRACE_EXIT(l);
+    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_TRACE_ENTER(l);
+    status = jk_b_get_long(msg);
+
+    if (status == 0xFFFFFFFF) {
+        jk_log(l, JK_LOG_ERROR,
+               "can't get failure code");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    jk_log(l, JK_LOG_INFO, "Can't shutdown servlet engine - code %08lx",
+           status);
+    JK_TRACE_EXIT(l);
+    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_TRACE_ENTER(l);
+
+    /* To be on the safe side */
+    jk_b_reset(msg);
+
+    /*
+     * UNKNOWN PACKET CMD
+     */
+    if (jk_b_append_byte(msg, AJP14_UNKNOW_PACKET_CMD)) {
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    /*
+     * UNHANDLED MESSAGE SIZE
+     */
+    if (jk_b_append_int(msg, (unsigned short)unk->len)) {
+        JK_TRACE_EXIT(l);
+        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, unk->buf, unk->len)) {
+        jk_log(l, JK_LOG_ERROR,
+               "failed appending the UNHANDLED MESSAGE");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    JK_TRACE_EXIT(l);
+    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_TRACE_ENTER(l);
+
+    /* To be on the safe side */
+    jk_b_reset(msg);
+
+    /*
+     * CONTEXT QUERY CMD
+     */
+    if (jk_b_append_byte(msg, AJP14_CONTEXT_QRY_CMD)) {
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    /*
+     * VIRTUAL HOST CSTRING
+     */
+    if (jk_b_append_string(msg, virtual)) {
+        jk_log(l, JK_LOG_ERROR,
+               "failed appending the virtual host string");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    JK_TRACE_EXIT(l);
+    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_TRACE_ENTER(l);
+    jk_log(l, JK_LOG_DEBUG,
+           "get virtual %s for virtual %s",
+           vname, c->virt);
+
+    if (!vname) {
+        jk_log(l, JK_LOG_ERROR,
+               "can't get virtual hostname");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    /* Check if we get the correct virtual host */
+    if (c->virt != NULL && vname != NULL && strcmp(c->virt, 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,
+                   "can't malloc virtual hostname");
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+    }
+
+    for (;;) {
+
+        cname = (char *)jk_b_get_string(msg);
+
+        if (!cname) {
+            jk_log(l, JK_LOG_ERROR,
+                   "can't get context");
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+
+        jk_log(l, JK_LOG_DEBUG,
+               "get context %s for virtual %s",
+               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,
+                   "can't add/set context %s",
+                   cname);
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+
+        for (;;) {
+
+            uri = (char *)jk_b_get_string(msg);
+
+            if (!uri) {
+                jk_log(l, JK_LOG_ERROR,
+                       "can't get URI");
+                JK_TRACE_EXIT(l);
+                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", uri,
+                   vname, cname);
+
+            if (context_add_uri(c, cname, uri) == JK_FALSE) {
+                jk_log(l, JK_LOG_ERROR,
+                       "can't add/set uri (%s) for context %s",
+                       uri, cname);
+                JK_TRACE_EXIT(l);
+                return JK_FALSE;
+            }
+        }
+    }
+
+    JK_TRACE_EXIT(l);
+    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_TRACE_ENTER(l);
+
+    /* To be on the safe side */
+    jk_b_reset(msg);
+
+    /*
+     * CONTEXT STATE CMD
+     */
+    if (jk_b_append_byte(msg, AJP14_CONTEXT_STATE_CMD)) {
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    /*
+     * VIRTUAL HOST CSTRING
+     */
+    if (jk_b_append_string(msg, c->virt)) {
+        jk_log(l, JK_LOG_ERROR,
+               "failed appending the virtual host string");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    if (cname) {
+
+        ci = context_find_base(c, cname);
+
+        if (!ci) {
+            jk_log(l, JK_LOG_ERROR,
+                   "unknown context %s",
+                   cname);
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+
+        /*
+         * CONTEXT CSTRING
+         */
+
+        if (jk_b_append_string(msg, cname)) {
+            jk_log(l, JK_LOG_ERROR,
+                   "failed appending the context string %s",
+                   cname);
+            JK_TRACE_EXIT(l);
+            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,
+                       "failed appending the context string %s",
+                       c->contexts[i]->cbase);
+                JK_TRACE_EXIT(l);
+                return JK_FALSE;
+            }
+        }
+    }
+
+    /* End of context list, an empty string */
+
+    if (jk_b_append_string(msg, "")) {
+        jk_log(l, JK_LOG_ERROR,
+               "failed appending end of contexts");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    JK_TRACE_EXIT(l);
+    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;
+
+    JK_TRACE_ENTER(l);
+    /* get virtual name */
+    vname = (char *)jk_b_get_string(msg);
+
+    if (!vname) {
+        jk_log(l, JK_LOG_ERROR,
+               "can't get virtual hostname");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    /* Check if we speak about the correct virtual */
+    if (strcmp(c->virt, vname)) {
+        jk_log(l, JK_LOG_ERROR,
+               "incorrect virtual %s instead of %s",
+               vname, c->virt);
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    for (;;) {
+
+        /* get context name */
+        cname = (char *)jk_b_get_string(msg);
+
+        if (!cname) {
+            jk_log(l, JK_LOG_ERROR,
+                   "can't get context");
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+
+        if (!strlen(cname))
+            break;
+
+        ci = context_find_base(c, cname);
+
+        if (!ci) {
+            jk_log(l, JK_LOG_ERROR,
+                   "unknow context %s for virtual %s",
+                   cname, vname);
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+
+        ci->status = jk_b_get_int(msg);
+
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "updated context %s to state %d",
+                   cname, ci->status);
+    }
+
+    JK_TRACE_EXIT(l);
+    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)
+{
+    int rc;
+    JK_TRACE_ENTER(l);
+    rc = ajp14_unmarshal_context_state_reply(msg, c, l);
+    JK_TRACE_EXIT(l);
+    return rc;
+}
diff --git a/connectors/jk/native/common/jk_ajp14.h b/connectors/jk/native/common/jk_ajp14.h
new file mode 100644
index 0000000..b7845f4
--- /dev/null
+++ b/connectors/jk/native/common/jk_ajp14.h
@@ -0,0 +1,307 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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
+     */
+    const char *web_server_name;
+
+    /*
+     * Pointer to servlet-engine name
+     */
+    char *servlet_engine_name;
+
+    /*
+     * Pointer to secret key
+     */
+    const 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..c437d35
--- /dev/null
+++ b/connectors/jk/native/common/jk_ajp14_worker.c
@@ -0,0 +1,435 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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;
+#if 0
+    /* Not used for now */
+    jk_login_service_t *jl = ae->worker->login;
+#endif
+    jk_context_item_t *ci;
+    jk_context_t *c;
+    char *buf;
+
+#ifndef TESTME
+    JK_TRACE_ENTER(l);
+
+    ajp14_marshal_context_query_into_msgb(msg, we->virtual, l);
+
+    jk_log(l, JK_LOG_DEBUG, "send query");
+
+    if (ajp_connection_tcp_send_message(ae, msg, l) != JK_TRUE) {
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    jk_log(l, JK_LOG_DEBUG, "wait context reply");
+
+    jk_b_reset(msg);
+
+    if (ajp_connection_tcp_get_message(ae, msg, l) != JK_TRUE) {
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    if ((cmd = jk_b_get_byte(msg)) != AJP14_CONTEXT_INFO_CMD) {
+        jk_log(l, JK_LOG_ERROR,
+               "awaited command %d, received %d",
+               AJP14_CONTEXT_INFO_CMD, cmd);
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    if (context_alloc(&c, we->virtual) != JK_TRUE) {
+        jk_log(l, JK_LOG_ERROR,
+               "can't allocate context room");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    if (ajp14_unmarshal_context_info(msg, c, l) != JK_TRUE) {
+        jk_log(l, JK_LOG_ERROR,
+               "can't get context reply");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    jk_log(l, JK_LOG_DEBUG, "received context");
+
+    buf = malloc(MAX_URI_SIZE); /* Really a very long URI */
+
+    if (!buf) {
+        jk_log(l, JK_LOG_ERROR, "can't malloc buf");
+        JK_TRACE_EXIT(l);
+        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,
+                   "worker %s will handle uri %s in context %s [%s]",
+                   ae->worker->name, ci->uris[j], ci->cbase, buf);
+
+            uri_worker_map_add(we->uri_to_worker, buf, ae->worker->name, SOURCE_TYPE_DISCOVER, l);
+        }
+    }
+
+    free(buf);
+    context_free(&c);
+
+#else
+
+    uri_worker_map_add(we->uri_to_worker, "/examples/servlet/*",
+                       ae->worker->name, SOURCE_TYPE_DISCOVER, l);
+    uri_worker_map_add(we->uri_to_worker, "/examples/*.jsp", ae->worker->name,
+                       SOURCE_TYPE_DISCOVER, l);
+    uri_worker_map_add(we->uri_to_worker, "/examples/*.gif", ae->worker->name,
+                       SOURCE_TYPE_DISCOVER, l);
+
+#endif
+
+    JK_TRACE_EXIT(l);
+    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;
+    JK_TRACE_ENTER(l);
+
+    ajp14_marshal_login_init_into_msgb(msg, jl, l);
+
+    jk_log(l, JK_LOG_DEBUG, "send init");
+
+    if (ajp_connection_tcp_send_message(ae, msg, l) != JK_TRUE) {
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    jk_log(l, JK_LOG_DEBUG, "wait init reply");
+
+    jk_b_reset(msg);
+
+    if (ajp_connection_tcp_get_message(ae, msg, l) != JK_TRUE) {
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    if ((cmd = jk_b_get_byte(msg)) != AJP14_LOGSEED_CMD) {
+        jk_log(l, JK_LOG_ERROR,
+               "awaited command %d, received %d",
+               AJP14_LOGSEED_CMD, cmd);
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    if (ajp14_unmarshal_login_seed(msg, jl, l) != JK_TRUE) {
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    jk_log(l, JK_LOG_DEBUG, "received entropy %s",
+           jl->entropy);
+
+    ajp14_compute_md5(jl, l);
+
+    if (ajp14_marshal_login_comp_into_msgb(msg, jl, l) != JK_TRUE) {
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    if (ajp_connection_tcp_send_message(ae, msg, l) != JK_TRUE) {
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    jk_b_reset(msg);
+
+    if (ajp_connection_tcp_get_message(ae, msg, l) != JK_TRUE) {
+        JK_TRACE_EXIT(l);
+        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",
+                   jl->servlet_engine_name);
+            JK_TRACE_EXIT(l);
+            return JK_TRUE;
+        }
+        break;
+
+    case AJP14_LOGNOK_CMD:
+        ajp14_unmarshal_log_nok(msg, l);
+        break;
+    }
+
+    JK_TRACE_EXIT(l);
+    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_TRACE_ENTER(l);
+
+    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);
+
+    JK_TRACE_EXIT(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_TRACE_ENTER(l);
+
+    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);
+
+    JK_TRACE_EXIT(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;
+    const char *secret_key;
+
+    JK_TRACE_ENTER(l);
+    if (ajp_validate(pThis, props, we, l, AJP14_PROTO) == JK_FALSE) {
+        JK_TRACE_EXIT(l);
+        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");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    /* jk_log(l, JK_LOG_DEBUG, "Into ajp14:validate - secret_key=%s", secret_key); */
+    JK_TRACE_EXIT(l);
+    return JK_TRUE;
+}
+
+static int JK_METHOD get_endpoint(jk_worker_t *pThis,
+                                  jk_endpoint_t **pend, jk_logger_t *l)
+{
+    int rc;
+    JK_TRACE_ENTER(l);
+    rc = ajp_get_endpoint(pThis, pend, l, AJP14_PROTO);
+    JK_TRACE_EXIT(l);
+    return rc;
+}
+
+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;
+
+    JK_TRACE_EXIT(l);
+
+    if (ajp_init(pThis, props, we, l, AJP14_PROTO) == JK_FALSE) {
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    aw = pThis->worker_private;
+    pThis->retries = jk_get_worker_retries(props, aw->name,
+                                           JK_RETRIES);
+
+    /* Set Secret Key (used at logon time) */
+    aw->login->secret_key = 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");
+        JK_TRACE_EXIT(l);
+        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");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    if (get_endpoint(pThis, &je, l) == JK_FALSE) {
+        JK_TRACE_EXIT(l);
+        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);
+        JK_TRACE_EXIT(l);
+        return rc;
+    }
+
+    JK_TRACE_EXIT(l);
+    return JK_TRUE;
+}
+
+
+static int JK_METHOD destroy(jk_worker_t **pThis, jk_logger_t *l)
+{
+    int rc;
+    ajp_worker_t *aw = (*pThis)->worker_private;
+
+    JK_TRACE_ENTER(l);
+
+    if (aw->login) {
+        free(aw->login);
+        aw->login = NULL;
+    }
+
+    rc = ajp_destroy(pThis, l, AJP14_PROTO);
+    JK_TRACE_EXIT(l);
+    return rc;
+}
+
+int JK_METHOD ajp14_worker_factory(jk_worker_t **w,
+                                   const char *name, jk_logger_t *l)
+{
+    ajp_worker_t *aw;
+
+    JK_TRACE_ENTER(l);
+
+    if (name == NULL || w == NULL) {
+        JK_LOG_NULL_PARAMS(l);
+        JK_TRACE_EXIT(l);
+        return 0;
+    }
+
+    aw = (ajp_worker_t *) calloc(1, sizeof(ajp_worker_t));
+    if (!aw) {
+        jk_log(l, JK_LOG_ERROR,
+               "malloc of private data failed");
+       JK_TRACE_EXIT(l);
+       return 0;
+    }
+
+    aw->name = strdup(name);
+
+    if (!aw->name) {
+        free(aw);
+        jk_log(l, JK_LOG_ERROR,
+               "malloc failed for name");
+        JK_TRACE_EXIT(l);
+        return 0;
+    }
+
+    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,
+               "malloc failed for login area");
+        JK_TRACE_EXIT(l);
+        return 0;
+    }
+
+    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->worker.maintain = ajp_maintain;
+    aw->worker.retries = JK_RETRIES;
+
+    aw->logon = logon;          /* LogOn Handler for AJP14 */
+    *w = &aw->worker;
+
+    JK_TRACE_EXIT(l);
+    return JK_AJP14_WORKER_TYPE;
+}
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..2ef03a6
--- /dev/null
+++ b/connectors/jk/native/common/jk_ajp14_worker.h
@@ -0,0 +1,52 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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")
+#define JK_AJP14_WORKER_TYPE (3)
+
+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..d431ef0
--- /dev/null
+++ b/connectors/jk/native/common/jk_ajp_common.c
@@ -0,0 +1,2470 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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"
+#if defined(AS400) && !defined(AS400_UTF8)
+#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;
+    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_method(const char *method, 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 'A':
+            return (method[1] == 'C'
+                    && method[2] == 'L'
+                    ? SC_M_ACL : UNKNOWN_METHOD);
+        case 'P':
+            return (method[1] == 'U'
+                    && method[2] == 'T'
+                    ? SC_M_PUT : UNKNOWN_METHOD);
+        case 'G':
+            return (method[1] == 'E'
+                    && method[2] == 'T'
+                    ? SC_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'
+                    ? SC_M_HEAD : UNKNOWN_METHOD);
+        case 'P':
+            return (method[1] == 'O'
+                    && method[2] == 'S'
+                    && method[3] == 'T'
+                    ? SC_M_POST : UNKNOWN_METHOD);
+        case 'M':
+            return (method[1] == 'O'
+                    && method[2] == 'V'
+                    && method[3] == 'E'
+                    ? SC_M_MOVE : UNKNOWN_METHOD);
+        case 'L':
+            return (method[1] == 'O'
+                    && method[2] == 'C'
+                    && method[3] == 'K'
+                    ? SC_M_LOCK : UNKNOWN_METHOD);
+        case 'C':
+            return (method[1] == 'O'
+                    && method[2] == 'P'
+                    && method[3] == 'Y'
+                    ? SC_M_COPY : UNKNOWN_METHOD);
+        default:
+            return UNKNOWN_METHOD;
+        }
+
+    case 5:
+        switch (method[2])
+        {
+        case 'R':
+            return (memcmp(method, "MERGE", 5) == 0
+                    ? SC_M_MERGE : UNKNOWN_METHOD);
+        case 'C':
+            return (memcmp(method, "MKCOL", 5) == 0
+                    ? SC_M_MKCOL : UNKNOWN_METHOD);
+        case 'B':
+            return (memcmp(method, "LABEL", 5) == 0
+                    ? SC_M_LABEL : UNKNOWN_METHOD);
+        case 'A':
+            return (memcmp(method, "TRACE", 5) == 0
+                    ? SC_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
+                        ? SC_M_UNLOCK : UNKNOWN_METHOD);
+            case 'E':
+                return (memcmp(method, "UPDATE", 6) == 0
+                        ? SC_M_UPDATE : UNKNOWN_METHOD);
+            default:
+                return UNKNOWN_METHOD;
+            }
+        case 'R':
+            return (memcmp(method, "REPORT", 6) == 0
+                    ? SC_M_REPORT : UNKNOWN_METHOD);
+        case 'S':
+            return (memcmp(method, "SEARCH", 6) == 0
+                    ? SC_M_SEARCH : UNKNOWN_METHOD);
+        case 'D':
+            return (memcmp(method, "DELETE", 6) == 0
+                    ? SC_M_DELETE : UNKNOWN_METHOD);
+        default:
+            return UNKNOWN_METHOD;
+        }
+
+    case 7:
+        switch (method[1])
+        {
+        case 'P':
+            return (memcmp(method, "OPTIONS", 7) == 0
+                    ? SC_M_OPTIONS : UNKNOWN_METHOD);
+        case 'H':
+            return (memcmp(method, "CHECKIN", 7) == 0
+                    ? SC_M_CHECKIN : UNKNOWN_METHOD);
+        default:
+            return UNKNOWN_METHOD;
+        }
+
+    case 8:
+        switch (method[0])
+        {
+        case 'P':
+            return (memcmp(method, "PROPFIND", 8) == 0
+                    ? SC_M_PROPFIND : UNKNOWN_METHOD);
+        case 'C':
+            return (memcmp(method, "CHECKOUT", 8) == 0
+                    ? SC_M_CHECKOUT : UNKNOWN_METHOD);
+        default:
+            return UNKNOWN_METHOD;
+        }
+
+    case 9:
+        return (memcmp(method, "PROPPATCH", 9) == 0
+                ? SC_M_PROPPATCH : UNKNOWN_METHOD);
+
+    case 10:
+        switch (method[0])
+        {
+        case 'U':
+            return (memcmp(method, "UNCHECKOUT", 10) == 0
+                    ? SC_M_UNCHECKOUT : UNKNOWN_METHOD);
+        case 'M':
+            return (memcmp(method, "MKACTIVITY", 10) == 0
+                    ? SC_M_MKACTIVITY : UNKNOWN_METHOD);
+        default:
+            return UNKNOWN_METHOD;
+        }
+
+    case 11:
+        return (memcmp(method, "MKWORKSPACE", 11) == 0
+                ? SC_M_MKWORKSPACE : UNKNOWN_METHOD);
+
+    case 15:
+        return (memcmp(method, "VERSION-CONTROL", 15) == 0
+                ? SC_M_VERSION_CONTROL : UNKNOWN_METHOD);
+
+    case 16:
+        return (memcmp(method, "BASELINE-CONTROL", 16) == 0
+                ? SC_M_BASELINE_CONTROL : UNKNOWN_METHOD);
+
+    default:
+        return UNKNOWN_METHOD;
+    }
+
+    /* NOTREACHED */
+}
+
+static int sc_for_req_header(const char *header_name)
+{
+    char header[16];
+    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++] = toupper((unsigned char)*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, "OOKIE2", 6) == 0)
+                return SC_COOKIE2;
+            else 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
+                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 */
+}
+
+
+/*
+ * 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)
+    ?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)
+{
+    int method;
+    unsigned int i;
+
+    JK_TRACE_ENTER(l);
+
+    if ((method = sc_for_req_method(s->method,
+                                    strlen(s->method))) == UNKNOWN_METHOD)
+        method = SC_M_JK_STORED;
+
+    if (jk_b_append_byte(msg, JK_AJP13_FORWARD_REQUEST) ||
+        jk_b_append_byte(msg, (unsigned char)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,
+               "failed appending the message begining");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    for (i = 0; i < s->num_headers; i++) {
+        int sc;
+
+        if ((sc = sc_for_req_header(s->headers_names[i])) != UNKNOWN_METHOD) {
+            if (jk_b_append_int(msg, (unsigned short)sc)) {
+                jk_log(l, JK_LOG_ERROR,
+                       "failed appending the header name");
+                JK_TRACE_EXIT(l);
+                return JK_FALSE;
+            }
+        }
+        else {
+            if (jk_b_append_string(msg, s->headers_names[i])) {
+                jk_log(l, JK_LOG_ERROR,
+                       "failed appending the header name");
+                JK_TRACE_EXIT(l);
+                return JK_FALSE;
+            }
+        }
+
+        if (jk_b_append_string(msg, s->headers_values[i])) {
+            jk_log(l, JK_LOG_ERROR,
+                   "failed appending the header value");
+            JK_TRACE_EXIT(l);
+            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,
+                   "failed appending secret");
+            JK_TRACE_EXIT(l);
+            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,
+                   "failed appending the remote user");
+            JK_TRACE_EXIT(l);
+            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,
+                   "failed appending the auth type");
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+    }
+    if (s->query_string) {
+        if (jk_b_append_byte(msg, SC_A_QUERY_STRING) ||
+#if defined(AS400) && !defined(AS400_UTF8)
+            jk_b_append_asciistring(msg, s->query_string)) {
+#else
+            jk_b_append_string(msg, s->query_string)) {
+#endif
+            jk_log(l, JK_LOG_ERROR,
+                   "failed appending the query string");
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+    }
+    if (s->route) {
+        if (jk_b_append_byte(msg, SC_A_ROUTE) ||
+            jk_b_append_string(msg, s->route)) {
+            jk_log(l, JK_LOG_ERROR,
+                   "failed appending the route");
+            JK_TRACE_EXIT(l);
+            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,
+                   "failed appending the SSL certificates");
+            JK_TRACE_EXIT(l);
+            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,
+                   "failed appending the SSL ciphers");
+            JK_TRACE_EXIT(l);
+            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,
+                   "failed appending the SSL session");
+            JK_TRACE_EXIT(l);
+            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,
+                   "failed appending the SSL key size");
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+    }
+
+    /* If the method was unrecognized, encode it as an attribute */
+    if (method == SC_M_JK_STORED) {
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG, "unknown method %s", s->method);
+        if (jk_b_append_byte(msg, SC_A_STORED_METHOD) ||
+            jk_b_append_string(msg, s->method)) {
+            jk_log(l, JK_LOG_ERROR,
+                   "failed appending the request method");
+            JK_TRACE_EXIT(l);
+            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,
+                       "failed appending attribute %s=%s",
+                       s->attributes_names[i], s->attributes_values[i]);
+                JK_TRACE_EXIT(l);
+                return JK_FALSE;
+            }
+        }
+    }
+
+    if (jk_b_append_byte(msg, SC_A_ARE_DONE)) {
+        jk_log(l, JK_LOG_ERROR,
+               "failed appending the message end");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG, "ajp marshaling done");
+    JK_TRACE_EXIT(l);
+    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);
+    JK_TRACE_ENTER(l);
+
+    if (!d->status) {
+        jk_log(l, JK_LOG_ERROR,
+               "NULL status");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    d->msg = (char *)jk_b_get_string(msg);
+    if (d->msg) {
+#if (defined(AS400) && !defined(AS400_UTF8)) || defined(_OSD_POSIX)
+        jk_xlate_from_ascii(d->msg, strlen(d->msg));
+#endif
+    }
+
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+               "status = %d", d->status);
+
+    d->num_headers = jk_b_get_int(msg);
+    d->header_names = d->header_values = NULL;
+
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+               "Number of headers is = %d",
+               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 int i;
+            for (i = 0; i < d->num_headers; i++) {
+                unsigned short name = jk_b_pget_int(msg, msg->pos);
+
+                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,
+                               "No such sc (%d)", name);
+                        JK_TRACE_EXIT(l);
+                        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,
+                               "NULL header name");
+                        JK_TRACE_EXIT(l);
+                        return JK_FALSE;
+                    }
+#if (defined(AS400) && !defined(AS400_UTF8)) || 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,
+                           "NULL header value");
+                    JK_TRACE_EXIT(l);
+                    return JK_FALSE;
+                }
+
+#if (defined(AS400) && !defined(AS400_UTF8)) || defined(_OSD_POSIX)
+                jk_xlate_from_ascii(d->header_values[i],
+                                    strlen(d->header_values[i]));
+#endif
+
+                if (JK_IS_DEBUG_LEVEL(l))
+                    jk_log(l, JK_LOG_DEBUG,
+                           "Header[%d] [%s] = [%s]",
+                           i, d->header_names[i], d->header_values[i]);
+            }
+        }
+    }
+
+    JK_TRACE_EXIT(l);
+    return JK_TRUE;
+}
+
+/*
+ * Reset the endpoint (clean buf and close socket)
+ */
+
+static void ajp_reset_endpoint(ajp_endpoint_t * ae, jk_logger_t *l)
+{
+    if (IS_VALID_SOCKET(ae->sd) && !ae->reuse) {
+        jk_close_socket(ae->sd);
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+            "reset socket with sd = %u", ae->sd );
+        ae->sd = JK_INVALID_SOCKET;
+    }
+    jk_reset_pool(&(ae->pool));
+}
+
+/*
+ * Close the endpoint (close pool and close socket)
+ */
+
+void ajp_close_endpoint(ajp_endpoint_t * ae, jk_logger_t *l)
+{
+    JK_TRACE_ENTER(l);
+
+    if (IS_VALID_SOCKET(ae->sd)) {
+        jk_shutdown_socket(ae->sd);
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "closed socket with sd = %d", ae->sd);
+        ae->sd = JK_INVALID_SOCKET;
+    }
+
+    jk_close_pool(&(ae->pool));
+    free(ae);
+    JK_TRACE_EXIT(l);
+}
+
+
+/*
+ * Try another connection from cache
+ */
+
+static void ajp_next_connection(ajp_endpoint_t *ae, jk_logger_t *l)
+{
+    int rc;
+    ajp_worker_t *aw = ae->worker;
+    jk_sock_t sock = ae->sd;
+
+    /* Mark existing endpoint socket as closed */
+    ae->sd = JK_INVALID_SOCKET;
+    JK_ENTER_CS(&aw->cs, rc);
+    if (rc) {
+        unsigned int i;
+        for (i = 0; i < aw->ep_cache_sz; i++) {
+            /* Find cache slot with usable socket */
+            if (aw->ep_cache[i] && IS_VALID_SOCKET(aw->ep_cache[i]->sd)) {
+                ae->sd = aw->ep_cache[i]->sd;
+                aw->ep_cache[i]->sd = JK_INVALID_SOCKET;
+                break;
+            }
+        }
+        JK_LEAVE_CS(&aw->cs, rc);
+    }
+    /* Close previous socket */
+    if (IS_VALID_SOCKET(sock))
+        jk_close_socket(sock);
+}
+
+/*
+ * Wait input event on ajp_endpoint for timeout ms
+ */
+static int ajp_is_input_event(ajp_endpoint_t * ae, int timeout, jk_logger_t *l)
+{
+    fd_set rset;
+    struct timeval tv;
+    int rc;
+
+    FD_ZERO(&rset);
+    FD_SET(ae->sd, &rset);
+    tv.tv_sec = timeout / 1000;
+    tv.tv_usec = (timeout % 1000) * 1000;
+
+    do {
+        rc = select((int)ae->sd + 1, &rset, NULL, NULL, &tv);
+    } while (rc < 0 && errno == EINTR);
+
+    ae->last_errno = 0;
+    if (rc == 0) {
+        /* Timeout. Set the errno to timeout */
+#if defined(WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__))
+        errno = WSAETIMEDOUT - WSABASEERR;
+#else
+        errno = ETIMEDOUT;
+#endif
+        ae->last_errno = errno;
+        return JK_FALSE;
+    }
+    else if (rc < 0) {
+        ae->last_errno = errno;
+        jk_log(l, JK_LOG_WARNING,
+               "error during select (errno=%d)", ae->last_errno);
+        return JK_FALSE;
+    }
+    else
+        return JK_TRUE;
+}
+
+
+/*
+ * Handle the CPING/CPONG initial query
+ */
+static int ajp_handle_cping_cpong(ajp_endpoint_t * ae, int timeout, jk_logger_t *l)
+{
+    int cmd;
+    jk_msg_buf_t *msg;
+
+    JK_TRACE_ENTER(l);
+    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_INFO,
+               "can't send cping query");
+        JK_TRACE_EXIT(l);
+        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_INFO, "timeout in reply pong");
+        JK_TRACE_EXIT(l);
+        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_INFO,
+               "awaited reply cpong, not received");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    if ((cmd = jk_b_get_byte(msg)) != AJP13_CPONG_REPLY) {
+        jk_log(l, JK_LOG_INFO,
+               "awaited reply cpong, received %d instead",
+               cmd);
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    JK_TRACE_EXIT(l);
+    return JK_TRUE;
+}
+
+int ajp_connect_to_endpoint(ajp_endpoint_t * ae, jk_logger_t *l)
+{
+    char buf[32];
+    int rc = JK_TRUE;
+
+    JK_TRACE_ENTER(l);
+
+    ae->sd = jk_open_socket(&ae->worker->worker_inet_addr,
+                            ae->worker->keepalive,
+                            ae->worker->socket_timeout,
+                            ae->worker->socket_buf, l);
+    if (IS_VALID_SOCKET(ae->sd)) {
+        ae->last_errno = 0;
+        if (JK_IS_DEBUG_LEVEL(l)) {
+            jk_log(l, JK_LOG_DEBUG,
+                   "Connected socket %d to (%s)",
+                   ae->sd,
+                   jk_dump_hinfo(&ae->worker->worker_inet_addr, buf));
+        }
+        /* set last_access only if needed */
+        if (ae->worker->cache_timeout > 0)
+            ae->last_access = time(NULL);
+        /* Check if we must execute a logon after the physical connect */
+        if (ae->worker->logon != NULL) {
+            rc = ae->worker->logon(ae, l);
+            JK_TRACE_EXIT(l);
+            return rc;
+        }
+        /* should we send a CPING to validate connection ? */
+        if (ae->worker->connect_timeout > 0) {
+            rc = ajp_handle_cping_cpong (ae,
+                        ae->worker->connect_timeout, l);
+            JK_TRACE_EXIT(l);
+            return rc;
+        }
+        JK_TRACE_EXIT(l);
+        return JK_TRUE;
+    }
+    ae->last_errno = errno;
+
+    jk_log(l, JK_LOG_INFO,
+           "Failed opening socket to (%s) (errno=%d)",
+           jk_dump_hinfo(&ae->worker->worker_inet_addr, buf), ae->last_errno);
+    JK_TRACE_EXIT(l);
+    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)
+{
+    int rc;
+
+    JK_TRACE_ENTER(l);
+    if (ae->proto == AJP13_PROTO) {
+        jk_b_end(msg, AJP13_WS_HEADER);
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_dump_buff(l, JK_LOG_DEBUG, "sending to ajp13", msg);
+    }
+    else if (ae->proto == AJP14_PROTO) {
+        jk_b_end(msg, AJP14_WS_HEADER);
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_dump_buff(l, JK_LOG_DEBUG, "sending to ajp14", msg);
+    }
+    else {
+        jk_log(l, JK_LOG_ERROR,
+               "unknown protocol %d, supported are AJP13/AJP14", ae->proto);
+        JK_TRACE_EXIT(l);
+        return JK_FATAL_ERROR;
+    }
+
+    if ((rc = jk_tcp_socket_sendfull(ae->sd, msg->buf,
+                                     msg->len)) > 0) {
+        ae->endpoint.wr += msg->len;
+        JK_TRACE_EXIT(l);
+        ae->last_errno = 0;
+        return JK_TRUE;
+    }
+    ae->last_errno = errno;
+    jk_log(l, JK_LOG_ERROR,
+           "sendfull returned %d (errno=%d)", rc, ae->last_errno);
+
+    JK_TRACE_EXIT(l);
+    return JK_FALSE;
+}
+
+/*
+ * 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];
+
+    JK_TRACE_ENTER(l);
+
+    rc = jk_tcp_socket_recvfull(ae->sd, head, AJP_HEADER_LEN);
+
+    if (rc < 0) {
+        ae->last_errno = errno;
+        if (rc == JK_SOCKET_EOF) {
+            jk_log(l, JK_LOG_INFO,
+                   "(%s) Tomcat has forced a connection close for socket %d",
+                   ae->worker->name, ae->sd);
+            JK_TRACE_EXIT(l);
+        }
+        else {
+            jk_log(l, JK_LOG_ERROR,
+                   "(%s) can't receive the response message from tomcat, "
+                   "network problems or tomcat (%s) is down (errno=%d)",
+                   ae->worker->name, jk_dump_hinfo(&ae->worker->worker_inet_addr, buf),
+                   ae->last_errno);
+             JK_TRACE_EXIT(l);
+        }
+        return JK_FALSE;
+    }
+    ae->last_errno = 0;
+    ae->endpoint.rd += rc;
+    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,
+                       "received AJP14 reply on an AJP13 connection from %s",
+                       jk_dump_hinfo(&ae->worker->worker_inet_addr, buf));
+            }
+            else {
+                jk_log(l, JK_LOG_ERROR,
+                       "wrong message format 0x%04x from %s",
+                       header, jk_dump_hinfo(&ae->worker->worker_inet_addr,
+                                             buf));
+            }
+            JK_TRACE_EXIT(l);
+            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,
+                       "received AJP13 reply on an AJP14 connection from %s",
+                       jk_dump_hinfo(&ae->worker->worker_inet_addr, buf));
+            }
+            else {
+                jk_log(l, JK_LOG_ERROR,
+                       "wrong message format 0x%04x from %s",
+                       header, jk_dump_hinfo(&ae->worker->worker_inet_addr,
+                                             buf));
+            }
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+    }
+
+    msglen = ((head[2] & 0xff) << 8);
+    msglen += (head[3] & 0xFF);
+
+    if (msglen > msg->maxlen) {
+        jk_log(l, JK_LOG_ERROR,
+               "wrong message size %d %d from %s",
+               msglen, msg->maxlen,
+               jk_dump_hinfo(&ae->worker->worker_inet_addr, buf));
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    msg->len = msglen;
+    msg->pos = 0;
+
+    rc = jk_tcp_socket_recvfull(ae->sd, msg->buf, msglen);
+    if (rc < 0) {
+        ae->last_errno = errno;
+        if (rc == JK_SOCKET_EOF) {
+            jk_log(l, JK_LOG_ERROR,
+                   "(%s) can't receive the response message from tomcat, "
+                   "tomcat (%s) has forced a connection close for socket %d",
+                   ae->worker->name, jk_dump_hinfo(&ae->worker->worker_inet_addr, buf),
+                   ae->sd);
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+        else {
+            jk_log(l, JK_LOG_ERROR,
+                   "(%s) can't receive the response message from tomcat, "
+                   "network problems or tomcat (%s) is down (errno=%d)",
+                   ae->worker->name, jk_dump_hinfo(&ae->worker->worker_inet_addr, buf),
+                   ae->last_errno);
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+    }
+    ae->last_errno = 0;
+    ae->endpoint.rd += rc;
+
+    if (ae->proto == AJP13_PROTO) {
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_dump_buff(l, JK_LOG_DEBUG, "received from ajp13", msg);
+    }
+    else if (ae->proto == AJP14_PROTO) {
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_dump_buff(l, JK_LOG_DEBUG, "received from ajp14", msg);
+    }
+    JK_TRACE_EXIT(l);
+    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, jk_logger_t *l,
+                                      unsigned char *buf, unsigned int len)
+{
+    unsigned int rdlen = 0;
+    unsigned int padded_len = len;
+
+    JK_TRACE_ENTER(l);
+    if (s->is_chunked && s->no_more_chunks) {
+        JK_TRACE_EXIT(l);
+        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 int this_time = 0;
+        if (!s->read(s, buf + rdlen, len - rdlen, &this_time)) {
+            /* Remote Client read failed. */
+            JK_TRACE_EXIT(l);
+            return JK_CLIENT_RD_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 = msg->buf;
+
+    JK_TRACE_ENTER(l);
+    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, l, read_buf, len)) < 0) {
+        jk_log(l, JK_LOG_INFO,
+               "(%s) receiving data from client failed. "
+               "Connection aborted or network problems",
+               ae->worker->name);
+        JK_TRACE_EXIT(l);
+        return JK_CLIENT_RD_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,
+                   "Failed appending message length");
+            JK_TRACE_EXIT(l);
+            return JK_CLIENT_RD_ERROR;
+        }
+    }
+
+    msg->len += len;
+
+    JK_TRACE_EXIT(l);
+    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;
+
+    JK_TRACE_ENTER(l);
+    /* Up to now, we can recover */
+    op->recoverable = JK_TRUE;
+
+    /*
+     * First try to reuse open connections...
+     */
+    while (IS_VALID_SOCKET(ae->sd)) {
+        int rc = 0;
+        err = 0;
+        if (!jk_is_socket_connected(ae->sd)) {
+            jk_log(l, JK_LOG_DEBUG,
+                   "(%s) socket %d is not connected any more (errno=%d)",
+                   ae->worker->name, ae->sd, errno);
+            jk_close_socket(ae->sd);
+            ae->sd = JK_INVALID_SOCKET;
+            err = 1;
+        }
+        if (ae->worker->prepost_timeout > 0 && !err) {
+            /* handle cping/cpong if prepost_timeout is set
+             * If the socket is disconnected no need to handle
+             * the cping/cpong
+             */
+            if (ajp_handle_cping_cpong(ae,
+                        ae->worker->prepost_timeout, l) == JK_FALSE) {
+                /* XXX: Is there any reason to try other
+                 * connections to the node if one of them fails
+                 * the cping/cpong heartbeat?
+                 * Tomcat can be either too busy or simply dead, so
+                 * there is a chance that all oter connections would
+                 * fail as well.
+                 */
+                err = 2;
+            }
+        }
+
+        /* 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 ||
+            ((rc = ajp_connection_tcp_send_message(ae, op->request, l)) != JK_TRUE)) {
+            if (rc != JK_FATAL_ERROR) {
+                if (err == 1) {                
+                    jk_log(l, JK_LOG_DEBUG,
+                           "(%s) failed sending request. "
+                           "Will try another pooled connection",
+                            ae->worker->name);
+                }
+                else {
+                    jk_log(l, JK_LOG_INFO,
+                           "(%s) error sending request. "
+                           "Will try another pooled connection",
+                            ae->worker->name);                    
+                }
+                ajp_next_connection(ae, l);
+            }
+            else {
+                op->recoverable = JK_FALSE;
+                jk_log(l, JK_LOG_ERROR,
+                       "(%s) error sending request. Unrecoverable operation",
+                       ae->worker->name);
+                jk_close_socket(ae->sd);
+                ae->sd = JK_INVALID_SOCKET;
+                JK_TRACE_EXIT(l);
+                return JK_FALSE;
+            }
+        }
+        else
+            break;
+    }
+
+    /*
+     * If we failed to reuse a connection, try to reconnect.
+     */
+    if (!IS_VALID_SOCKET(ae->sd)) {
+        if (err == 1) {
+            /* If err is set, the tomcat is disconnected */
+            jk_log(l, JK_LOG_INFO,
+                   "(%s) all endpoints are disconnected", ae->worker->name);
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+        else if (err) {
+            /* If err is set, the tomcat is dead */
+            jk_log(l, JK_LOG_INFO,
+                   "(%s) all endpoints are dead", ae->worker->name);
+            /* TODO: What is the purpose of the following log message?
+             *       IMO it is very confusing and does not reflect the
+             *       real reason (CPING/CPONG failed) of the error.
+             *       Further more user might deliberately set the
+             *       connectionTimeout and this is normal operational
+             *       message in that case.
+             */
+            jk_log(l, JK_LOG_INFO,
+                   "(%s) increase the backend idle connection "
+                   "timeout or the connection_pool_minsize",
+                   ae->worker->name);
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+        /* Connect to the backend.
+         * This can be either uninitalized connection or a reconnect.
+         */
+        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_TRUE) {
+                /* Close the socket if unable to send request */
+                jk_close_socket(ae->sd);
+                ae->sd = JK_INVALID_SOCKET;
+                jk_log(l, JK_LOG_INFO,
+                       "(%s) error sending request on a fresh connection (errno=%d)",
+                       ae->worker->name, ae->last_errno);
+                JK_TRACE_EXIT(l);
+                return JK_FALSE;
+            }
+        }
+        else {
+            /* Close the socket if unable to connect */
+            jk_close_socket(ae->sd);
+            ae->sd = JK_INVALID_SOCKET;
+            jk_log(l, JK_LOG_INFO,
+                   "(%s) error connecting to the backend server (errno=%d)",
+                   ae->worker->name, ae->last_errno);
+            JK_TRACE_EXIT(l);
+            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.
+     */
+
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+               "(%s) request body to send %d - request body to resend %d",
+               ae->worker->name, ae->left_bytes_to_send,
+               op->reply->len - 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 = op->post->len;
+    if (postlen > AJP_HEADER_LEN) {
+        if (ajp_connection_tcp_send_message(ae, op->post, l) != JK_TRUE) {
+            /* Close the socket if unable to send request */
+            jk_close_socket(ae->sd);
+            ae->sd = JK_INVALID_SOCKET;
+            jk_log(l, JK_LOG_ERROR, "(%s) failed resending request body (%d)",
+                   ae->worker->name, postlen);
+            JK_TRACE_EXIT(l);
+            return JK_SERVER_ERROR;
+        }
+        else {
+            if (JK_IS_DEBUG_LEVEL(l))
+                jk_log(l, JK_LOG_DEBUG, "Resent the request body (%d)",
+                       postlen);
+        }
+    }
+    else if (s->reco_status == RECO_FILLED) {
+        /* Recovery in LB MODE */
+        postlen = s->reco_buf->len;
+
+        if (postlen > AJP_HEADER_LEN) {
+            if (ajp_connection_tcp_send_message(ae, s->reco_buf, l) != JK_TRUE) {
+                /* Close the socket if unable to send request */
+                jk_close_socket(ae->sd);
+                ae->sd = JK_INVALID_SOCKET;
+                jk_log(l, JK_LOG_ERROR,
+                       "(%s) failed resending request body (lb mode) (%d)",
+                       ae->worker->name, postlen);
+                JK_TRACE_EXIT(l);
+                return JK_SERVER_ERROR;
+            }
+        }
+        else {
+            if (JK_IS_DEBUG_LEVEL(l))
+                jk_log(l, JK_LOG_DEBUG,
+                       "Resent the request body (lb mode) (%d)", 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;
+                JK_TRACE_EXIT(l);
+                return JK_CLIENT_RD_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_TRUE) {
+                /* Close the socket if unable to send request */
+                jk_close_socket(ae->sd);
+                ae->sd = JK_INVALID_SOCKET;
+                jk_log(l, JK_LOG_ERROR, "(%s) error sending request body",
+                       ae->worker->name);
+                JK_TRACE_EXIT(l);
+                return JK_SERVER_ERROR;
+            }
+        }
+    }
+    JK_TRACE_EXIT(l);
+    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);
+
+    JK_TRACE_ENTER(l);
+    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,
+                       "ajp_unmarshal_response failed");
+                JK_TRACE_EXIT(l);
+                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);
+            if (r->flush && r->flush_header)
+                r->flush(r);
+            r->http_response_status = res.status;
+        }
+        return JK_AJP13_SEND_HEADERS;
+
+    case JK_AJP13_SEND_BODY_CHUNK:
+        {
+            unsigned int len = (unsigned int)jk_b_get_int(msg);
+            /*
+             * Do a sanity check on len to prevent write reading beyond buffer
+             * boundaries and thus revealing possible sensitive memory
+             * contents to the client.
+             * len cannot be larger than msg->len - 3 because the ajp message
+             * contains the magic byte for JK_AJP13_SEND_BODY_CHUNK (1 byte)
+             * and the length of the chunk (2 bytes). The remaining part of
+             * the message is the chunk.
+             */
+            if (len > (unsigned int)(msg->len - 3)) {
+                jk_log(l, JK_LOG_ERROR,
+                       "Chunk length too large. Length of AJP message is %i,"
+                       " chunk length is %i.", msg->len, len);
+                JK_TRACE_EXIT(l);
+                return JK_INTERNAL_ERROR;
+            }
+            if (len == 0) {
+                /* AJP13_SEND_BODY_CHUNK with length 0 is
+                 * explicit flush packet message.
+                 */
+                if (r->flush)
+                    r->flush(r);
+            }
+            else {
+                if (!r->write(r, msg->buf + msg->pos, len)) {
+                    jk_log(l, JK_LOG_INFO,
+                           "Writing to client aborted or client network problems");
+                    JK_TRACE_EXIT(l);
+                    return JK_CLIENT_WR_ERROR;
+                }
+                if (r->flush && r->flush_packets)
+                    r->flush(r);
+            }
+        }
+        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;
+                JK_TRACE_EXIT(l);
+                return JK_AJP13_HAS_RESPONSE;
+            }
+
+            jk_log(l, JK_LOG_INFO,
+                   "Reding from client aborted or client network problems");
+
+            JK_TRACE_EXIT(l);
+            return JK_CLIENT_RD_ERROR;
+        }
+        break;
+
+    case JK_AJP13_END_RESPONSE:
+        ae->reuse = (int)jk_b_get_byte(msg);
+        if (!ae->reuse) {
+            /*
+             * AJP13 protocol reuse flag set to false.
+             * Tomcat will close its side of the connection.
+             */
+            jk_log(l, JK_LOG_WARNING, "AJP13 protocol: Reuse is set to false");
+        }
+        else if (r->disable_reuse) {
+            if (JK_IS_DEBUG_LEVEL(l)) {
+                jk_log(l, JK_LOG_DEBUG, "AJP13 protocol: Reuse is disabled");
+            }
+            ae->reuse = JK_FALSE;
+        }
+        else {
+            /* Reuse in all cases */
+            if (JK_IS_DEBUG_LEVEL(l)) {
+                jk_log(l, JK_LOG_DEBUG, "AJP13 protocol: Reuse is OK");
+            }
+            ae->reuse = JK_TRUE;
+        }
+        /* Flush after the last write */
+        if (r->flush && !r->flush_packets)
+            r->flush(r);
+
+        JK_TRACE_EXIT(l);
+        return JK_AJP13_END_RESPONSE;
+        break;
+
+    default:
+        jk_log(l, JK_LOG_ERROR,
+               "Unknown AJP protocol code: %02X", code);
+        JK_TRACE_EXIT(l);
+        return JK_AJP13_ERROR;
+    }
+
+    JK_TRACE_EXIT(l);
+    return JK_AJP13_NO_RESPONSE;
+}
+
+static int is_http_status_fail(ajp_worker_t *w, int status)
+{
+    unsigned int i;
+    for (i = 0; i < w->http_status_fail_num; i++) {
+        if (w->http_status_fail[i] == status)
+            return 1;
+    }
+    return 0;
+}
+
+
+/*
+ * 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;
+
+    JK_TRACE_ENTER(l);
+
+    /* 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,
+                       "(%s) Timeout with waiting reply from tomcat. "
+                       "Tomcat is down, stopped or network problems (errno=%d)",
+                       p->worker->name, p->last_errno);
+                if (headeratclient == JK_FALSE) {
+                    if (p->worker->recovery_opts & RECOVER_ABORT_IF_TCGETREQUEST)
+                        op->recoverable = JK_FALSE;
+                }
+                else {
+                    if (p->worker->recovery_opts & RECOVER_ABORT_IF_TCSENDHEADER)
+                        op->recoverable = JK_FALSE;
+                }
+
+                JK_TRACE_EXIT(l);
+                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,
+                       "(%s) Tomcat is down or refused connection. "
+                       "No response has been sent to the client (yet)",
+                       p->worker->name);
+                /*
+                 * 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
+                 */
+                JK_TRACE_EXIT(l);
+                return JK_FALSE;
+            }
+            else {
+                jk_log(l, JK_LOG_ERROR,
+                       "(%s) Tomcat is down or network problems. "
+                       "Part of the response has already been sent to the client",
+                       p->worker->name);
+
+                /* 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;
+
+                JK_TRACE_EXIT(l);
+                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) {
+            JK_TRACE_EXIT(l);
+            return JK_TRUE;
+        }
+        else if (JK_AJP13_SEND_HEADERS == rc) {
+            if (is_http_status_fail(p->worker, s->http_response_status)) {
+                JK_TRACE_EXIT(l);
+                return JK_STATUS_ERROR;
+            }
+            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,
+                       "(%s) Tomcat is down or network problems",
+                        p->worker->name);
+                jk_close_socket(p->sd);
+                p->sd = JK_INVALID_SOCKET;
+                JK_TRACE_EXIT(l);
+                return JK_FALSE;
+            }
+        }
+        else if (JK_AJP13_ERROR == rc) {
+            /*
+             * Tomcat has send invalid AJP message.
+             * Locadbalancer if present will decide if
+             * failover is possible.
+             */
+            JK_TRACE_EXIT(l);
+            return JK_SERVER_ERROR;
+        }
+        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;
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+        else if (JK_CLIENT_RD_ERROR == rc) {
+            /*
+             * Client has stop sending to us, so get out.
+             * We assume this isn't our fault, so just a normal exit.
+             */
+            JK_TRACE_EXIT(l);
+            return JK_CLIENT_RD_ERROR;
+        }
+        else if (JK_CLIENT_WR_ERROR == rc) {
+            /*
+             * Client has stop receiving to us, so get out.
+             * We assume this isn't our fault, so just a normal exit.
+             */
+            JK_TRACE_EXIT(l);
+            return JK_CLIENT_WR_ERROR;
+        }
+        else if (JK_SERVER_ERROR == rc) {
+            /*
+             * Tomcat has stop talking to us, so get out.
+             * Locadbalancer if present will decide if
+             * failover is possible.
+             */
+            JK_TRACE_EXIT(l);
+            return JK_SERVER_ERROR;
+        }
+        else if (rc < 0) {
+            JK_TRACE_EXIT(l);
+            return (JK_FALSE);  /* XXX error */
+        }
+    }
+    /* XXX: Not reached? */
+    JK_TRACE_EXIT(l);
+    return JK_FALSE;
+}
+
+/*
+ * 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)
+ *
+ */
+static int JK_METHOD ajp_service(jk_endpoint_t *e,
+                                 jk_ws_service_t *s,
+                                 jk_logger_t *l, int *is_error)
+{
+    int i, err;
+    ajp_operation_t oper;
+    ajp_operation_t *op = &oper;
+    ajp_endpoint_t *p;
+
+    JK_TRACE_ENTER(l);
+
+    if (is_error)
+        *is_error = JK_HTTP_SERVER_ERROR;
+    if (!e || !e->endpoint_private || !s || !is_error) {
+        JK_LOG_NULL_PARAMS(l);
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    p = e->endpoint_private;
+    op->request = jk_b_new(&(p->pool));
+    jk_b_set_buffer_size(op->request, p->worker->max_packet_size);
+    jk_b_reset(op->request);
+
+    op->reply = jk_b_new(&(p->pool));
+    jk_b_set_buffer_size(op->reply, p->worker->max_packet_size);
+    jk_b_reset(op->reply);
+
+    op->post = jk_b_new(&(p->pool));
+    jk_b_set_buffer_size(op->post, p->worker->max_packet_size);
+    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;
+
+    s->secret = p->worker->secret;
+
+    /*
+     * We get here initial request (in reqmsg)
+     */
+    if (!ajp_marshal_into_msgb(op->request, s, l, p)) {
+        *is_error = JK_REQUEST_TOO_LARGE;
+        jk_log(l, JK_LOG_INFO,
+                "Creating AJP message failed, "
+                "without recovery");
+        JK_TRACE_EXIT(l);
+        return JK_CLIENT_ERROR;
+    }
+
+    if (JK_IS_DEBUG_LEVEL(l)) {
+        jk_log(l, JK_LOG_DEBUG, "processing %s with %d retries",
+               p->worker->name, p->worker->worker.retries);
+    }
+    /*
+     * JK_RETRIES could be replaced by the number of workers in
+     * a load-balancing configuration
+     */
+    for (i = 0; i < p->worker->worker.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_error = JK_HTTP_SERVER_ERROR;
+                jk_log(l, JK_LOG_ERROR,
+                       "(%s) sending request to tomcat failed "
+                       "without recovery in send loop %d",
+                       p->worker->name, i);
+                JK_TRACE_EXIT(l);
+                return JK_FALSE;
+            }
+
+            /* Up to there we can recover */
+
+            err = ajp_get_reply(e, s, l, p, op);
+            if (err == JK_TRUE) {
+                *is_error = JK_HTTP_OK;
+                /* Done with the request */
+                JK_TRACE_EXIT(l);
+                return JK_TRUE;
+            }
+
+            if (err == JK_CLIENT_RD_ERROR) {
+                *is_error = JK_HTTP_BAD_REQUEST;
+                if (p->worker->recovery_opts & RECOVER_ABORT_IF_CLIENTERROR) {
+                    /* Mark the endpoint for shutdown */
+                    p->reuse = JK_FALSE;
+                }
+                jk_log(l, JK_LOG_INFO,
+                       "(%s) request failed, "
+                       "because of client read error "
+                       "without recovery in send loop attempt=%d",
+                       p->worker->name, i);
+                JK_TRACE_EXIT(l);
+                return JK_CLIENT_ERROR;
+            }
+            else if (err == JK_CLIENT_WR_ERROR) {
+                /* XXX: Is this correct to log this as 200? */
+                *is_error = JK_HTTP_OK;
+                if (p->worker->recovery_opts & RECOVER_ABORT_IF_CLIENTERROR) {
+                    /* Mark the endpoint for shutdown */
+                    p->reuse = JK_FALSE;
+                }
+                jk_log(l, JK_LOG_INFO,
+                       "(%s) request failed, "
+                       "because of client write error "
+                       "without recovery in send loop attempt=%d",
+                       p->worker->name, i);
+                JK_TRACE_EXIT(l);
+                return JK_CLIENT_ERROR;
+            }
+            else if (err == JK_SERVER_ERROR) {
+                *is_error = JK_HTTP_SERVER_ERROR;
+                jk_log(l, JK_LOG_INFO,
+                       "(%s) request failed, "
+                       "because of server error "
+                       "without recovery in send loop attempt=%d",
+                       p->worker->name, i);
+                JK_TRACE_EXIT(l);
+                return JK_SERVER_ERROR;
+            }
+            else if (err == JK_STATUS_ERROR) {
+                jk_log(l, JK_LOG_INFO,
+                       "(%s) request failed, "
+                       "because of response status %d, "
+                       "recoverable operation attempt=%d",
+                       p->worker->name,
+                       s->http_response_status, i);
+                JK_TRACE_EXIT(l);
+                if (i >= JK_RETRIES) {
+                    jk_sleep(JK_SLEEP_DEF);
+                }
+            }
+            else {
+                /* 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_error = JK_HTTP_BAD_GATEWAY;
+                    jk_log(l, JK_LOG_ERROR,
+                           "(%s) receiving reply from tomcat failed "
+                           "without recovery in send loop attempt=%d",
+                           p->worker->name, i);
+                    JK_TRACE_EXIT(l);
+                    return JK_FALSE;
+                }
+                jk_log(l, JK_LOG_INFO,
+                       "(%s) receiving from tomcat failed, "
+                       "recoverable operation attempt=%d",
+                       p->worker->name, i);
+                /* Check for custom retries */
+                if (i >= JK_RETRIES) {
+                    jk_sleep(JK_SLEEP_DEF);
+                }
+            }
+        }
+        if (err == JK_CLIENT_RD_ERROR) {
+            *is_error = JK_HTTP_BAD_REQUEST;
+            if (p->worker->recovery_opts & RECOVER_ABORT_IF_CLIENTERROR) {
+                /* Mark the endpoint for shutdown */
+                p->reuse = JK_FALSE;
+            }
+            jk_log(l, JK_LOG_INFO,
+                   "(%s) sending request to tomcat failed, "
+                   "because of client read error "
+                   "without recovery in send loop attempt=%d",
+                   p->worker->name, i);
+            JK_TRACE_EXIT(l);
+            return JK_CLIENT_ERROR;
+        }
+        else if (err == JK_CLIENT_WR_ERROR) {
+            *is_error = JK_HTTP_OK;
+            if (p->worker->recovery_opts & RECOVER_ABORT_IF_CLIENTERROR) {
+                /* Mark the endpoint for shutdown */
+                p->reuse = JK_FALSE;
+            }
+            jk_log(l, JK_LOG_INFO,
+                   "(%s) sending request to tomcat failed, "
+                   "because of client write error "
+                   "without recovery in send loop attempt=%d",
+                   p->worker->name, i);
+            JK_TRACE_EXIT(l);
+            return JK_CLIENT_ERROR;
+        }
+        else {
+            jk_log(l, JK_LOG_INFO,
+                   "(%s) sending request to tomcat failed,  "
+                   "recoverable operation attempt=%d",
+                   p->worker->name, i + 1);
+        }
+        /* Get another connection from the pool and try again.
+         * Note: All sockets are probably closed already.
+         */
+        ajp_next_connection(p, l);
+    }
+    *is_error = JK_HTTP_SERVER_BUSY;
+    /* Log the error only once per failed request. */
+    jk_log(l, JK_LOG_ERROR,
+           "(%s) Connecting to tomcat failed. Tomcat is probably not started "
+           "or is listening on the wrong port",
+           p->worker->name);
+
+    JK_TRACE_EXIT(l);
+    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;
+    const char *host;
+
+    JK_TRACE_ENTER(l);
+
+    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_ERROR,
+               "unknown protocol %d", proto);
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    if (pThis && pThis->worker_private) {
+        ajp_worker_t *p = pThis->worker_private;
+        p->port = jk_get_worker_port(props, p->name, port);
+        p->host = jk_get_worker_host(props, p->name, host);
+
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "worker %s contact is '%s:%d'",
+                   p->name, p->host, p->port);
+
+        if (p->port > 1024) {
+            if (jk_resolve(p->host, p->port, &p->worker_inet_addr)) {
+                JK_TRACE_EXIT(l);
+                return JK_TRUE;
+            }
+            jk_log(l, JK_LOG_ERROR,
+                   "can't resolve tomcat address %s", p->host);
+        }
+        jk_log(l, JK_LOG_ERROR,
+               "invalid host and port %s %d",
+               ((p->host == NULL) ? "NULL" : p->host), p->port);
+    }
+    else {
+        JK_LOG_NULL_PARAMS(l);
+    }
+
+    JK_TRACE_EXIT(l);
+    return JK_FALSE;
+}
+
+static int ajp_create_endpoint_cache(ajp_worker_t *p, int proto, jk_logger_t *l)
+{
+    unsigned int i;
+    time_t now = time(NULL);
+
+    JK_TRACE_ENTER(l);
+    p->ep_cache = (ajp_endpoint_t **)calloc(1, sizeof(ajp_endpoint_t *) *
+                                            p->ep_cache_sz);
+    if (!p->ep_cache) {
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+                "setting connection pool size to %u with min %u",
+                p->ep_cache_sz, p->ep_mincache_sz);
+        for (i = 0; i < p->ep_cache_sz; i++) {
+            p->ep_cache[i] = (ajp_endpoint_t *)calloc(1, sizeof(ajp_endpoint_t));
+            if (!p->ep_cache[i]) {
+                jk_log(l, JK_LOG_ERROR,
+                        "allocating endpoint slot %d (errno=%d)",
+                        i, errno);
+                JK_TRACE_EXIT(l);
+                return JK_FALSE;
+            }
+            p->ep_cache[i]->sd = JK_INVALID_SOCKET;
+            p->ep_cache[i]->reuse = JK_FALSE;
+            p->ep_cache[i]->last_access = now;
+            jk_open_pool(&(p->ep_cache[i]->pool), p->ep_cache[i]->buf,
+                         sizeof(p->ep_cache[i]->buf));
+            p->ep_cache[i]->worker = p;
+            p->ep_cache[i]->endpoint.endpoint_private = p->ep_cache[i];
+            p->ep_cache[i]->proto = proto;
+            p->ep_cache[i]->endpoint.service = ajp_service;
+            p->ep_cache[i]->endpoint.done    = ajp_done;
+        }
+
+    JK_TRACE_EXIT(l);
+    return JK_TRUE;
+}
+
+int ajp_init(jk_worker_t *pThis,
+             jk_map_t *props, jk_worker_env_t *we, jk_logger_t *l, int proto)
+{
+    int rc = JK_FALSE;
+    int cache;
+    /*
+     * start the connection cache
+     */
+    JK_TRACE_ENTER(l);
+
+    cache = jk_get_worker_def_cache_size(proto);
+
+    if (pThis && pThis->worker_private) {
+        ajp_worker_t *p = pThis->worker_private;
+        p->ep_cache_sz = jk_get_worker_cache_size(props, p->name, cache);
+        p->ep_mincache_sz = jk_get_worker_cache_size_min(props, p->name,
+                                                         (p->ep_cache_sz+1) / 2);
+        p->socket_timeout =
+            jk_get_worker_socket_timeout(props, p->name, AJP_DEF_SOCKET_TIMEOUT);
+
+        p->socket_buf =
+            jk_get_worker_socket_buffer(props, p->name, 8192);
+
+        p->keepalive =
+            jk_get_worker_socket_keepalive(props, p->name, JK_FALSE);
+
+        p->cache_timeout =
+            jk_get_worker_cache_timeout(props, p->name,
+                                        AJP_DEF_CACHE_TIMEOUT);
+
+        p->connect_timeout =
+            jk_get_worker_connect_timeout(props, p->name,
+                                          AJP_DEF_CONNECT_TIMEOUT);
+
+        p->reply_timeout =
+            jk_get_worker_reply_timeout(props, p->name,
+                                        AJP_DEF_REPLY_TIMEOUT);
+
+        p->prepost_timeout =
+            jk_get_worker_prepost_timeout(props, p->name,
+                                          AJP_DEF_PREPOST_TIMEOUT);
+
+        p->recovery_opts =
+            jk_get_worker_recovery_opts(props, p->name,
+                                        AJP_DEF_RECOVERY_OPTS);
+        p->max_packet_size =
+            jk_get_max_packet_size(props, p->name);
+
+        p->http_status_fail_num = jk_get_worker_fail_on_status(props, p->name,
+                                     &p->http_status_fail[0],
+                                     JK_MAX_HTTP_STATUS_FAILS);
+
+
+        pThis->retries =
+            jk_get_worker_retries(props, p->name,
+                                  JK_RETRIES);
+        if (pThis->retries < 1) {
+            jk_log(l, JK_LOG_INFO,
+                   "number of retries must be grater then 1. Setting to default=%d",
+                   JK_RETRIES);
+            pThis->retries = JK_RETRIES;
+        }
+
+        if (JK_IS_DEBUG_LEVEL(l)) {
+
+            jk_log(l, JK_LOG_DEBUG,
+                   "setting endpoint options:",
+                   p->keepalive);
+            jk_log(l, JK_LOG_DEBUG,
+                   "keepalive:        %d",
+                   p->keepalive);
+
+            jk_log(l, JK_LOG_DEBUG,
+                   "timeout:          %d",
+                   p->socket_timeout);
+
+            jk_log(l, JK_LOG_DEBUG,
+                   "buffer size:      %d",
+                   p->socket_buf);
+
+            jk_log(l, JK_LOG_DEBUG,
+                   "pool timeout:     %d",
+                   p->cache_timeout);
+
+            jk_log(l, JK_LOG_DEBUG,
+                   "connect timeout:  %d",
+                   p->connect_timeout);
+
+            jk_log(l, JK_LOG_DEBUG,
+                   "reply timeout:    %d",
+                   p->reply_timeout);
+
+            jk_log(l, JK_LOG_DEBUG,
+                   "prepost timeout:  %d",
+                   p->prepost_timeout);
+
+            jk_log(l, JK_LOG_DEBUG,
+                   "recovery options: %d",
+                   p->recovery_opts);
+
+            jk_log(l, JK_LOG_DEBUG,
+                   "retries:          %d",
+                    pThis->retries);
+
+            jk_log(l, JK_LOG_DEBUG,
+                   "max packet size:  %d",
+                    p->max_packet_size);
+        }
+        /*
+         *  Need to initialize secret here since we could return from inside
+         *  of the following loop
+         */
+
+        p->secret = jk_get_worker_secret(props, p->name);
+        /* Initialize cache slots */
+        JK_INIT_CS(&(p->cs), rc);
+        if (!rc) {
+            jk_log(l, JK_LOG_ERROR,
+                   "creating thread lock (errno=%d)",
+                   errno);
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+        if (!ajp_create_endpoint_cache(p, proto, l)) {
+            jk_log(l, JK_LOG_ERROR,
+                   "allocating connection pool of size %u",
+                   p->ep_cache_sz);
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+        rc = JK_TRUE;
+    }
+    else {
+        JK_LOG_NULL_PARAMS(l);
+    }
+
+    JK_TRACE_EXIT(l);
+    return rc;
+}
+
+int ajp_destroy(jk_worker_t **pThis, jk_logger_t *l, int proto)
+{
+    JK_TRACE_ENTER(l);
+
+    if (pThis && *pThis && (*pThis)->worker_private) {
+        unsigned int i;
+        ajp_worker_t *aw = (*pThis)->worker_private;
+
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "up to %u endpoints to close",
+                   aw->ep_cache_sz);
+
+        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) {
+             /* take care of removing previously allocated data */
+            if (aw->login->servlet_engine_name)
+                free(aw->login->servlet_engine_name);
+
+            free(aw->login);
+            aw->login = NULL;
+        }
+
+        free(aw);
+        JK_TRACE_EXIT(l);
+        return JK_TRUE;
+    }
+
+    JK_LOG_NULL_PARAMS(l);
+    JK_TRACE_EXIT(l);
+    return JK_FALSE;
+}
+
+int JK_METHOD ajp_done(jk_endpoint_t **e, jk_logger_t *l)
+{
+    JK_TRACE_ENTER(l);
+    if (e && *e && (*e)->endpoint_private) {
+        ajp_endpoint_t *p = (*e)->endpoint_private;
+        int rc;
+        ajp_worker_t *w = p->worker;
+
+        JK_ENTER_CS(&w->cs, rc);
+        if (rc) {
+            int i;
+            jk_sock_t sock = JK_INVALID_SOCKET;
+
+            /* If we are going to close the connection, then park the socket so
+               we can shut it down nicely rather than letting ajp_reset_endpoint kill it */
+            if (IS_VALID_SOCKET(p->sd) && !p->reuse) {
+                if (JK_IS_DEBUG_LEVEL(l))
+                    jk_log(l, JK_LOG_DEBUG,
+                    "will be shutting down socket %u for worker %s",
+                            p->sd, p->worker->name );
+                sock  = p->sd;
+                p->sd = JK_INVALID_SOCKET;
+            }
+            for(i = w->ep_cache_sz - 1; i >= 0; i--) {
+                if (w->ep_cache[i] == NULL) {
+                    w->ep_cache[i] = p;
+                    break;
+                }
+            }
+            ajp_reset_endpoint(p, l);
+            *e = NULL;
+            /* set last_access only if needed */
+            if (w->cache_timeout > 0)
+                p->last_access = time(NULL);
+            JK_LEAVE_CS(&w->cs, rc);
+
+            /* Drain and close the socket */
+            if (IS_VALID_SOCKET(sock)) {
+                if (JK_IS_DEBUG_LEVEL(l))
+                    jk_log(l, JK_LOG_DEBUG,
+                    "Shutting down held socket %u in worker %s",
+                            sock, p->worker->name);
+                jk_shutdown_socket(sock);
+            }
+            if (i >= 0) {
+                if (JK_IS_DEBUG_LEVEL(l))
+                    jk_log(l, JK_LOG_DEBUG,
+                            "recycling connection pool slot=%u for worker %s",
+                            i, p->worker->name);
+                JK_TRACE_EXIT(l);
+                return JK_TRUE;
+            }
+            /* XXX: This should never hapen because
+             * there is always free empty cache slot
+             */
+            jk_log(l, JK_LOG_ERROR,
+                    "could not find empty connection pool slot from %u for worker %s",
+                    w->ep_cache_sz, w->name);
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+
+        jk_log(l, JK_LOG_ERROR,
+               "locking thread (errno=%d)", errno);
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    JK_LOG_NULL_PARAMS(l);
+    JK_TRACE_EXIT(l);
+    return JK_FALSE;
+}
+
+int ajp_get_endpoint(jk_worker_t *pThis,
+                     jk_endpoint_t **je, jk_logger_t *l, int proto)
+{
+    JK_TRACE_ENTER(l);
+
+    if (pThis && pThis->worker_private && je) {
+        ajp_worker_t *aw = pThis->worker_private;
+        ajp_endpoint_t *ae = NULL;
+        time_t now = 0;
+        int rc;
+        /* Obtain current time only if needed */
+        if (aw->cache_timeout > 0)
+            now = time(NULL);
+        *je = NULL;
+
+        JK_ENTER_CS(&aw->cs, rc);
+        if (rc) {
+            unsigned int slot;
+            for (slot = 0; slot < aw->ep_cache_sz; slot++) {
+                if (aw->ep_cache[slot]) {
+                    ae = aw->ep_cache[slot];
+                    aw->ep_cache[slot] = NULL;
+                    break;
+                }
+            }
+            if (ae) {
+                ae->last_access = now;
+                *je = &ae->endpoint;
+                JK_LEAVE_CS(&aw->cs, rc);
+                if (JK_IS_DEBUG_LEVEL(l))
+                    jk_log(l, JK_LOG_DEBUG,
+                           "acquired connection pool slot=%u",
+                           slot);
+                JK_TRACE_EXIT(l);
+                return JK_TRUE;
+            }
+            else {
+                jk_log(l, JK_LOG_WARNING,
+                        "Unable to get the free endpoint for worker %s from %u slots",
+                        aw->name, aw->ep_cache_sz);
+            }
+            JK_LEAVE_CS(&aw->cs, rc);
+        }
+        else {
+           jk_log(l, JK_LOG_ERROR,
+                  "locking thread (errno=%d)",
+                  errno);
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+
+        }
+        jk_log(l, JK_LOG_INFO,
+               "can't find free endpoint");
+    }
+    else {
+        JK_LOG_NULL_PARAMS(l);
+    }
+
+    JK_TRACE_EXIT(l);
+    return JK_FALSE;
+}
+
+int JK_METHOD ajp_maintain(jk_worker_t *pThis, time_t now, jk_logger_t *l)
+{
+    JK_TRACE_ENTER(l);
+
+    if (pThis && pThis->worker_private) {
+        ajp_worker_t *aw = pThis->worker_private;
+        int rc;
+        /* Obtain current time only if needed */
+        if (aw->cache_timeout <= 0) {
+            /* Nothing to do. */
+            JK_TRACE_EXIT(l);
+            return JK_TRUE;
+        }
+        JK_ENTER_CS(&aw->cs, rc);
+        if (rc) {
+            unsigned int i, n = 0, cnt = 0;
+            /* Count open slots */
+            for (i = 0; i < aw->ep_cache_sz; i++) {
+                if (aw->ep_cache[i] && IS_VALID_SOCKET(aw->ep_cache[i]->sd))
+                    cnt++;
+            }
+            /* Handle worker cache and recycle timeouts */
+            for (i = 0; i < aw->ep_cache_sz; i++) {
+                /* Skip the closed sockets */
+                if (aw->ep_cache[i] && IS_VALID_SOCKET(aw->ep_cache[i]->sd)) {
+                    int elapsed = (int)difftime(now, aw->ep_cache[i]->last_access);
+                    if (elapsed > aw->cache_timeout) {
+                        time_t rt = 0;
+                        n++;
+                        if (JK_IS_DEBUG_LEVEL(l))
+                            rt = time(NULL);
+                        aw->ep_cache[i]->reuse = JK_FALSE;
+                        ajp_reset_endpoint(aw->ep_cache[i], l);
+                        if (JK_IS_DEBUG_LEVEL(l))
+                            jk_log(l, JK_LOG_DEBUG,
+                                    "cleaning pool slot=%u elapsed %d in %d",
+                                    i, elapsed, (int)(difftime(time(NULL), rt)));
+                    }
+                }
+                if ((cnt - n) <= aw->ep_mincache_sz) {
+                    if (JK_IS_DEBUG_LEVEL(l)) {
+                        jk_log(l, JK_LOG_DEBUG,
+                        "reached pool min size %u from %u cache slots",
+                        aw->ep_mincache_sz, aw->ep_cache_sz);
+                    }
+                    break;
+                }
+            }
+            if (JK_IS_DEBUG_LEVEL(l))
+                jk_log(l, JK_LOG_DEBUG,
+                        "recycled %u sockets in %d seconds from %u pool slots",
+                        n, (int)(difftime(time(NULL), now)),
+                        aw->ep_cache_sz);
+            JK_LEAVE_CS(&aw->cs, rc);
+            JK_TRACE_EXIT(l);
+            return JK_TRUE;
+        }
+        else {
+           jk_log(l, JK_LOG_ERROR,
+                  "locking thread (errno=%d)",
+                  errno);
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+
+        }
+    }
+    else {
+        JK_LOG_NULL_PARAMS(l);
+    }
+
+    JK_TRACE_EXIT(l);
+    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..b74b50c
--- /dev/null
+++ b/connectors/jk/native/common/jk_ajp_common.h
@@ -0,0 +1,373 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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_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_STORED_METHOD      (unsigned char)13
+#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
+#define SC_M_JK_STORED          (unsigned char)0xFF
+
+/*
+ * 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     (0)
+#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 AJP_DEF_SOCKET_TIMEOUT    (0)  /* No timeout */
+
+#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 */
+#define RECOVER_ABORT_IF_CLIENTERROR     0x0004 /* CLOSE THE SOCKET IN CASE OF CLIENT ERROR */
+
+#define JK_MAX_HTTP_STATUS_FAILS   32   /* Should be enough for most 400 and 500 statuses */
+
+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;
+    const char *name;
+    const char *host;
+    int port;
+    /*
+     * 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 int ep_cache_sz;
+    unsigned int ep_mincache_sz;
+    unsigned int 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 */
+    const 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
+     */
+    int socket_timeout;
+    int keepalive;
+    int socket_buf;
+    /*
+     * Handle Cache Timeouts
+     */
+    int cache_timeout;
+
+    /*
+     * Handle Connection/Reply Timeouts
+     */
+    int connect_timeout;   /* connect cping/cpong delay in ms (0 means disabled)  */
+    int reply_timeout;     /* reply timeout delay in ms (0 means disabled) */
+    int prepost_timeout;   /* before sending a request cping/cpong timeout delay in ms (0 means disabled) */
+
+    /*
+     * Recovery option
+     */
+    unsigned int recovery_opts; /* Set the recovery option */
+    
+    unsigned int max_packet_size;  /*  Maximum AJP Packet size */
+    
+    /* 
+     * HTTP status that will cause failover (0 means disabled)
+     */
+     unsigned int http_status_fail_num;
+     int http_status_fail[JK_MAX_HTTP_STATUS_FAILS];
+};
+
+
+/*
+ * 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 */
+
+    jk_sock_t sd;
+    int reuse;
+
+    jk_endpoint_t endpoint;
+
+    unsigned int left_bytes_to_send;
+
+    /* time of the last request
+       handled by this endpoint */
+    time_t last_access;
+    int last_errno;
+};
+
+/*
+ * 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);
+
+int JK_METHOD ajp_maintain(jk_worker_t *pThis, time_t now, 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..85bdfc6
--- /dev/null
+++ b/connectors/jk/native/common/jk_connect.c
@@ -0,0 +1,695 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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>
+ * @author      Mladen Turk <mturk@apache.org>
+ * @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"
+#include "apr_pools.h"
+static apr_pool_t *jk_apr_pool = NULL;
+#endif
+
+#ifdef HAVE_SYS_FILIO_H
+/* FIONREAD on Solaris et al. */
+#include <sys/filio.h>
+#endif
+
+#if defined(WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__))
+#define JK_IS_SOCKET_ERROR(x) ((x) == SOCKET_ERROR)
+#define JK_GET_SOCKET_ERRNO() errno = WSAGetLastError() - WSABASEERR
+#else
+#define JK_IS_SOCKET_ERROR(x) ((x) == -1)
+#define JK_GET_SOCKET_ERRNO() ((void)0)
+#endif /* WIN32 */
+
+/* our compiler cant deal with char* <-> const char* ... */
+#if defined(NETWARE) && !defined(__NOVELL_LIBC__)
+typedef char* SET_TYPE;
+#else
+typedef const char* SET_TYPE;
+#endif
+
+static int soblock(jk_sock_t sd)
+{
+/* BeOS uses setsockopt at present for non blocking... */
+#ifndef WIN32
+    int fd_flags;
+
+    fd_flags = fcntl(sd, F_GETFL, 0);
+#if defined(O_NONBLOCK)
+    fd_flags &= ~O_NONBLOCK;
+#elif defined(O_NDELAY)
+    fd_flags &= ~O_NDELAY;
+#elif defined(FNDELAY)
+    fd_flags &= ~FNDELAY;
+#else
+#error Please teach JK how to make sockets blocking on your platform.
+#endif
+    if (fcntl(sd, F_SETFL, fd_flags) == -1) {
+        return errno;
+    }
+#else
+    u_long on = 0;
+    if (ioctlsocket(sd, FIONBIO, &on) == SOCKET_ERROR) {
+        errno = WSAGetLastError() - WSABASEERR;
+        return errno;
+    }
+#endif /* WIN32 */
+    return 0;
+}
+
+static int sononblock(jk_sock_t sd)
+{
+#ifndef WIN32
+    int fd_flags;
+
+    fd_flags = fcntl(sd, F_GETFL, 0);
+#if defined(O_NONBLOCK)
+    fd_flags |= O_NONBLOCK;
+#elif defined(O_NDELAY)
+    fd_flags |= O_NDELAY;
+#elif defined(FNDELAY)
+    fd_flags |= FNDELAY;
+#else
+#error Please teach JK how to make sockets non-blocking on your platform.
+#endif
+    if (fcntl(sd, F_SETFL, fd_flags) == -1) {
+        return errno;
+    }
+#else
+    u_long on = 1;
+    if (ioctlsocket(sd, FIONBIO, &on) == SOCKET_ERROR) {
+        errno = WSAGetLastError() - WSABASEERR;
+        return errno;
+    }
+#endif /* WIN32 */
+    return 0;
+}
+
+#if defined (WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__))
+/* WIN32 implementation */
+static int nb_connect(jk_sock_t sock, struct sockaddr *addr, int timeout)
+{
+    int rc;
+    if (timeout <= 0)
+        return connect(sock, addr, sizeof(struct sockaddr_in));
+
+    if ((rc = sononblock(sock)))
+        return -1;
+    if (connect(sock, addr, sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
+        struct timeval tv;
+        fd_set wfdset, efdset;
+
+        if ((rc = WSAGetLastError()) != WSAEWOULDBLOCK) {
+            soblock(sock);
+            WSASetLastError(rc);
+            return -1;
+        }
+        /* wait for the connect to complete or timeout */
+        FD_ZERO(&wfdset);
+        FD_SET(sock, &wfdset);
+        FD_ZERO(&efdset);
+        FD_SET(sock, &efdset);
+
+        tv.tv_sec  = timeout;
+        tv.tv_usec = 0;
+        rc = select((int)sock + 1, NULL, &wfdset, &efdset, &tv);
+        if (rc == SOCKET_ERROR || rc == 0) {
+            rc = WSAGetLastError();
+            soblock(sock);
+            WSASetLastError(rc);
+            return -1;
+        }
+        /* Evaluate the efdset */
+        if (FD_ISSET(sock, &efdset)) {
+            /* The connect failed. */
+            int rclen = (int)sizeof(rc);
+            if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*) &rc, &rclen))
+                rc = 0;
+            soblock(sock);
+            if (rc)
+                WSASetLastError(rc);
+            return -1;
+        }
+    }
+    soblock(sock);
+    return 0;
+}
+
+#elif !defined(NETWARE)
+/* POSIX implementation */
+static int nb_connect(jk_sock_t sock, struct sockaddr *addr, int timeout)
+{
+    int rc = 0;
+
+    if (timeout > 0) {
+        if (sononblock(sock))
+            return -1;
+    }
+    do {
+        rc = connect(sock, addr, sizeof(struct sockaddr_in));
+    } while (rc == -1 && errno == EINTR);
+
+    if ((rc == -1) && (errno == EINPROGRESS || errno == EALREADY)
+                   && (timeout > 0)) {
+        fd_set wfdset;
+        struct timeval tv;
+        socklen_t rclen = (socklen_t)sizeof(rc);
+
+        FD_ZERO(&wfdset);
+        FD_SET(sock, &wfdset);
+        tv.tv_sec  = timeout;
+        tv.tv_usec = 0;
+        rc = select(sock + 1, NULL, &wfdset, NULL, &tv);
+        if (rc <= 0) {
+            /* Save errno */
+            int err = errno;
+            soblock(sock);
+            errno = err;
+            return -1;
+        }
+        rc = 0;
+#ifdef SO_ERROR
+        if (!FD_ISSET(sock, &wfdset) ||
+            (getsockopt(sock, SOL_SOCKET, SO_ERROR,
+                        (char *)&rc, &rclen) < 0) || rc) {
+            if (rc)
+                errno = rc;
+            rc = -1;
+        }
+#endif /* SO_ERROR */
+    }
+    /* Not sure we can be already connected */
+    if (rc == -1 && errno == EISCONN)
+        rc = 0;
+    soblock(sock);
+    return rc;
+}
+#else
+/* NETWARE implementation - blocking for now */
+static int nb_connect(jk_sock_t sock, struct sockaddr *addr, int timeout)
+{
+    return connect(sock, addr, sizeof(struct sockaddr_in));
+}
+#endif
+
+
+#ifdef AS400_UTF8
+
+/*
+ *  i5/OS V5R4 need EBCDIC for its runtime calls but APR/APACHE works in UTF
+ */
+in_addr_t jk_inet_addr(const char * addrstr)
+{
+    in_addr_t addr;
+    char *ptr;
+
+    ptr = (char *)malloc(strlen(addrstr) + 1);
+    jk_ascii2ebcdic((char *)addrstr, ptr);
+    addr = inet_addr(ptr);
+    free(ptr);
+
+    return(addr);
+}
+
+#endif
+
+/** resolve the host IP */
+
+int jk_resolve(const char *host, int port, struct sockaddr_in *rc)
+{
+    int x;
+    struct in_addr laddr;
+
+    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; host[x] != '\0'; 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_sockaddr_t *remote_sa, *temp_sa;
+        char *remote_ipaddr;
+
+        if (!jk_apr_pool) {
+            if (apr_pool_create(&jk_apr_pool, NULL) != APR_SUCCESS)
+                return JK_FALSE;
+        }
+        if (apr_sockaddr_info_get
+            (&remote_sa, host, APR_UNSPEC, (apr_port_t) port, 0, jk_apr_pool)
+            != 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.s_addr = jk_inet_addr(remote_ipaddr);
+
+#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 */
+#if defined(NETWARE) && !defined(__NOVELL_LIBC__)
+        struct hostent *hoste = gethostbyname((char*)host);
+#else
+        struct hostent *hoste = gethostbyname(host);
+#endif
+        if (!hoste) {
+            return JK_FALSE;
+        }
+
+        laddr = *((struct in_addr *)hoste->h_addr_list[0]);
+
+#endif /* HAVE_APR */
+    }
+    else {
+        /* If we found only digits we use inet_addr() */
+        laddr.s_addr = jk_inet_addr(host);
+    }
+    memcpy(&(rc->sin_addr), &laddr, sizeof(laddr));
+
+    return JK_TRUE;
+}
+
+/** connect to Tomcat */
+
+jk_sock_t jk_open_socket(struct sockaddr_in *addr, int keepalive,
+                         int timeout, int sock_buf, jk_logger_t *l)
+{
+    char buf[32];
+    jk_sock_t sock;
+    int set = 1;
+    int ret = 0;
+#ifdef SO_LINGER
+    struct linger li;
+#endif
+
+    JK_TRACE_ENTER(l);
+
+    sock = socket(AF_INET, SOCK_STREAM, 0);
+    if (!IS_VALID_SOCKET(sock)) {
+        JK_GET_SOCKET_ERRNO();
+        jk_log(l, JK_LOG_ERROR,
+               "socket() failed (errno=%d)", errno);
+        JK_TRACE_EXIT(l);
+        return JK_INVALID_SOCKET;
+;
+    }
+    /* Disable Nagle algorithm */
+    if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (SET_TYPE)&set,
+                   sizeof(set))) {
+        jk_log(l, JK_LOG_ERROR,
+                "failed setting TCP_NODELAY (errno=%d)", errno);
+        jk_close_socket(sock);
+        JK_TRACE_EXIT(l);
+        return JK_INVALID_SOCKET;
+    }
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+               "socket TCP_NODELAY set to On");
+    if (keepalive) {
+        set = 1;
+        if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (SET_TYPE)&set,
+                       sizeof(set))) {
+            jk_log(l, JK_LOG_ERROR,
+                   "failed setting SO_KEEPALIVE (errno=%d)", errno);
+            jk_close_socket(sock);
+            JK_TRACE_EXIT(l);
+            return JK_INVALID_SOCKET;
+        }
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "socket SO_KEEPALIVE set to On");
+    }
+
+    if (sock_buf > 0) {
+        set = sock_buf;
+        /* Set socket send buffer size */
+        if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (SET_TYPE)&set,
+                        sizeof(set))) {
+            JK_GET_SOCKET_ERRNO();
+            jk_log(l, JK_LOG_ERROR,
+                    "failed setting SO_SNDBUF (errno=%d)", errno);
+            jk_close_socket(sock);
+            JK_TRACE_EXIT(l);
+            return JK_INVALID_SOCKET;
+        }
+        set = sock_buf;
+        /* Set socket receive buffer size */
+        if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (SET_TYPE)&set,
+                                sizeof(set))) {
+            JK_GET_SOCKET_ERRNO();
+            jk_log(l, JK_LOG_ERROR,
+                    "failed setting SO_RCVBUF (errno=%d)", errno);
+            jk_close_socket(sock);
+            JK_TRACE_EXIT(l);
+            return JK_INVALID_SOCKET;
+        }
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "socket SO_SNDBUF and  SO_RCVBUF set to %d",
+                   sock_buf);
+    }
+
+    if (timeout > 0) {
+#if defined(WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__))
+        int tmout = timeout * 1000;
+        setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
+                   (const char *) &tmout, sizeof(int));
+        setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO,
+                   (const char *) &tmout, sizeof(int));
+#elif defined(SO_RCVTIMEO) && defined(USE_SO_RCVTIMEO) && defined(SO_SNDTIMEO) && defined(USE_SO_SNDTIMEO)
+        struct timeval tv;
+        tv.tv_sec  = timeout;
+        tv.tv_usec = 0;
+        setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
+                   (const void *) &tv, sizeof(tv));
+        setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO,
+                   (const void *) &tv, sizeof(tv));
+#endif
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "timeout %d set for socket=%d",
+                   timeout, sock);
+    }
+#ifdef SO_NOSIGPIPE
+    /* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when
+     * sending data to a dead peer. Possibly also existing and in use on other BSD
+     * systems?
+    */
+    set = 1;
+    if (setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, (const char *)&set,
+                   sizeof(int))) {
+        JK_GET_SOCKET_ERRNO();
+        jk_log(l, JK_LOG_ERROR,
+                "failed setting SO_NOSIGPIPE (errno=%d)", errno);
+        jk_close_socket(sock);
+        JK_TRACE_EXIT(l);
+        return JK_INVALID_SOCKET;
+    }
+#endif
+#ifdef SO_LINGER
+    /* Make hard closesocket by disabling lingering */
+    li.l_linger = li.l_onoff = 0;
+    if (setsockopt(sock, SOL_SOCKET, SO_LINGER, (SET_TYPE)&li,
+                   sizeof(li))) {
+        JK_GET_SOCKET_ERRNO();
+        jk_log(l, JK_LOG_ERROR,
+                "failed setting SO_LINGER (errno=%d)", errno);
+        jk_close_socket(sock);
+        JK_TRACE_EXIT(l);
+        return JK_INVALID_SOCKET;
+    }
+#endif
+    /* Tries to connect to Tomcat (continues trying while error is EINTR) */
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+                "trying to connect socket %d to %s", 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 = nb_connect(sock, (struct sockaddr *)addr, timeout);
+#if defined(WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__))
+    if (ret == SOCKET_ERROR) {
+        errno = WSAGetLastError() - WSABASEERR;
+    }
+#endif /* WIN32 */
+
+    /* Check if we are connected */
+    if (ret) {
+        jk_log(l, JK_LOG_INFO,
+               "connect to %s failed (errno=%d)",
+               jk_dump_hinfo(addr, buf), errno);
+        jk_close_socket(sock);
+        sock = JK_INVALID_SOCKET;
+    }
+    else {
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG, "socket %d connected to %s",
+                   sock, jk_dump_hinfo(addr, buf));
+    }
+    JK_TRACE_EXIT(l);
+    return sock;
+}
+
+/** close the socket */
+
+int jk_close_socket(jk_sock_t s)
+{
+    if (IS_VALID_SOCKET(s))
+#if defined(WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__))
+        return closesocket(s) ? -1 : 0;
+#else
+        return close(s);
+#endif
+
+    return -1;
+}
+
+#ifndef MAX_SECS_TO_LINGER
+#define MAX_SECS_TO_LINGER 16
+#endif
+#define SECONDS_TO_LINGER  1
+
+#ifndef SHUT_WR
+#ifdef SD_SEND
+#define SHUT_WR SD_SEND
+#else
+#define SHUT_WR 0x01
+#endif
+#endif
+int jk_shutdown_socket(jk_sock_t s)
+{
+    unsigned char dummy[512];
+    int nbytes;
+    int ttl = 0;
+    int rc = 0;
+#if defined(WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__))
+    int tmout = SECONDS_TO_LINGER * 1000;
+#elif defined(SO_RCVTIMEO) && defined(USE_SO_RCVTIMEO)
+    struct timeval tv;
+#endif
+    if (!IS_VALID_SOCKET(s))
+        return -1;
+
+    /* Shut down the socket for write, which will send a FIN
+     * to the peer.
+     */
+    if (shutdown(s, SHUT_WR)) {
+        return jk_close_socket(s);
+    }
+#if defined(WIN32)  || (defined(NETWARE) && defined(__NOVELL_LIBC__))
+    if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO,
+                   (const char *) &tmout, sizeof(int)) == 0)
+        rc = 1;
+#elif defined(SO_RCVTIMEO) && defined(USE_SO_RCVTIMEO)
+    tv.tv_sec  = SECONDS_TO_LINGER;
+    tv.tv_usec = 0;
+    if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO,
+                   (const void *) &tv, sizeof(tv)))
+        rc = 1;
+#endif
+    /* Read all data from the peer until we reach "end-of-file" (FIN
+     * from peer) or we've exceeded our overall timeout. If the client does
+     * not send us bytes within 16 second, close the connection.
+     */
+    while (rc) {
+        nbytes = jk_tcp_socket_recvfull(s, dummy, sizeof(dummy));
+        if (nbytes <= 0)
+            break;
+        ttl += SECONDS_TO_LINGER;
+        if (ttl > MAX_SECS_TO_LINGER)
+            break;
+    }
+    return jk_close_socket(s);
+}
+
+/** 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(jk_sock_t sd, const unsigned char *b, int len)
+{
+    int sent = 0;
+    int wr;
+
+    while (sent < len) {
+        do {
+#if defined(WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__))
+            wr = send(sd, (const char*)(b + sent),
+                      len - sent, 0);
+            if (wr == SOCKET_ERROR)
+                errno = WSAGetLastError() - WSABASEERR;
+#else
+            wr = write(sd, b + sent, len - sent);
+#endif
+        } while (wr == -1 && (errno == EINTR || errno == EAGAIN));
+
+        if (wr == -1)
+            return (errno > 0) ? -errno : errno;
+        else if (wr == 0)
+            return JK_SOCKET_EOF;
+        sent += wr;
+    }
+
+    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(jk_sock_t sd, unsigned char *b, int len)
+{
+    int rdlen = 0;
+    int rd;
+
+    while (rdlen < len) {
+        do {
+#if defined(WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__))
+            rd = recv(sd, (char *)b + rdlen,
+                      len - rdlen, 0);
+            /* Assuming SOCKET_ERROR is -1 on NETWARE too */
+            if (rd == SOCKET_ERROR)
+                errno = WSAGetLastError() - WSABASEERR;
+#else
+            rd = read(sd, (char *)b + rdlen, len - rdlen);
+#endif
+        } while (rd == -1 && (errno == EINTR || errno == EAGAIN));
+
+        if (rd == -1)
+            return (errno > 0) ? -errno : errno;
+        else if (rd == 0)
+            return JK_SOCKET_EOF;
+        rdlen += rd;
+    }
+
+    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;
+}
+
+int jk_is_socket_connected(jk_sock_t sock)
+{
+    fd_set fd;
+    struct timeval tv;
+    int rc;
+
+    FD_ZERO(&fd);
+    FD_SET(sock, &fd);
+
+    /* Initially test the socket without any blocking.
+     */
+    tv.tv_sec  = 0;
+    tv.tv_usec = 0;
+
+    do {
+        rc = select((int)sock + 1, &fd, NULL, NULL, &tv);
+#if defined(WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__))
+        errno = WSAGetLastError() - WSABASEERR;
+#endif
+        /* Wait one microsecond on next select, if EINTR */
+        tv.tv_usec = 1;
+    } while (rc == -1 && errno == EINTR);
+
+    if (rc == 0) {
+        /* If we get a timeout, then we are still connected */
+        return 1;
+    }
+    else if (rc == 1) {
+#if defined(WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__))
+        u_long nr;
+        if (ioctlsocket(sock, FIONREAD, &nr) == 0) {
+            if (WSAGetLastError() == 0)
+                errno = 0;
+            else
+                errno = WSAGetLastError() - WSABASEERR;
+            return nr == 0 ? 0 : 1;
+        }
+        errno = WSAGetLastError() - WSABASEERR;
+#else
+        int nr;
+        if (ioctl(sock, FIONREAD, (void*)&nr) == 0) {
+            return nr == 0 ? 0 : 1;
+        }
+#endif
+    }
+
+    return 0;
+}
diff --git a/connectors/jk/native/common/jk_connect.h b/connectors/jk/native/common/jk_connect.h
new file mode 100644
index 0000000..59a5618
--- /dev/null
+++ b/connectors/jk/native/common/jk_connect.h
@@ -0,0 +1,74 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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 */
+
+#define JK_SOCKET_EOF      (-2)
+#define JK_SOCKET_ERROR    (-3)
+
+int jk_resolve(const char *host, int port, struct sockaddr_in *rc);
+
+jk_sock_t jk_open_socket(struct sockaddr_in *addr, int keepalive,
+                         int timeout, int sock_buf, jk_logger_t *l);
+
+int jk_close_socket(jk_sock_t s);
+
+int jk_shutdown_socket(jk_sock_t s);
+
+int jk_tcp_socket_sendfull(jk_sock_t sd, const unsigned char *b, int len);
+
+int jk_tcp_socket_recvfull(jk_sock_t sd, unsigned char *b, int len);
+
+char *jk_dump_hinfo(struct sockaddr_in *saddr, char *buf);
+
+int jk_is_socket_connected(jk_sock_t sd);
+
+
+/***
+ * i5/OS V5R4 need ASCII<->EBCDIC translation for inet_addr() call
+ */
+#if !defined(AS400_UTF8)
+
+#define jk_inet_addr inet_addr
+
+#endif
+
+
+#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..54242b6
--- /dev/null
+++ b/connectors/jk/native/common/jk_context.c
@@ -0,0 +1,296 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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 *virt)
+{
+    if (c) {
+
+        if (virt) {
+            c->virt = jk_pool_strdup(&c->p, virt);
+
+            if (!c->virt)
+                return JK_FALSE;
+        }
+
+        return JK_TRUE;
+    }
+
+    return JK_FALSE;
+}
+
+/*
+ * Init the context info struct
+ */
+
+int context_open(jk_context_t *c, char *virt)
+{
+    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, virt);
+    }
+
+    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 *virt)
+{
+    if (c)
+        return context_open(*c =
+                            (jk_context_t *)calloc(1, sizeof(jk_context_t)),
+                            virt);
+
+    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..ee53fa7
--- /dev/null
+++ b/connectors/jk/native/common/jk_context.h
@@ -0,0 +1,138 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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 *virt;
+
+    /*
+     * 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 *virt);
+
+int context_open(jk_context_t *c, char *virt);
+
+int context_close(jk_context_t *c);
+
+int context_alloc(jk_context_t **c, char *virt);
+
+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..d3c3d05
--- /dev/null
+++ b/connectors/jk/native/common/jk_global.h
@@ -0,0 +1,366 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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
+
+#if !defined(WIN32) && !defined(AS400) && !defined(NETWARE)
+#include "portable.h"
+#endif
+
+#if defined(WIN32)
+
+/* Ignore most warnings (back down to /W3) for poorly constructed headers
+ */
+#if defined(_MSC_VER) && _MSC_VER >= 1200
+#pragma warning(push, 3)
+#endif
+
+/* disable or reduce the frequency of...
+ *   C4057: indirection to slightly different base types
+ *   C4075: slight indirection changes (unsigned short* vs short[])
+ *   C4100: unreferenced formal parameter
+ *   C4127: conditional expression is constant
+ *   C4163: '_rotl64' : not available as an intrinsic function
+ *   C4201: nonstandard extension nameless struct/unions
+ *   C4244: int to char/short - precision loss
+ *   C4514: unreferenced inline function removed
+ */
+#pragma warning(disable: 4100 4127 4163 4201 4514; once: 4057 4075 4244)
+
+/* Ignore Microsoft's interpretation of secure development
+ * and the POSIX string handling API
+ */
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+#ifndef _CRT_SECURE_NO_DEPRECATE
+#define _CRT_SECURE_NO_DEPRECATE
+#endif
+#pragma warning(disable: 4996)
+#endif
+#endif
+
+#include "jk_version.h"
+
+#ifdef HAVE_APR
+#include "apr_lib.h"
+#include "apr_strings.h"
+#endif
+
+#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"
+extern char *strdup(const char *str);
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef WIN32
+#ifndef _WINDOWS_
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#ifndef _WIN32_WINNT
+
+/* Restrict the server to a subset of Windows NT 4.0 header files by default
+ */
+#define _WIN32_WINNT 0x0400
+#endif
+#ifndef NOUSER
+#define NOUSER
+#endif
+#ifndef NOMCX
+#define NOMCX
+#endif
+#ifndef NOIME
+#define NOIME
+#endif
+#include <windows.h>
+/*
+ * Add a _very_few_ declarations missing from the restricted set of headers
+ * (If this list becomes extensive, re-enable the required headers above!)
+ * winsock headers were excluded by WIN32_LEAN_AND_MEAN, so include them now
+ */
+#include <winsock2.h>
+#include <mswsock.h>
+#include <ws2tcpip.h>
+#endif
+#include <sys/timeb.h>
+#include <process.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>
+#include <fcntl.h>
+#ifndef NETWARE
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#if !defined(_OSD_POSIX) && !defined(AS400) && !defined(CYGWIN) && !defined(HPUX11)
+#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_SHM_FILE_TAG         ("shm_file")
+#define JK_WORKER_NAME_TAG      ("worker")
+
+#define JK_WORKER_FILE_DEF  ("workers.properties")
+
+/* Urimap reload check time. Use 60 seconds by default.
+ */
+#define JK_URIMAP_DEF_RELOAD    (60)
+
+#define JK_TRUE  (1)
+#define JK_FALSE (0)
+
+#define JK_UNSET (-1)
+
+#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
+
+/* HTTP Error codes
+ */
+
+#define JK_HTTP_OK               200
+#define JK_HTTP_BAD_REQUEST      400
+#define JK_REQUEST_TOO_LARGE     413
+#define JK_HTTP_SERVER_ERROR     500
+#define JK_HTTP_NOT_IMPLEMENTED  501
+#define JK_HTTP_BAD_GATEWAY      502
+#define JK_HTTP_SERVER_BUSY      503
+#define JK_HTTP_GATEWAY_TIME_OUT 504
+
+
+/*
+ * RECO STATUS
+ */
+
+#define RECO_NONE   0x00
+#define RECO_INITED 0x01
+#define RECO_FILLED 0x02
+
+/*
+ * JK options
+ */
+
+#define JK_OPT_FWDURIMASK           0x0007
+
+#define JK_OPT_FWDURICOMPAT         0x0001
+#define JK_OPT_FWDURICOMPATUNPARSED 0x0002
+#define JK_OPT_FWDURIESCAPED        0x0003
+#define JK_OPT_FWDURIPROXY          0x0004
+
+#define JK_OPT_FWDURIDEFAULT        JK_OPT_FWDURIPROXY
+
+#define JK_OPT_FWDKEYSIZE           0x0200
+
+#define JK_OPT_FWDDIRS              0x0008
+/* Forward local instead remote address */
+#define JK_OPT_FWDLOCAL             0x0010
+#define JK_OPT_FLUSHPACKETS         0x0020
+#define JK_OPT_FLUSHEADER           0x0040
+#define JK_OPT_DISABLEREUSE         0x0080
+#define JK_OPT_FWDCERTCHAIN         0x0100
+
+/* 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 */
+
+/* on i5/OS V5R4 HTTP/APR APIs and Datas are in UTF */
+#if defined(AS400_UTF8)
+#undef USE_CHARSET_EBCDIC
+#define jk_xlate_to_ascii(b, l) /* NOOP */
+#define jk_xlate_from_ascii(b, l)       /* NOOP */
+#endif
+
+/* jk_uint32_t defines a four byte word */
+/* jk_uint64_t defines a eight byte word */
+#if defined (WIN32)
+    typedef unsigned int jk_uint32_t;
+#define JK_UINT32_T_FMT "u"
+#define JK_UINT32_T_HEX_FMT "x"
+    typedef unsigned __int64 jk_uint64_t;
+#define JK_UINT64_T_FMT "I64u"
+#define JK_UINT64_T_HEX_FMT "I64x"
+#elif defined(AS400) || defined(NETWARE)
+    typedef unsigned int jk_uint32_t;
+#define JK_UINT32_T_FMT "u"
+#define JK_UINT32_T_HEX_FMT "x"
+    typedef unsigned long long jk_uint64_t;
+#define JK_UINT64_T_FMT "llu"
+#define JK_UINT64_T_HEX_FMT "llx"
+#else
+#include "jk_types.h"
+#endif
+
+#define JK_UINT32_T_FMT_LEN  (sizeof(JK_UINT32_T_FMT) - 1)
+#define JK_UINT32_T_HEX_FMT_LEN  (sizeof(JK_UINT32_T_HEX_FMT) - 1)
+#define JK_UINT64_T_FMT_LEN  (sizeof(JK_UINT64_T_FMT) - 1)
+#define JK_UINT64_T_HEX_FMT_LEN  (sizeof(JK_UINT64_T_HEX_FMT) - 1)
+
+#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; }
+#define HAVE_VSNPRINTF
+#define HAVE_SNPRINTF
+#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
+
+/* Use ap snprintf() and vsnprintf() when needed */
+#if !defined(HAVE_APR)
+#if !defined(HAVE_SNPRINTF)
+#define snprintf ap_snprintf
+#endif
+#if !defined(HAVE_VSNPRINTF)
+#define vsnprintf ap_vsnprintf
+#endif
+#endif
+
+#if defined(WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__))
+typedef SOCKET jk_sock_t;
+#define IS_VALID_SOCKET(s) ((s) != INVALID_SOCKET)
+#define JK_INVALID_SOCKET  INVALID_SOCKET
+#else
+typedef int jk_sock_t;
+#define IS_VALID_SOCKET(s) ((s) > 0)
+#define JK_INVALID_SOCKET  (-1)
+#endif
+
+#ifdef AS400_UTF8
+#define strcasecmp(a,b) apr_strnatcasecmp(a,b)
+#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..05496b7
--- /dev/null
+++ b/connectors/jk/native/common/jk_jni_worker.c
@@ -0,0 +1,1248 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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(JNI_VERSION_1_6)
+#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 (before V5R4 ?) 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_TRACE_ENTER(l);
+
+    if (is_recoverable_error)
+        *is_recoverable_error = JK_FALSE;
+    if (!e || !e->endpoint_private || !s || !is_recoverable_error) {
+        JK_LOG_NULL_PARAMS(l);
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    p = e->endpoint_private;
+
+    if (!p->attached) {
+        /* Try to attach */
+        if (!(p->env = attach_to_jvm(p->worker, l))) {
+            jk_log(l, JK_LOG_EMERG, "Attach failed");
+            /*   Is it recoverable ?? */
+            *is_recoverable_error = JK_TRUE;
+            JK_TRACE_EXIT(l);
+            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 !!!
+     */
+    jk_log(l, JK_LOG_DEBUG, "In service, calling Tomcat...");
+
+    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, "Tomcat returned OK, done");
+        JK_TRACE_EXIT(l);
+        return JK_TRUE;
+    }
+    else {
+        jk_log(l, JK_LOG_ERROR, "Tomcat FAILED!");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+}
+
+static int JK_METHOD done(jk_endpoint_t **e, jk_logger_t *l)
+{
+    jni_endpoint_t *p;
+
+    JK_TRACE_ENTER(l);
+    if (!e || !*e || !(*e)->endpoint_private) {
+        JK_LOG_NULL_PARAMS(l);
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    p = (*e)->endpoint_private;
+
+    if (p->attached) {
+        detach_from_jvm(p->worker, l);
+    }
+
+    free(p);
+    *e = NULL;
+    JK_TRACE_EXIT(l);
+    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;
+    const char *str_config = NULL;
+    JNIEnv *env;
+
+    JK_TRACE_ENTER(l);
+
+    if (!pThis || !pThis->worker_private) {
+        JK_LOG_NULL_PARAMS(l);
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    p = pThis->worker_private;
+
+    if (p->was_verified) {
+        jk_log(l, JK_LOG_DEBUG, "been here before, done");
+        JK_TRACE_EXIT(l);
+        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, "no classpath");
+        JK_TRACE_EXIT(l);
+        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, "no jvm_dll_path");
+        JK_TRACE_EXIT(l);
+        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", 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, "can't load jvm dll");
+        /* [V] no detach needed here */
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    if (!open_jvm(p, &env, l)) {
+        jk_log(l, JK_LOG_EMERG, "can't open jvm");
+        /* [V] no detach needed here */
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    if (!get_bridge_object(p, env, l)) {
+        jk_log(l, JK_LOG_EMERG, "can't get bridge object");
+        /* [V] the detach here may segfault on 1.1 JVM... */
+        detach_from_jvm(p, l);
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    if (!get_method_ids(p, env, l)) {
+        jk_log(l, JK_LOG_EMERG, "can't get method ids");
+        /* [V] the detach here may segfault on 1.1 JVM... */
+        detach_from_jvm(p, l);
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    p->was_verified = JK_TRUE;
+    p->tmp_env = env;
+
+    JK_TRACE_EXIT(l);
+    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_TRACE_ENTER(l);
+
+    if (!pThis || !pThis->worker_private) {
+        JK_LOG_NULL_PARAMS(l);
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    p = pThis->worker_private;
+
+    if (p->was_initialized) {
+        jk_log(l, JK_LOG_DEBUG, "done (been here!)");
+        JK_TRACE_EXIT(l);
+        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, "worker not set completely");
+        JK_TRACE_EXIT(l);
+        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,
+               "calling Tomcat to intialize itself...");
+        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, "Tomcat initialized OK, done");
+            JK_TRACE_EXIT(l);
+            return JK_TRUE;
+        }
+        else {
+            jk_log(l, JK_LOG_EMERG, "could not initialize Tomcat");
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+    }
+    else {
+        jk_log(l, JK_LOG_ERROR,
+               "In init, FIXME: init didn't gen env from validate!");
+        JK_TRACE_EXIT(l);
+        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;
+
+    JK_TRACE_ENTER(l);
+
+    if (!pThis || !pThis->worker_private || !pend) {
+        JK_LOG_NULL_PARAMS(l);
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    p = (jni_endpoint_t *) calloc(1, sizeof(jni_endpoint_t));
+    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;
+
+        JK_TRACE_EXIT(l);
+        return JK_TRUE;
+    }
+    else {
+        jk_log(l, JK_LOG_ERROR,
+               "could not allocate endpoint");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+}
+
+static int JK_METHOD destroy(jk_worker_t **pThis, jk_logger_t *l)
+{
+    jni_worker_t *p;
+    JNIEnv *env;
+
+    JK_TRACE_ENTER(l);
+
+    if (!pThis || !*pThis || !(*pThis)->worker_private) {
+        JK_LOG_NULL_PARAMS(l);
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    p = (*pThis)->worker_private;
+
+    if (!p->jvm) {
+        jk_log(l, JK_LOG_DEBUG, "JVM not intantiated");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    if (!p->jk_java_bridge_object || !p->jk_shutdown_method) {
+        jk_log(l, JK_LOG_DEBUG, "Tomcat not intantiated");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    if ((env = attach_to_jvm(p, l))) {
+        jk_log(l, JK_LOG_DEBUG, "shutting down Tomcat...");
+        (*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, "destroyed");
+
+    JK_TRACE_EXIT(l);
+    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_TRACE_ENTER(l);
+
+    if (!name || !w) {
+        JK_LOG_NULL_PARAMS(l);
+        JK_TRACE_EXIT(l);
+        return 0;
+    }
+
+    if (the_singleton_jni_worker) {
+        jk_log(l, JK_LOG_DEBUG,
+               "instance already created");
+        *w = the_singleton_jni_worker;
+        JK_TRACE_EXIT(l);
+        return JK_JNI_WORKER_TYPE;
+    }
+
+    private_data = (jni_worker_t *) malloc(sizeof(jni_worker_t));
+
+    if (!private_data) {
+        jk_log(l, JK_LOG_ERROR,
+               "memory allocation error");
+        JK_TRACE_EXIT(l);
+        return 0;
+    }
+
+    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,
+               "memory allocation error");
+        jk_close_pool(&private_data->p);
+        free(private_data);
+        JK_TRACE_EXIT(l);
+        return 0;
+    }
+
+    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;
+    private_data->worker.retries = JK_RETRIES;
+
+    *w = &private_data->worker;
+    the_singleton_jni_worker = &private_data->worker;
+
+    JK_TRACE_EXIT(l);
+    return JK_JNI_WORKER_TYPE;
+}
+
+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");
+
+        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)");
+    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, "loading JVM %s", 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", 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");
+            return JK_TRUE;
+        }
+        jk_log(l, JK_LOG_EMERG,
+               "Can't resolve JNI_CreateJavaVM or JNI_GetDefaultJavaVMInitArgs");
+        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_TRACE_ENTER(l);
+
+    vm_args.version = JNI_VERSION_1_1;
+
+    if (0 != jni_get_default_java_vm_init_args(&vm_args)) {
+        jk_log(l, JK_LOG_EMERG, "can't get default vm init args");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    jk_log(l, JK_LOG_DEBUG, "got default jvm args");
+
+    if (vm_args.classpath) {
+        size_t 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,
+                   "allocation error for classpath");
+            JK_TRACE_EXIT(l);
+            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...");
+    if ((err = jni_create_java_vm(&(p->jvm), &penv, &vm_args)) != 0) {
+        jk_log(l, JK_LOG_EMERG,
+               "could not create JVM, code: %d ", err);
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    jk_log(l, JK_LOG_DEBUG, "JVM created, done");
+
+    *env = penv;
+
+    JK_TRACE_EXIT(l);
+    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_TRACE_ENTER(l);
+
+    /* [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, "can't get default vm init args");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    jk_log(l, JK_LOG_DEBUG,
+           "found version: %X, done", vm_args.version);
+
+    JK_TRACE_EXIT(l);
+    return vm_args.version;
+}
+
+static char *build_opt_str(jk_pool_t *p,
+                           char *opt_name, char *opt_value, jk_logger_t *l)
+{
+    size_t 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,
+               "allocation error for %s", 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 */
+    size_t 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,
+               "allocation error for %s", 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_TRACE_ENTER(l);
+
+    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, "setting classpath to %s",
+               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, "setting max heap to %d",
+               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, "setting start heap to %d",
+               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, "setting %s",
+                   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, "using option: %s",
+                   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,
+               "the JVM will ignore unknown options");
+        vm_args.ignoreUnrecognized = JNI_TRUE;
+    }
+    else {
+        jk_log(l, JK_LOG_DEBUG,
+               "the JVM will FAIL if it finds unknown options");
+        vm_args.ignoreUnrecognized = JNI_FALSE;
+    }
+
+    jk_log(l, JK_LOG_DEBUG, "about to create JVM...");
+
+    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.");
+
+        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 ",
+               err);
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    *env = penv;
+    jk_log(l, JK_LOG_DEBUG, "JVM created");
+
+    JK_TRACE_EXIT(l);
+    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_TRACE_ENTER(l);
+
+    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",
+               p->bridge_type);
+        JK_TRACE_EXIT(l);
+        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", btype);
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    jk_log(l, JK_LOG_DEBUG,
+           "In get_bridge_object, loaded %s bridge class", 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");
+        JK_TRACE_EXIT(l);
+        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");
+        JK_TRACE_EXIT(l);
+        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");
+        p->jk_java_bridge_class = (jclass) NULL;
+        p->jk_java_bridge_object = (jobject) NULL;
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    JK_TRACE_EXIT(l);
+    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()");
+        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()");
+        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()");
+        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_TRACE_ENTER(l);
+
+#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");
+        JK_TRACE_EXIT(l);
+        return rc;
+    }
+    jk_log(l, JK_LOG_ERROR,
+           "In attach_to_jvm, cannot attach thread to JVM.");
+    JK_TRACE_EXIT(l);
+    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)
+{
+    JK_TRACE_ENTER(l);
+    if (!p->jvm || !(*(p->jvm))) {
+        jk_log(l, JK_LOG_ERROR,
+               "cannot detach from NULL JVM.");
+    }
+
+    if (0 == (*(p->jvm))->DetachCurrentThread(p->jvm)) {
+        jk_log(l, JK_LOG_DEBUG, "detached ok");
+    }
+    else {
+        jk_log(l, JK_LOG_ERROR,
+               "cannot detach from JVM.");
+    }
+    JK_TRACE_EXIT(l);
+}
+#else
+int JK_METHOD jni_worker_factory(jk_worker_t **w,
+                                 const char *name, jk_logger_t *l)
+{
+    if (w)
+        *w = NULL;
+    return 0;
+}
+#endif /* JNI_VERSION_1_6 */
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..3c241fd
--- /dev/null
+++ b/connectors/jk/native/common/jk_jni_worker.h
@@ -0,0 +1,45 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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")
+#define JK_JNI_WORKER_TYPE  (4)
+
+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..7d5427d
--- /dev/null
+++ b/connectors/jk/native/common/jk_lb_worker.c
@@ -0,0 +1,1463 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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:      Mladen Turk <mturk@apache.org>                             *
+ * Author:      Rainer Jung <rjung@apache.org>                             *
+ * 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"
+#include "jk_ajp13.h"
+#include "jk_ajp13_worker.h"
+#include "jk_ajp14_worker.h"
+#include "jk_mt.h"
+#include "jk_shm.h"
+
+/*
+ * The load balancing code in this
+ */
+
+#define JK_WORKER_USABLE(w)   ((w)->state != JK_LB_STATE_ERROR && (w)->state != JK_LB_STATE_PROBE && (w)->state != JK_LB_STATE_BUSY && (w)->activation != JK_LB_ACTIVATION_STOPPED && (w)->activation != JK_LB_ACTIVATION_DISABLED)
+#define JK_WORKER_USABLE_STICKY(w)   ((w)->state != JK_LB_STATE_ERROR && (w)->state != JK_LB_STATE_PROBE && (w)->activation != JK_LB_ACTIVATION_STOPPED)
+
+static const char *lb_locking_type[] = {
+    JK_LB_LOCK_TEXT_OPTIMISTIC,
+    JK_LB_LOCK_TEXT_PESSIMISTIC,
+    "unknown",
+    NULL
+};
+
+static const char *lb_method_type[] = {
+    JK_LB_METHOD_TEXT_REQUESTS,
+    JK_LB_METHOD_TEXT_TRAFFIC,
+    JK_LB_METHOD_TEXT_BUSYNESS,
+    JK_LB_METHOD_TEXT_SESSIONS,
+    "unknown",
+    NULL
+};
+
+static const char *lb_state_type[] = {
+    JK_LB_STATE_TEXT_NA,
+    JK_LB_STATE_TEXT_OK,
+    JK_LB_STATE_TEXT_RECOVER,
+    JK_LB_STATE_TEXT_BUSY,
+    JK_LB_STATE_TEXT_ERROR,
+    JK_LB_STATE_TEXT_FORCE,
+    JK_LB_STATE_TEXT_PROBE,
+    "unknown",
+    NULL
+};
+
+static const char *lb_activation_type[] = {
+    JK_LB_ACTIVATION_TEXT_ACTIVE,
+    JK_LB_ACTIVATION_TEXT_DISABLED,
+    JK_LB_ACTIVATION_TEXT_STOPPED,
+    "unknown",
+    NULL
+};
+
+static const char *lb_first_log_names[] = {
+    JK_NOTE_LB_FIRST_NAME,
+    JK_NOTE_LB_FIRST_VALUE,
+    JK_NOTE_LB_FIRST_ACCESSED,
+    JK_NOTE_LB_FIRST_READ,
+    JK_NOTE_LB_FIRST_TRANSFERRED,
+    JK_NOTE_LB_FIRST_ERRORS,
+    JK_NOTE_LB_FIRST_BUSY,
+    JK_NOTE_LB_FIRST_ACTIVATION,
+    JK_NOTE_LB_FIRST_STATE,
+    NULL
+};
+
+static const char *lb_last_log_names[] = {
+    JK_NOTE_LB_LAST_NAME,
+    JK_NOTE_LB_LAST_VALUE,
+    JK_NOTE_LB_LAST_ACCESSED,
+    JK_NOTE_LB_LAST_READ,
+    JK_NOTE_LB_LAST_TRANSFERRED,
+    JK_NOTE_LB_LAST_ERRORS,
+    JK_NOTE_LB_LAST_BUSY,
+    JK_NOTE_LB_LAST_ACTIVATION,
+    JK_NOTE_LB_LAST_STATE,
+    NULL
+};
+
+struct lb_endpoint
+{
+    lb_worker_t *worker;
+
+    jk_endpoint_t endpoint;
+};
+typedef struct lb_endpoint lb_endpoint_t;
+
+
+/* Calculate the greatest common divisor of two positive integers */
+static jk_uint64_t gcd(jk_uint64_t a, jk_uint64_t b)
+{
+    jk_uint64_t r;
+    if (b > a) {
+        r = a;
+        a = b;
+        b = r;
+    }
+    while (b > 0) {
+        r = a % b;
+        a = b;
+        b = r;
+    }
+    return a;
+}
+
+/* Calculate the smallest common multiple of two positive integers */
+static jk_uint64_t scm(jk_uint64_t a, jk_uint64_t b)
+{
+    return a * b / gcd(a, b);
+}
+
+/* Return the string representation of the lb lock type */
+const char *jk_lb_get_lock(lb_worker_t *p, jk_logger_t *l)
+{
+    return lb_locking_type[p->lblock];
+}
+
+/* Return the int representation of the lb lock type */
+int jk_lb_get_lock_code(const char *v)
+{
+    if (!v)
+        return JK_LB_LOCK_DEF;
+    else if  (*v == 'o' || *v == 'O' || *v == '0')
+        return JK_LB_LOCK_OPTIMISTIC;
+    else if  (*v == 'p' || *v == 'P' || *v == '1')
+        return JK_LB_LOCK_PESSIMISTIC;
+    else
+        return JK_LB_LOCK_DEF;
+}
+
+/* Return the string representation of the lb method type */
+const char *jk_lb_get_method(lb_worker_t *p, jk_logger_t *l)
+{
+    return lb_method_type[p->lbmethod];
+}
+
+/* Return the int representation of the lb lock type */
+int jk_lb_get_method_code(const char *v)
+{
+    if (!v)
+        return JK_LB_METHOD_DEF;
+    else if  (*v == 'r' || *v == 'R' || *v == '0')
+        return JK_LB_METHOD_REQUESTS;
+    else if  (*v == 't' || *v == 'T' || *v == '1')
+        return JK_LB_METHOD_TRAFFIC;
+    else if  (*v == 'b' || *v == 'B' || *v == '2')
+        return JK_LB_METHOD_BUSYNESS;
+    else if  (*v == 's' || *v == 'S' || *v == '3')
+        return JK_LB_METHOD_SESSIONS;
+    else
+        return JK_LB_METHOD_DEF;
+}
+
+/* Return the string representation of the balance worker state */
+const char *jk_lb_get_state(worker_record_t *p, jk_logger_t *l)
+{
+    return lb_state_type[p->s->state];
+}
+
+/* Return the int representation of the lb lock type */
+int jk_lb_get_state_code(const char *v)
+{
+    if (!v)
+        return JK_LB_STATE_DEF;
+    else if  (*v == 'n' || *v == 'N' || *v == '0')
+        return JK_LB_STATE_NA;
+    else if  (*v == 'o' || *v == 'O' || *v == '1')
+        return JK_LB_STATE_OK;
+    else if  (*v == 'r' || *v == 'R' || *v == '2')
+        return JK_LB_STATE_RECOVER;
+    else if  (*v == 'b' || *v == 'B' || *v == '3')
+        return JK_LB_STATE_BUSY;
+    else if  (*v == 'e' || *v == 'E' || *v == '4')
+        return JK_LB_STATE_ERROR;
+    else if  (*v == 'f' || *v == 'F' || *v == '5')
+        return JK_LB_STATE_FORCE;
+    else if  (*v == 'p' || *v == 'P' || *v == '6')
+        return JK_LB_STATE_PROBE;
+    else
+        return JK_LB_STATE_DEF;
+}
+
+/* Return the string representation of the balance worker activation */
+const char *jk_lb_get_activation(worker_record_t *p, jk_logger_t *l)
+{
+    return lb_activation_type[p->s->activation];
+}
+
+int jk_lb_get_activation_code(const char *v)
+{
+    if (!v)
+        return JK_LB_ACTIVATION_DEF;
+    else if (*v == 'a' || *v == 'A' || *v == '0')
+        return JK_LB_ACTIVATION_ACTIVE;
+    else if (*v == 'd' || *v == 'D' || *v == '1')
+        return JK_LB_ACTIVATION_DISABLED;
+    else if (*v == 's' || *v == 'S' || *v == '2')
+        return JK_LB_ACTIVATION_STOPPED;
+    else
+        return JK_LB_ACTIVATION_DEF;
+}
+
+/* Update the load multipliers wrt. lb_factor */
+void update_mult(lb_worker_t *p, jk_logger_t *l)
+{
+    unsigned int i = 0;
+    jk_uint64_t s = 1;
+    JK_TRACE_ENTER(l);
+    for (i = 0; i < p->num_of_workers; i++) {
+        s = scm(s, p->lb_workers[i].s->lb_factor);
+    }
+    for (i = 0; i < p->num_of_workers; i++) {
+        p->lb_workers[i].s->lb_mult = s / p->lb_workers[i].s->lb_factor;
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "worker %s gets multiplicity %"
+                   JK_UINT64_T_FMT,
+                   p->lb_workers[i].s->name,
+                   p->lb_workers[i].s->lb_mult);
+    }
+    JK_TRACE_EXIT(l);
+}
+
+/* Reset all lb values.
+ */
+void reset_lb_values(lb_worker_t *p, jk_logger_t *l)
+{
+    unsigned int i = 0;
+    JK_TRACE_ENTER(l);
+    if (p->lbmethod != JK_LB_METHOD_BUSYNESS) {
+        for (i = 0; i < p->num_of_workers; i++) {
+            p->lb_workers[i].s->lb_value = 0;
+        }
+    }
+    JK_TRACE_EXIT(l);
+}
+
+/* Syncing config values from shm */
+void jk_lb_pull(lb_worker_t * p, jk_logger_t *l) {
+    JK_TRACE_ENTER(l);
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+               "syncing mem for lb '%s' from shm",
+               p->s->name);
+    p->sticky_session = p->s->sticky_session;
+    p->sticky_session_force = p->s->sticky_session_force;
+    p->recover_wait_time = p->s->recover_wait_time;
+    p->retries = p->s->retries;
+    p->lbmethod = p->s->lbmethod;
+    p->lblock = p->s->lblock;
+    p->sequence = p->s->sequence;
+    JK_TRACE_EXIT(l);
+}
+
+/* Syncing config values from shm */
+void jk_lb_push(lb_worker_t * p, jk_logger_t *l) {
+    JK_TRACE_ENTER(l);
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+               "syncing shm for lb '%s' from mem",
+               p->s->name);
+    p->s->sticky_session = p->sticky_session;
+    p->s->sticky_session_force = p->sticky_session_force;
+    p->s->recover_wait_time = p->recover_wait_time;
+    p->s->retries = p->retries;
+    p->s->lbmethod = p->lbmethod;
+    p->s->lblock = p->lblock;
+    p->s->sequence = p->sequence;
+    JK_TRACE_EXIT(l);
+}
+
+/* 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, '?')) != NULL) {
+                    *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 (strcasecmp(s->headers_names[i], "cookie") == 0) {
+
+            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 {
+                            size_t osz = strlen(result) + 1;
+                            size_t 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);
+    }
+}
+
+/* If the worker is in error state run
+ * retry on that worker. It will be marked as
+ * operational if the retry timeout is elapsed.
+ * The worker might still be unusable, but we try
+ * anyway.
+ * If the worker is in ok state and got no requests
+ * since the last global maintenance, we mark its
+ * state as not available.
+ * Return the number of workers not in error state.
+ */
+static int recover_workers(lb_worker_t *p,
+                            jk_uint64_t curmax,
+                            time_t now,
+                            jk_logger_t *l)
+{
+    unsigned int i;
+    int non_error = 0;
+    int elapsed;
+    worker_record_t *w = NULL;
+    JK_TRACE_ENTER(l);
+
+    if (p->sequence != p->s->sequence)
+        jk_lb_pull(p, l);
+    for (i = 0; i < p->num_of_workers; i++) {
+        w = &p->lb_workers[i];
+        if (w->s->state == JK_LB_STATE_ERROR) {
+            elapsed = (int)difftime(now, w->s->error_time);
+            if (elapsed <= p->s->recover_wait_time) {
+                if (JK_IS_DEBUG_LEVEL(l))
+                    jk_log(l, JK_LOG_DEBUG,
+                           "worker %s will recover in %d seconds",
+                           w->s->name, p->s->recover_wait_time - elapsed);
+            }
+            else {
+                if (JK_IS_DEBUG_LEVEL(l))
+                    jk_log(l, JK_LOG_DEBUG,
+                           "worker %s is marked for recovery",
+                           w->s->name);
+                if (p->lbmethod != JK_LB_METHOD_BUSYNESS)
+                    w->s->lb_value = curmax;
+                w->s->state = JK_LB_STATE_RECOVER;
+                non_error++;
+            }
+        }
+        else {
+            non_error++;
+            if (w->s->state == JK_LB_STATE_OK &&
+                w->s->elected == w->s->elected_snapshot)
+                w->s->state = JK_LB_STATE_NA;
+        }
+        w->s->elected_snapshot = w->s->elected;
+    }
+
+    JK_TRACE_EXIT(l);
+    return non_error;
+}
+
+static int force_recovery(lb_worker_t *p,
+                          jk_logger_t *l)
+{
+    unsigned int i;
+    int forced = 0;
+    worker_record_t *w = NULL;
+    JK_TRACE_ENTER(l);
+
+    for (i = 0; i < p->num_of_workers; i++) {
+        w = &p->lb_workers[i];
+        if (w->s->state == JK_LB_STATE_ERROR) {
+            if (JK_IS_DEBUG_LEVEL(l))
+                jk_log(l, JK_LOG_INFO,
+                       "worker %s is marked for recovery",
+                       w->s->name);
+            w->s->state = JK_LB_STATE_FORCE;
+            forced++;
+        }
+    }
+
+    JK_TRACE_EXIT(l);
+    return forced;
+}
+
+/* Divide old load values by the decay factor,
+ * such that older values get less important
+ * for the routing decisions.
+ */
+static jk_uint64_t decay_load(lb_worker_t *p,
+                              time_t exponent,
+                              jk_logger_t *l)
+{
+    unsigned int i;
+    jk_uint64_t curmax = 0;
+
+    JK_TRACE_ENTER(l);
+    if (p->lbmethod != JK_LB_METHOD_BUSYNESS) {
+        for (i = 0; i < p->num_of_workers; i++) {
+            p->lb_workers[i].s->lb_value >>= exponent;
+            if (p->lb_workers[i].s->lb_value > curmax) {
+                curmax = p->lb_workers[i].s->lb_value;
+            }
+        }
+    }
+    JK_TRACE_EXIT(l);
+    return curmax;
+}
+
+static int JK_METHOD maintain_workers(jk_worker_t *p, time_t now, jk_logger_t *l)
+{
+    unsigned int i = 0;
+    jk_uint64_t curmax = 0;
+    long delta;
+
+    JK_TRACE_ENTER(l);
+    if (p && p->worker_private) {
+        lb_worker_t *lb = (lb_worker_t *)p->worker_private;
+
+        for (i = 0; i < lb->num_of_workers; i++) {
+            if (lb->lb_workers[i].w->maintain) {
+                lb->lb_workers[i].w->maintain(lb->lb_workers[i].w, now, l);
+            }
+        }
+
+        jk_shm_lock();
+
+        /* Now we check for global maintenance (once for all processes).
+         * Checking workers for recovery and applying decay to the
+         * load values should not be done by each process individually.
+         * Therefore we globally sync and we use a global timestamp.
+         * Since it's possible that we come here a few milliseconds
+         * before the interval has passed, we allow a little tolerance.
+         */
+        delta = (long)difftime(now, lb->s->last_maintain_time) + JK_LB_MAINTAIN_TOLERANCE;
+        if (delta >= lb->maintain_time) {
+            lb->s->last_maintain_time = now;
+            if (JK_IS_DEBUG_LEVEL(l))
+                jk_log(l, JK_LOG_DEBUG,
+                       "decay with 2^%d",
+                       JK_LB_DECAY_MULT * delta / lb->maintain_time);
+            curmax = decay_load(lb, JK_LB_DECAY_MULT * delta / lb->maintain_time, l);
+            if (!recover_workers(lb, curmax, now, l)) {
+                force_recovery(lb, l);
+            }
+        }
+
+        jk_shm_unlock();
+
+    }
+    else {
+        JK_LOG_NULL_PARAMS(l);
+    }
+
+    JK_TRACE_EXIT(l);
+    return JK_TRUE;
+}
+
+static worker_record_t *find_by_session(lb_worker_t *p,
+                                        const char *name,
+                                        jk_logger_t *l)
+{
+
+    worker_record_t *rc = NULL;
+    unsigned int i;
+
+    for (i = 0; i < p->num_of_workers; i++) {
+        if (strcmp(p->lb_workers[i].s->route, name) == 0) {
+            rc = &p->lb_workers[i];
+            rc->r = &(rc->s->route[0]);
+            break;
+        }
+    }
+    return rc;
+}
+
+static worker_record_t *find_best_bydomain(lb_worker_t *p,
+                                           const char *domain,
+                                           jk_logger_t *l)
+{
+    unsigned int i;
+    int d = 0;
+    jk_uint64_t curmin = 0;
+
+    worker_record_t *candidate = NULL;
+
+    /* First try to see if we have available candidate */
+    for (i = 0; i < p->num_of_workers; i++) {
+        /* Skip all workers that are not member of domain */
+        if (strlen(p->lb_workers[i].s->domain) == 0 ||
+            strcmp(p->lb_workers[i].s->domain, domain))
+            continue;
+        /* Take into calculation only the workers that are
+         * not in error state, stopped, disabled or busy.
+         */
+        if (JK_WORKER_USABLE(p->lb_workers[i].s)) {
+            if (!candidate || p->lb_workers[i].s->distance < d ||
+                (p->lb_workers[i].s->lb_value < curmin &&
+                p->lb_workers[i].s->distance == d)) {
+                candidate = &p->lb_workers[i];
+                curmin = p->lb_workers[i].s->lb_value;
+                d = p->lb_workers[i].s->distance;
+            }
+        }
+    }
+
+    if (candidate) {
+        candidate->r = &(candidate->s->domain[0]);
+    }
+
+    return candidate;
+}
+
+
+static worker_record_t *find_best_byvalue(lb_worker_t *p,
+                                          jk_logger_t *l)
+{
+    static unsigned int next_offset = 0;
+    unsigned int i;
+    unsigned int j;
+    unsigned int offset;
+    int d = 0;
+    jk_uint64_t curmin = 0;
+
+    /* find the least busy worker */
+    worker_record_t *candidate = NULL;
+
+    offset = next_offset;
+
+    /* First try to see if we have available candidate */
+    for (j = offset; j < p->num_of_workers + offset; j++) {
+        i = j % p->num_of_workers;
+
+        /* Take into calculation only the workers that are
+         * not in error state, stopped, disabled or busy.
+         */
+        if (JK_WORKER_USABLE(p->lb_workers[i].s)) {
+            if (!candidate || p->lb_workers[i].s->distance < d ||
+                (p->lb_workers[i].s->lb_value < curmin &&
+                p->lb_workers[i].s->distance == d)) {
+                candidate = &p->lb_workers[i];
+                curmin = p->lb_workers[i].s->lb_value;
+                d = p->lb_workers[i].s->distance;
+                next_offset = i + 1;
+            }
+        }
+    }
+    return candidate;
+}
+
+static worker_record_t *find_bysession_route(lb_worker_t *p,
+                                             const char *name,
+                                             jk_logger_t *l)
+{
+    int uses_domain  = 0;
+    worker_record_t *candidate = NULL;
+
+    candidate = find_by_session(p, name, l);
+    if (!candidate) {
+        uses_domain = 1;
+        candidate = find_best_bydomain(p, name, l);
+    }
+    if (candidate) {
+        if (!JK_WORKER_USABLE_STICKY(candidate->s)) {
+            /* We have a worker that is error state or stopped.
+             * If it has a redirection set use that redirection worker.
+             * This enables to safely remove the member from the
+             * balancer. Of course you will need a some kind of
+             * session replication between those two remote.
+             */
+            if (p->sticky_session_force)
+                candidate = NULL;
+            else if (*candidate->s->redirect)
+                candidate = find_by_session(p, candidate->s->redirect, l);
+            else if (*candidate->s->domain && !uses_domain) {
+                uses_domain = 1;
+                candidate = find_best_bydomain(p, candidate->s->domain, l);
+            }
+            if (candidate && !JK_WORKER_USABLE_STICKY(candidate->s))
+                candidate = NULL;
+        }
+    }
+    return candidate;
+}
+
+static worker_record_t *find_failover_worker(lb_worker_t * p,
+                                             jk_logger_t *l)
+{
+    worker_record_t *rc = NULL;
+    unsigned int i;
+    const char *redirect = NULL;
+
+    for (i = 0; i < p->num_of_workers; i++) {
+        if (strlen(p->lb_workers[i].s->redirect)) {
+            redirect = &(p->lb_workers[i].s->redirect[0]);
+            break;
+        }
+    }
+    if (redirect)
+        rc = find_bysession_route(p, redirect, l);
+    return rc;
+}
+
+static worker_record_t *find_best_worker(lb_worker_t * p,
+                                         jk_logger_t *l)
+{
+    worker_record_t *rc = NULL;
+
+    rc = find_best_byvalue(p, l);
+    /* By default use worker route as session route */
+    if (rc)
+        rc->r = &(rc->s->route[0]);
+    else
+        rc = find_failover_worker(p, l);
+    return rc;
+}
+
+static worker_record_t *get_most_suitable_worker(lb_worker_t * p,
+                                                 char *sessionid,
+                                                 jk_ws_service_t *s,
+                                                 jk_logger_t *l)
+{
+    worker_record_t *rc = NULL;
+    int r;
+
+    JK_TRACE_ENTER(l);
+    if (p->num_of_workers == 1) {
+        /* No need to find the best worker
+         * if there is a single one
+         */
+        if (JK_WORKER_USABLE_STICKY(p->lb_workers[0].s)) {
+            if (p->lb_workers[0].s->activation != JK_LB_ACTIVATION_DISABLED) {
+                p->lb_workers[0].r = &(p->lb_workers[0].s->route[0]);
+                JK_TRACE_EXIT(l);
+                return &p->lb_workers[0];
+            }
+        }
+        else {
+            JK_TRACE_EXIT(l);
+            return NULL;
+        }
+    }
+    if (p->lblock == JK_LB_LOCK_PESSIMISTIC)
+        r = jk_shm_lock();
+    else {
+        JK_ENTER_CS(&(p->cs), r);
+    }
+    if (!r) {
+       jk_log(l, JK_LOG_ERROR,
+              "locking failed (errno=%d)",
+              errno);
+        JK_TRACE_EXIT(l);
+        return NULL;
+    }
+    if (sessionid) {
+        char *session = sessionid;
+        while (sessionid) {
+            char *next = strchr(sessionid, ';');
+            char *session_route = NULL;
+            if (next)
+               *next++ = '\0';
+            if (JK_IS_DEBUG_LEVEL(l))
+                jk_log(l, JK_LOG_DEBUG,
+                       "searching worker for partial sessionid %s",
+                       sessionid);
+            session_route = strchr(sessionid, '.');
+            if (session_route) {
+                ++session_route;
+
+                if (JK_IS_DEBUG_LEVEL(l))
+                    jk_log(l, JK_LOG_DEBUG,
+                           "searching worker for session route %s",
+                           session_route);
+
+                /* We have a session route. Whow! */
+                rc = find_bysession_route(p, session_route, l);
+                if (rc) {
+                    if (p->lblock == JK_LB_LOCK_PESSIMISTIC)
+                        jk_shm_unlock();
+                    else {
+                        JK_LEAVE_CS(&(p->cs), r);
+                    }
+                    if (JK_IS_DEBUG_LEVEL(l))
+                        jk_log(l, JK_LOG_DEBUG,
+                               "found worker %s (%s) for route %s and partial sessionid %s",
+                               rc->s->name, rc->s->route, session_route, sessionid);
+                        JK_TRACE_EXIT(l);
+                    return rc;
+                }
+            }
+            /* Try next partial sessionid if present */
+            sessionid = next;
+            rc = NULL;
+        }
+        if (!rc && p->sticky_session_force) {
+            if (p->lblock == JK_LB_LOCK_PESSIMISTIC)
+                jk_shm_unlock();
+            else {
+                JK_LEAVE_CS(&(p->cs), r);
+            }
+            jk_log(l, JK_LOG_INFO,
+                   "all workers are in error state for session %s",
+                   session);
+            JK_TRACE_EXIT(l);
+            return NULL;
+        }
+    }
+    rc = find_best_worker(p, l);
+    if (p->lblock == JK_LB_LOCK_PESSIMISTIC)
+        jk_shm_unlock();
+    else {
+        JK_LEAVE_CS(&(p->cs), r);
+    }
+    if (rc && JK_IS_DEBUG_LEVEL(l)) {
+        jk_log(l, JK_LOG_DEBUG,
+               "found best worker %s (%s) using method '%s'",
+               rc->s->name, rc->s->route, jk_lb_get_method(p, l));
+    }
+    JK_TRACE_EXIT(l);
+    return rc;
+}
+
+static void lb_add_log_items(jk_ws_service_t *s,
+                             const char *const *log_names,
+                             worker_record_t *w,
+                             jk_logger_t *l)
+{
+    const char **log_values = jk_pool_alloc(s->pool, sizeof(char *) * JK_LB_NOTES_COUNT);
+    char *buf = jk_pool_alloc(s->pool, sizeof(char *) * JK_LB_NOTES_COUNT * JK_LB_UINT64_STR_SZ);
+    if (log_values && buf) {
+        /* JK_NOTE_LB_FIRST/LAST_NAME */
+        log_values[0] = w->s->name;
+        snprintf(buf, JK_LB_UINT64_STR_SZ, "%" JK_UINT64_T_FMT, w->s->lb_value);
+        /* JK_NOTE_LB_FIRST/LAST_VALUE */
+        log_values[1] = buf;
+        buf += JK_LB_UINT64_STR_SZ;
+        snprintf(buf, JK_LB_UINT64_STR_SZ, "%" JK_UINT64_T_FMT, w->s->elected);
+        /* JK_NOTE_LB_FIRST/LAST_ACCESSED */
+        log_values[2] = buf;
+        buf += JK_LB_UINT64_STR_SZ;
+        snprintf(buf, JK_LB_UINT64_STR_SZ, "%" JK_UINT64_T_FMT, w->s->readed);
+        /* JK_NOTE_LB_FIRST/LAST_READ */
+        log_values[3] = buf;
+        buf += JK_LB_UINT64_STR_SZ;
+        snprintf(buf, JK_LB_UINT64_STR_SZ, "%" JK_UINT64_T_FMT, w->s->transferred);
+        /* JK_NOTE_LB_FIRST/LAST_TRANSFERRED */
+        log_values[4] = buf;
+        buf += JK_LB_UINT64_STR_SZ;
+        snprintf(buf, JK_LB_UINT64_STR_SZ, "%" JK_UINT32_T_FMT, w->s->errors);
+        /* JK_NOTE_LB_FIRST/LAST_ERRORS */
+        log_values[5] = buf;
+        buf += JK_LB_UINT64_STR_SZ;
+        snprintf(buf, JK_LB_UINT64_STR_SZ, "%d", w->s->busy);
+        /* JK_NOTE_LB_FIRST/LAST_BUSY */
+        log_values[6] = buf;
+        /* JK_NOTE_LB_FIRST/LAST_ACTIVATION */
+        log_values[7] = jk_lb_get_activation(w, l);
+        /* JK_NOTE_LB_FIRST/LAST_STATE */
+        log_values[8] = jk_lb_get_state(w, l);
+        s->add_log_items(s, log_names, log_values, JK_LB_NOTES_COUNT);
+    }
+}
+
+static int JK_METHOD service(jk_endpoint_t *e,
+                             jk_ws_service_t *s,
+                             jk_logger_t *l, int *is_error)
+{
+    lb_endpoint_t *p;
+    int attempt = 1;
+    worker_record_t *prec = NULL;
+    int num_of_workers;
+    int first = 1;
+    int was_forced = 0;
+    int rc = -1;
+    char *sessionid = NULL;
+
+    JK_TRACE_ENTER(l);
+
+    if (is_error)
+        *is_error = JK_HTTP_SERVER_ERROR;
+    if (!e || !e->endpoint_private || !s || !is_error) {
+        JK_LOG_NULL_PARAMS(l);
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    p = e->endpoint_private;
+    num_of_workers = p->worker->num_of_workers;
+
+    /* Set returned error to OK */
+    *is_error = JK_HTTP_OK;
+
+    /* set the recovery post, for LB mode */
+    s->reco_buf = jk_b_new(s->pool);
+    jk_b_set_buffer_size(s->reco_buf, p->worker->max_packet_size);
+    jk_b_reset(s->reco_buf);
+    s->reco_status = RECO_INITED;
+
+    jk_shm_lock();
+    if (p->worker->sequence != p->worker->s->sequence)
+        jk_lb_pull(p->worker, l);
+    jk_shm_unlock();
+
+    if (p->worker->sticky_session) {
+        /* Use sessionid only if sticky_session is
+         * defined for this load balancer
+         */
+        sessionid = get_sessionid(s);
+    }
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+               "service sticky_session=%d id='%s'",
+               p->worker->sticky_session, sessionid ? sessionid : "empty");
+
+    while (attempt <= num_of_workers && rc == -1) {
+        worker_record_t *rec =
+            get_most_suitable_worker(p->worker, sessionid, s, l);
+        /* Do not reuse previous worker, because
+         * that worker already failed.
+         */
+        if (rec) {
+            int r;
+            int is_service_error = JK_HTTP_OK;
+            jk_endpoint_t *end = NULL;
+            int retry = 0;
+            int retry_wait = JK_LB_MIN_RETRY_WAIT;
+            s->route = rec->r;
+            prec = rec;
+
+            if (JK_IS_DEBUG_LEVEL(l))
+                jk_log(l, JK_LOG_DEBUG,
+                       "service worker=%s route=%s",
+                       rec->s->name, s->route);
+
+            if (p->worker->lblock == JK_LB_LOCK_PESSIMISTIC)
+                jk_shm_lock();
+            if (rec->s->state == JK_LB_STATE_RECOVER)
+                rec->s->state = JK_LB_STATE_PROBE;
+            if (p->worker->lblock == JK_LB_LOCK_PESSIMISTIC)
+                jk_shm_unlock();
+                       
+            while ((!(r=rec->w->get_endpoint(rec->w, &end, l)) || !end) && (retry < p->worker->s->retries)) {
+                retry++;
+                retry_wait *=2;
+
+                if (p->worker->lblock == JK_LB_LOCK_PESSIMISTIC)
+                    jk_shm_lock();
+                if (retry_wait > JK_LB_MAX_RETRY_WAIT)
+                    retry_wait = JK_LB_MAX_RETRY_WAIT;
+                if (p->worker->lblock == JK_LB_LOCK_PESSIMISTIC)
+                    jk_shm_unlock();
+
+                if (JK_IS_DEBUG_LEVEL(l))
+                    jk_log(l, JK_LOG_DEBUG,
+                           "could not get free endpoint for worker"
+                           " (retry %d, sleeping for %d ms)",
+                           retry, retry_wait);
+                jk_sleep(retry_wait);
+            }
+            if (!r || !end) {
+                /* If we can not get the endpoint
+                 * mark the worker as busy rather then
+                 * as in error if the retry number is
+                 * greater then the number of retries.
+                 */
+                if (p->worker->lblock == JK_LB_LOCK_PESSIMISTIC)
+                    jk_shm_lock();
+                if (rec->s->state != JK_LB_STATE_ERROR)
+                    rec->s->state = JK_LB_STATE_BUSY;
+                if (p->worker->lblock == JK_LB_LOCK_PESSIMISTIC)
+                    jk_shm_unlock();
+                jk_log(l, JK_LOG_INFO,
+                       "could not get free endpoint for worker %s (%d retries)",
+                       rec->s->name, retry);
+            }
+            else {
+                int service_stat = -1;
+                size_t rd = 0;
+                size_t wr = 0;
+                /* Reset endpoint read and write sizes for
+                 * this request.
+                 */
+                end->rd = end->wr = 0;
+                if (p->worker->lblock == JK_LB_LOCK_PESSIMISTIC)
+                    jk_shm_lock();
+
+                rec->s->elected++;
+                /* Increment the number of workers serving request */
+                p->worker->s->busy++;
+                if (p->worker->s->busy > p->worker->s->max_busy)
+                    p->worker->s->max_busy = p->worker->s->busy;
+                rec->s->busy++;
+                if (rec->s->busy > rec->s->max_busy)
+                    rec->s->max_busy = rec->s->busy;
+                if ( (p->worker->lbmethod == JK_LB_METHOD_REQUESTS) ||
+                     (p->worker->lbmethod == JK_LB_METHOD_BUSYNESS) ||
+                     (p->worker->lbmethod == JK_LB_METHOD_SESSIONS &&
+                      !sessionid) )
+                    rec->s->lb_value += rec->s->lb_mult;
+                if (p->worker->lblock == JK_LB_LOCK_PESSIMISTIC)
+                    jk_shm_unlock();
+
+                service_stat = end->service(end, s, l, &is_service_error);
+                rd = end->rd;
+                wr = end->wr;
+                end->done(&end, l);
+
+                if (p->worker->lblock == JK_LB_LOCK_PESSIMISTIC)
+                    jk_shm_lock();
+
+                /* Update partial reads and writes if any */
+                rec->s->readed += rd;
+                rec->s->transferred += wr;
+                if (p->worker->lbmethod == JK_LB_METHOD_TRAFFIC) {
+                    rec->s->lb_value += (rd+wr)*rec->s->lb_mult;
+                }
+                else if (p->worker->lbmethod == JK_LB_METHOD_BUSYNESS) {
+                    if (rec->s->lb_value >= rec->s->lb_mult) {
+                        rec->s->lb_value -= rec->s->lb_mult;
+                    }
+                    else {
+                        rec->s->lb_value = 0;
+                        if (JK_IS_DEBUG_LEVEL(l)) {
+                            jk_log(l, JK_LOG_DEBUG,
+                                   "worker %s has load value to low (%"
+                                   JK_UINT64_T_FMT
+                                   " < %"
+                                   JK_UINT64_T_FMT
+                                   ") ",
+                                   "- correcting to 0",
+                                   rec->s->name,
+                                   rec->s->lb_value,
+                                   rec->s->lb_mult);
+                        }
+                    }
+                }
+
+                /* When returning the endpoint mark the worker as not busy.
+                 * We have at least one endpoint free
+                 */
+                if (rec->s->state == JK_LB_STATE_BUSY)
+                    rec->s->state = JK_LB_STATE_OK;
+                /* Decrement the busy worker count.
+                 * Check if the busy was reset to zero by graceful
+                 * restart of the server.
+                 */
+                if (rec->s->busy)
+                    rec->s->busy--;
+                if (p->worker->s->busy)
+                    p->worker->s->busy--;
+                if (service_stat == JK_TRUE) {
+                    rec->s->state = JK_LB_STATE_OK;
+                    rec->s->error_time = 0;
+                    rc = JK_TRUE;
+                }
+                else if (service_stat == JK_CLIENT_ERROR) {
+                    /*
+                    * Client error !!!
+                    * Since this is bad request do not fail over.
+                    */
+                    rec->s->client_errors++;
+                    rec->s->state = JK_LB_STATE_OK;
+                    rec->s->error_time = 0;
+                    jk_log(l, JK_LOG_INFO,
+                           "unrecoverable error %d, request failed."
+                           " Client failed in the middle of request,"
+                           " we can't recover to another instance.",
+                           is_service_error);
+                    *is_error = is_service_error;
+                    rc = JK_CLIENT_ERROR;
+                }
+                else {
+                    /*
+                    * Service failed !!!
+                    * Time for fault tolerance (if possible)...
+                    */
+
+                    rec->s->errors++;
+                    rec->s->state = JK_LB_STATE_ERROR;
+                    rec->s->error_time = time(NULL);
+                    if (is_service_error != JK_HTTP_SERVER_BUSY) {
+                        /*
+                        * Error is not recoverable - break with an error.
+                        */
+                        jk_log(l, JK_LOG_ERROR,
+                            "unrecoverable error %d, request failed."
+                            " Tomcat failed in the middle of request,"
+                            " we can't recover to another instance.",
+                            is_service_error);
+                        *is_error = is_service_error;
+                        rc = JK_FALSE;
+                    }
+                    else
+                        jk_log(l, JK_LOG_INFO,
+                               "service failed, worker %s is in error state",
+                               rec->s->name);
+                }
+                if (p->worker->lblock == JK_LB_LOCK_PESSIMISTIC)
+                    jk_shm_unlock();
+            }
+            if ( rc == -1 ) {
+                /*
+                 * Error is recoverable by submitting the request to
+                 * another worker... Lets try to do that.
+                 */
+                if (JK_IS_DEBUG_LEVEL(l))
+                    jk_log(l, JK_LOG_DEBUG,
+                           "recoverable error... will try to recover on other worker");
+            }
+            if (first == 1 && s->add_log_items) {
+                first = 0;
+                lb_add_log_items(s, lb_first_log_names, prec, l);
+            }
+        }
+        else {
+            /* NULL record, no more workers left ... */
+            if (!was_forced) {
+                int nf;
+                /* Force recovery only once.
+                 * If it still fails, Tomcat is still disconnected.
+                 */
+                jk_shm_lock();
+                nf = force_recovery(p->worker, l);
+                jk_shm_unlock();
+                was_forced = 1;
+                if (nf) {
+                    /* We have forced recovery.
+                     * Reset the service loop and go again
+                     */
+                    prec = NULL;
+                    rc   = -1;
+                    jk_log(l, JK_LOG_INFO,
+                           "Forcing recovery once for %d workers", nf);
+                    continue;
+                }
+                else {
+                    /* No workers in error state.
+                     * Somebody set them all to disabled?
+                     */
+                    jk_log(l, JK_LOG_ERROR,
+                           "All tomcat instances failed, no more workers left for recovery");
+                    *is_error = JK_HTTP_SERVER_BUSY;
+                    rc = JK_FALSE;
+                }
+            }
+            else {
+                jk_log(l, JK_LOG_ERROR,
+                       "All tomcat instances failed, no more workers left");
+                *is_error = JK_HTTP_SERVER_BUSY;
+                rc = JK_FALSE;
+            }
+        }
+        attempt++;
+    }
+    if ( rc == -1 ) {
+        jk_log(l, JK_LOG_INFO,
+               "All tomcat instances are busy or in error state");
+        /* Set error to Timeout */
+        *is_error = JK_HTTP_SERVER_BUSY;
+        rc = JK_FALSE;
+    }
+    if (prec && s->add_log_items) {
+        lb_add_log_items(s, lb_last_log_names, prec, l);
+    }
+
+    JK_TRACE_EXIT(l);
+    return rc;
+}
+
+static int JK_METHOD done(jk_endpoint_t **e, jk_logger_t *l)
+{
+    JK_TRACE_ENTER(l);
+
+    if (e && *e && (*e)->endpoint_private) {
+        lb_endpoint_t *p = (*e)->endpoint_private;
+
+        free(p);
+        *e = NULL;
+        JK_TRACE_EXIT(l);
+        return JK_TRUE;
+    }
+
+    JK_LOG_NULL_PARAMS(l);
+    JK_TRACE_EXIT(l);
+    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_TRACE_ENTER(l);
+
+    if (pThis && pThis->worker_private) {
+        lb_worker_t *p = pThis->worker_private;
+        char **worker_names;
+        unsigned int num_of_workers;
+        const char *secret;
+
+        p->sticky_session = jk_get_is_sticky_session(props, p->s->name);
+        p->sticky_session_force = jk_get_is_sticky_session_force(props, p->s->name);
+        secret = jk_get_worker_secret(props, p->s->name);
+
+        if (jk_get_lb_worker_list(props,
+                                  p->s->name,
+                                  &worker_names,
+                                  &num_of_workers) && num_of_workers) {
+            unsigned int i = 0;
+            unsigned int j = 0;
+            p->max_packet_size = DEF_BUFFER_SZ;
+            p->lb_workers = jk_pool_alloc(&p->p,
+                                          num_of_workers *
+                                          sizeof(worker_record_t));
+            if (!p->lb_workers) {
+                JK_TRACE_EXIT(l);
+                return JK_FALSE;
+            }
+
+            for (i = 0; i < num_of_workers; i++) {
+                p->lb_workers[i].s = jk_shm_alloc_worker(&p->p);
+                if (p->lb_workers[i].s == NULL) {
+                    jk_log(l, JK_LOG_ERROR,
+                           "allocating worker record from shared memory");
+                    JK_TRACE_EXIT(l);
+                    return JK_FALSE;
+                }
+            }
+
+            for (i = 0; i < num_of_workers; i++) {
+                const char *s;
+                unsigned int ms;
+                strncpy(p->lb_workers[i].s->name, worker_names[i],
+                        JK_SHM_STR_SIZ);
+                p->lb_workers[i].s->lb_factor =
+                    jk_get_lb_factor(props, worker_names[i]);
+                if (p->lb_workers[i].s->lb_factor < 1) {
+                    p->lb_workers[i].s->lb_factor = 1;
+                }
+                /* Calculate the maximum packet size from all workers
+                 * for the recovery buffer.
+                 */
+                ms = jk_get_max_packet_size(props, worker_names[i]);
+                if (ms > p->max_packet_size)
+                    p->max_packet_size = ms;
+                p->lb_workers[i].s->distance =
+                    jk_get_distance(props, worker_names[i]);
+                if ((s = jk_get_worker_route(props, worker_names[i], NULL)))
+                    strncpy(p->lb_workers[i].s->route, s, JK_SHM_STR_SIZ);
+                else
+                    strncpy(p->lb_workers[i].s->route, worker_names[i], JK_SHM_STR_SIZ);
+                if ((s = jk_get_worker_domain(props, worker_names[i], NULL)))
+                    strncpy(p->lb_workers[i].s->domain, s, JK_SHM_STR_SIZ);
+                if ((s = jk_get_worker_redirect(props, worker_names[i], NULL)))
+                    strncpy(p->lb_workers[i].s->redirect, s, JK_SHM_STR_SIZ);
+
+                p->lb_workers[i].s->lb_value = 0;
+                p->lb_workers[i].s->state = JK_LB_STATE_NA;
+                p->lb_workers[i].s->error_time = 0;
+                p->lb_workers[i].s->activation =
+                    jk_get_worker_activation(props, worker_names[i]);
+                if (!wc_create_worker(p->lb_workers[i].s->name, 0,
+                                      props,
+                                      &(p->lb_workers[i].w),
+                                      we, l) || !p->lb_workers[i].w) {
+                    break;
+                }
+                if (secret && (p->lb_workers[i].w->type == JK_AJP13_WORKER_TYPE ||
+                    p->lb_workers[i].w->type == JK_AJP14_WORKER_TYPE)) {
+                    ajp_worker_t *aw = (ajp_worker_t *)p->lb_workers[i].w->worker_private;
+                    if (!aw->secret)
+                        aw->secret = secret;
+                }
+            }
+
+            if (i != num_of_workers) {
+                jk_log(l, JK_LOG_ERROR,
+                       "Failed creating worker %s",
+                       p->lb_workers[i].s->name);
+                close_workers(p, i, l);
+            }
+            else {
+                /* Update domain names if route contains period '.' */
+                for (i = 0; i < num_of_workers; i++) {
+                    if (!p->lb_workers[i].s->domain[0]) {
+                        char * id_domain = strchr(p->lb_workers[i].s->route, '.');
+                        if (id_domain) {
+                            *id_domain = '\0';
+                            strcpy(p->lb_workers[i].s->domain, p->lb_workers[i].s->route);
+                            *id_domain = '.';
+                        }
+                    }
+                    if (JK_IS_DEBUG_LEVEL(l)) {
+                        jk_log(l, JK_LOG_DEBUG,
+                               "Balanced worker %i has name %s and route %s in domain %s",
+                               i,
+                               p->lb_workers[i].s->name,
+                               p->lb_workers[i].s->route,
+                               p->lb_workers[i].s->domain);
+                    }
+                }
+                p->num_of_workers = num_of_workers;
+                update_mult(p, l);
+                for (i = 0; i < num_of_workers; i++) {
+                    for (j = 0; j < i; j++) {
+                        if (strcmp(p->lb_workers[i].s->route, p->lb_workers[j].s->route) == 0) {
+                            jk_log(l, JK_LOG_ERROR,
+                                   "Balanced workers number %i (%s) and %i (%s) share the same route %s - aborting configuration!",
+                                   i,
+                                   p->lb_workers[i].s->name,
+                                   j,
+                                   p->lb_workers[j].s->name,
+                                   p->lb_workers[i].s->route);
+                            JK_TRACE_EXIT(l);
+                            return JK_FALSE;
+                        }
+                    }
+                }
+                JK_TRACE_EXIT(l);
+                return JK_TRUE;
+            }
+        }
+    }
+
+    JK_LOG_NULL_PARAMS(l);
+    JK_TRACE_EXIT(l);
+    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)
+{
+    int i;
+
+    lb_worker_t *p = (lb_worker_t *)pThis->worker_private;
+    JK_TRACE_ENTER(log);
+
+    pThis->retries = jk_get_worker_retries(props, p->s->name,
+                                           JK_RETRIES);
+    p->retries = pThis->retries;
+    p->recover_wait_time = jk_get_worker_recover_timeout(props, p->s->name,
+                                                            WAIT_BEFORE_RECOVER);
+    if (p->recover_wait_time < 1)
+        p->recover_wait_time = 1;
+    p->maintain_time = jk_get_worker_maintain_time(props);
+    if(p->maintain_time < 0)
+        p->maintain_time = 0;
+    p->s->last_maintain_time = time(NULL);
+
+    p->lbmethod = jk_get_lb_method(props, p->s->name);
+    p->lblock   = jk_get_lb_lock(props, p->s->name);
+
+    JK_INIT_CS(&(p->cs), i);
+    if (i == JK_FALSE) {
+        jk_log(log, JK_LOG_ERROR,
+               "creating thread lock (errno=%d)",
+               errno);
+        JK_TRACE_EXIT(log);
+        return JK_FALSE;
+    }
+
+    jk_shm_lock();
+    p->sequence++;
+    jk_lb_push(p, log);
+    jk_shm_unlock();
+
+    JK_TRACE_EXIT(log);
+    return JK_TRUE;
+}
+
+static int JK_METHOD get_endpoint(jk_worker_t *pThis,
+                                  jk_endpoint_t **pend, jk_logger_t *l)
+{
+    JK_TRACE_ENTER(l);
+
+    if (pThis && pThis->worker_private && pend) {
+        lb_endpoint_t *p = (lb_endpoint_t *) malloc(sizeof(lb_endpoint_t));
+        p->worker = pThis->worker_private;
+        p->endpoint.endpoint_private = p;
+        p->endpoint.service = service;
+        p->endpoint.done = done;
+        *pend = &p->endpoint;
+
+        JK_TRACE_EXIT(l);
+        return JK_TRUE;
+    }
+    else {
+        JK_LOG_NULL_PARAMS(l);
+    }
+
+    JK_TRACE_EXIT(l);
+    return JK_FALSE;
+}
+
+static int JK_METHOD destroy(jk_worker_t **pThis, jk_logger_t *l)
+{
+    JK_TRACE_ENTER(l);
+
+    if (pThis && *pThis && (*pThis)->worker_private) {
+        unsigned int i;
+        lb_worker_t *private_data = (*pThis)->worker_private;
+
+        close_workers(private_data, private_data->num_of_workers, l);
+        JK_DELETE_CS(&(private_data->cs), i);
+        jk_close_pool(&private_data->p);
+        free(private_data);
+
+        JK_TRACE_EXIT(l);
+        return JK_TRUE;
+    }
+
+    JK_LOG_NULL_PARAMS(l);
+    JK_TRACE_EXIT(l);
+    return JK_FALSE;
+}
+
+int JK_METHOD lb_worker_factory(jk_worker_t **w,
+                                const char *name, jk_logger_t *l)
+{
+    JK_TRACE_ENTER(l);
+
+    if (NULL != name && NULL != w) {
+        lb_worker_t *private_data =
+            (lb_worker_t *) calloc(1, sizeof(lb_worker_t));
+
+
+        jk_open_pool(&private_data->p,
+                        private_data->buf,
+                        sizeof(jk_pool_atom_t) * TINY_POOL_SIZE);
+
+        private_data->s = jk_shm_alloc_worker(&private_data->p);
+        if (!private_data->s) {
+            free(private_data);
+            JK_TRACE_EXIT(l);
+            return 0;
+        }
+        strncpy(private_data->s->name, name, JK_SHM_STR_SIZ);
+        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;
+        private_data->worker.maintain = maintain_workers;
+        private_data->worker.retries = JK_RETRIES;
+        private_data->recover_wait_time = WAIT_BEFORE_RECOVER;
+        private_data->sequence = 0;
+        *w = &private_data->worker;
+        JK_TRACE_EXIT(l);
+        return JK_LB_WORKER_TYPE;
+    }
+    else {
+        JK_LOG_NULL_PARAMS(l);
+    }
+
+    JK_TRACE_EXIT(l);
+    return 0;
+}
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..a4e6bb1
--- /dev/null
+++ b/connectors/jk/native/common/jk_lb_worker.h
@@ -0,0 +1,176 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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>                           *
+ * Author:      Rainer Jung <rjung@apache.org>                             *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#ifndef JK_LB_WORKER_H
+#define JK_LB_WORKER_H
+
+#include "jk_logger.h"
+#include "jk_service.h"
+#include "jk_mt.h"
+#include "jk_shm.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif                          /* __cplusplus */
+
+#define JK_LB_WORKER_NAME     ("lb")
+#define JK_LB_WORKER_TYPE     (5)
+#define JK_LB_DEF_DOMAIN_NAME ("unknown")
+
+#define JK_LB_METHOD_REQUESTS          (0)
+#define JK_LB_METHOD_TRAFFIC           (1)
+#define JK_LB_METHOD_BUSYNESS          (2)
+#define JK_LB_METHOD_SESSIONS          (3)
+#define JK_LB_METHOD_DEF               (JK_LB_METHOD_REQUESTS)
+#define JK_LB_METHOD_MAX               (JK_LB_METHOD_SESSIONS)
+#define JK_LB_METHOD_TEXT_REQUESTS     ("Request")
+#define JK_LB_METHOD_TEXT_TRAFFIC      ("Traffic")
+#define JK_LB_METHOD_TEXT_BUSYNESS     ("Busyness")
+#define JK_LB_METHOD_TEXT_SESSIONS     ("Sessions")
+#define JK_LB_METHOD_TEXT_DEF          (JK_LB_METHOD_TEXT_REQUESTS)
+#define JK_LB_LOCK_OPTIMISTIC          (0)
+#define JK_LB_LOCK_PESSIMISTIC         (1)
+#define JK_LB_LOCK_DEF                 (JK_LB_LOCK_OPTIMISTIC)
+#define JK_LB_LOCK_MAX                 (JK_LB_LOCK_PESSIMISTIC)
+#define JK_LB_LOCK_TEXT_OPTIMISTIC     ("Optimistic")
+#define JK_LB_LOCK_TEXT_PESSIMISTIC    ("Pessimistic")
+#define JK_LB_LOCK_TEXT_DEF            (JK_LB_LOCK_TEXT_OPTIMISTIC)
+#define JK_LB_STATE_NA                 (0)
+#define JK_LB_STATE_OK                 (1)
+#define JK_LB_STATE_RECOVER            (2)
+#define JK_LB_STATE_BUSY               (3)
+#define JK_LB_STATE_ERROR              (4)
+#define JK_LB_STATE_FORCE              (5)
+#define JK_LB_STATE_PROBE              (6)
+#define JK_LB_STATE_DEF                (JK_LB_STATE_NA)
+#define JK_LB_STATE_TEXT_NA            ("N/A")
+#define JK_LB_STATE_TEXT_OK            ("OK")
+#define JK_LB_STATE_TEXT_RECOVER       ("REC")
+#define JK_LB_STATE_TEXT_BUSY          ("BSY")
+#define JK_LB_STATE_TEXT_ERROR         ("ERR")
+#define JK_LB_STATE_TEXT_FORCE         ("FRC")
+#define JK_LB_STATE_TEXT_PROBE         ("PRB")
+#define JK_LB_STATE_TEXT_MAX           (JK_LB_STATE_PROBE)
+#define JK_LB_STATE_TEXT_DEF           (JK_LB_STATE_TEXT_NA)
+#define JK_LB_ACTIVATION_ACTIVE        (0)
+#define JK_LB_ACTIVATION_DISABLED      (1)
+#define JK_LB_ACTIVATION_STOPPED       (2)
+#define JK_LB_ACTIVATION_DEF           (JK_LB_ACTIVATION_ACTIVE)
+#define JK_LB_ACTIVATION_MAX           (JK_LB_ACTIVATION_STOPPED)
+#define JK_LB_ACTIVATION_TEXT_ACTIVE   ("ACT")
+#define JK_LB_ACTIVATION_TEXT_DISABLED ("DIS")
+#define JK_LB_ACTIVATION_TEXT_STOPPED  ("STP")
+#define JK_LB_ACTIVATION_TEXT_DEF      (JK_LB_ACTIVATION_TEXT_ACTIVE)
+
+#define JK_LB_UINT64_STR_SZ          (21)
+#define JK_LB_NOTES_COUNT            (9)
+#define JK_NOTE_LB_FIRST_NAME        ("JK_LB_FIRST_NAME")
+#define JK_NOTE_LB_FIRST_VALUE       ("JK_LB_FIRST_VALUE")
+#define JK_NOTE_LB_FIRST_ACCESSED    ("JK_LB_FIRST_ACCESSED")
+#define JK_NOTE_LB_FIRST_READ        ("JK_LB_FIRST_READ")
+#define JK_NOTE_LB_FIRST_TRANSFERRED ("JK_LB_FIRST_TRANSFERRED")
+#define JK_NOTE_LB_FIRST_ERRORS      ("JK_LB_FIRST_ERRORS")
+#define JK_NOTE_LB_FIRST_BUSY        ("JK_LB_FIRST_BUSY")
+#define JK_NOTE_LB_FIRST_ACTIVATION  ("JK_LB_FIRST_ACTIVATION")
+#define JK_NOTE_LB_FIRST_STATE       ("JK_LB_FIRST_STATE")
+#define JK_NOTE_LB_LAST_NAME         ("JK_LB_LAST_NAME")
+#define JK_NOTE_LB_LAST_VALUE        ("JK_LB_LAST_VALUE")
+#define JK_NOTE_LB_LAST_ACCESSED     ("JK_LB_LAST_ACCESSED")
+#define JK_NOTE_LB_LAST_READ         ("JK_LB_LAST_READ")
+#define JK_NOTE_LB_LAST_TRANSFERRED  ("JK_LB_LAST_TRANSFERRED")
+#define JK_NOTE_LB_LAST_ERRORS       ("JK_LB_LAST_ERRORS")
+#define JK_NOTE_LB_LAST_BUSY         ("JK_LB_LAST_BUSY")
+#define JK_NOTE_LB_LAST_ACTIVATION   ("JK_LB_LAST_ACTIVATION")
+#define JK_NOTE_LB_LAST_STATE        ("JK_LB_LAST_STATE")
+
+/* Minimal time in ms to wait between get_endpoint retries for balanced workers */
+#define JK_LB_MIN_RETRY_WAIT  (25)
+/* Maximal time in ms to wait between get_endpoint retries for balanced workers */
+#define JK_LB_MAX_RETRY_WAIT  (100)
+/* Time to wait before retry. */
+#define WAIT_BEFORE_RECOVER   (60)
+/* We accept doing global maintenance if we are */
+/* JK_LB_MAINTAIN_TOLERANCE seconds early. */
+#define JK_LB_MAINTAIN_TOLERANCE (2)
+/* We divide load values by 2^x during global maintenance. */
+/* The exponent x is JK_LB_DECAY_MULT*#MAINT_INTV_SINCE_LAST_MAINT */
+#define JK_LB_DECAY_MULT         (1)
+
+struct worker_record
+{
+    jk_worker_t      *w;
+    /* Shared memory worker data */
+    jk_shm_worker_t  *s;
+    /* Current route. Can be name or domain */
+    const char       *r;
+};
+typedef struct worker_record worker_record_t;
+
+struct lb_worker
+{
+    worker_record_t *lb_workers;
+    unsigned int num_of_workers;
+    char         name[JK_SHM_STR_SIZ+1];
+    int          sticky_session;
+    int          sticky_session_force;
+    int          recover_wait_time;
+    int          retries;
+    int          lbmethod;
+    int          lblock;
+    int          maintain_time;
+    unsigned int max_packet_size;
+    unsigned int sequence;
+
+    jk_pool_t p;
+    jk_pool_atom_t buf[TINY_POOL_SIZE];
+
+    jk_worker_t worker;
+    JK_CRIT_SEC cs;
+
+    /* Shared memory worker data */
+    jk_shm_worker_t  *s;
+};
+typedef struct lb_worker lb_worker_t;
+
+int JK_METHOD lb_worker_factory(jk_worker_t **w,
+                                const char *name, jk_logger_t *l);
+
+const char *jk_lb_get_lock(lb_worker_t *p, jk_logger_t *l);
+int jk_lb_get_lock_code(const char *v);
+const char *jk_lb_get_method(lb_worker_t *p, jk_logger_t *l);
+int jk_lb_get_method_code(const char *v);
+const char *jk_lb_get_state(worker_record_t *p, jk_logger_t *l);
+int jk_lb_get_state_code(const char *v);
+const char *jk_lb_get_activation(worker_record_t *p, jk_logger_t *l);
+int jk_lb_get_activation_code(const char *v);
+void reset_lb_values(lb_worker_t *p, jk_logger_t *l);
+void jk_lb_pull(lb_worker_t * p, jk_logger_t *l);
+void jk_lb_push(lb_worker_t * p, jk_logger_t *l);
+void update_mult(lb_worker_t * p, 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..56ca683
--- /dev/null
+++ b/connectors/jk/native/common/jk_logger.h
@@ -0,0 +1,128 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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
+
+typedef struct jk_logger jk_logger_t;
+struct jk_logger
+{
+    void *logger_private;
+    int level;
+    const char *log_fmt;
+
+    int (JK_METHOD * log) (jk_logger_t *l, int level, const char *what);
+
+};
+
+typedef struct jk_file_logger_t jk_file_logger_t;
+struct jk_file_logger_t
+{
+    FILE *logfile;
+    /* For Apache 2 APR piped logging */
+    void *jklogfp;
+    /* For Apache 1.3 piped logging */
+    int log_fd;
+};
+
+/* Level like Java tracing, but available only
+   at compile time on DEBUG preproc define.
+ */
+#define JK_LOG_TRACE_LEVEL   0
+#define JK_LOG_DEBUG_LEVEL   1
+#define JK_LOG_INFO_LEVEL    2
+#define JK_LOG_WARNING_LEVEL 3
+#define JK_LOG_ERROR_LEVEL   4
+#define JK_LOG_EMERG_LEVEL   5
+#define JK_LOG_REQUEST_LEVEL 6
+#define JK_LOG_DEF_LEVEL     JK_LOG_INFO_LEVEL
+
+#define JK_LOG_TRACE_VERB   "trace"
+#define JK_LOG_DEBUG_VERB   "debug"
+#define JK_LOG_INFO_VERB    "info"
+#define JK_LOG_WARN_VERB    "warn"
+#define JK_LOG_ERROR_VERB   "error"
+#define JK_LOG_EMERG_VERB   "emerg"
+#define JK_LOG_DEF_VERB     JK_LOG_INFO_VERB
+
+#if defined(__GNUC__) || (defined(_MSC_VER) && (_MSC_VER > 1200))
+#define JK_LOG_TRACE   __FILE__,__LINE__,__FUNCTION__,JK_LOG_TRACE_LEVEL
+#define JK_LOG_DEBUG   __FILE__,__LINE__,__FUNCTION__,JK_LOG_DEBUG_LEVEL
+#define JK_LOG_ERROR   __FILE__,__LINE__,__FUNCTION__,JK_LOG_ERROR_LEVEL
+#define JK_LOG_EMERG   __FILE__,__LINE__,__FUNCTION__,JK_LOG_EMERG_LEVEL
+#define JK_LOG_INFO    __FILE__,__LINE__,__FUNCTION__,JK_LOG_INFO_LEVEL
+#define JK_LOG_WARNING __FILE__,__LINE__,__FUNCTION__,JK_LOG_WARNING_LEVEL
+#else
+#define JK_LOG_TRACE   __FILE__,__LINE__,NULL,JK_LOG_TRACE_LEVEL
+#define JK_LOG_DEBUG   __FILE__,__LINE__,NULL,JK_LOG_DEBUG_LEVEL
+#define JK_LOG_ERROR   __FILE__,__LINE__,NULL,JK_LOG_ERROR_LEVEL
+#define JK_LOG_EMERG   __FILE__,__LINE__,NULL,JK_LOG_EMERG_LEVEL
+#define JK_LOG_INFO    __FILE__,__LINE__,NULL,JK_LOG_INFO_LEVEL
+#define JK_LOG_WARNING __FILE__,__LINE__,NULL,JK_LOG_WARNING_LEVEL
+#endif
+
+#define JK_LOG_REQUEST __FILE__,0,NULL,JK_LOG_REQUEST_LEVEL
+
+#if defined(JK_PRODUCTION)
+/* TODO: all DEBUG messages should be compiled out
+ * when this define is in place.
+ */
+#define JK_IS_PRODUCTION    1
+#define JK_TRACE_ENTER(l)
+#define JK_TRACE_EXIT(l)
+#else
+#define JK_IS_PRODUCTION    0
+#define JK_TRACE_ENTER(l)                               \
+    do {                                                \
+        if ((l) && (l)->level == JK_LOG_TRACE_LEVEL) {  \
+            jk_log((l), JK_LOG_TRACE, "enter");         \
+    } } while (0)
+
+#define JK_TRACE_EXIT(l)                                \
+    do {                                                \
+        if ((l) && (l)->level == JK_LOG_TRACE_LEVEL) {  \
+            jk_log((l), JK_LOG_TRACE, "exit");          \
+    } } while (0)
+
+#endif  /* JK_PRODUCTION */
+
+#define JK_LOG_NULL_PARAMS(l) jk_log((l), JK_LOG_ERROR, "NULL parameters")
+
+/* Debug level macro
+ * It is more efficient to check the level prior
+ * calling function that will not execute anyhow because of level
+ */
+#define JK_IS_DEBUG_LEVEL(l)  ((l) && (l)->level <  JK_LOG_INFO_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..8ca37fe
--- /dev/null
+++ b/connectors/jk/native/common/jk_map.c
@@ -0,0 +1,883 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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>                           *
+ * Author:      Mladen Turk <mturk@apache.org>                             *
+ * Version:     $Revision$                                          *
+ ***************************************************************************/
+#if defined(AS400) && !defined(AS400_UTF8)
+#include "apr_xlate.h"
+#endif
+
+#include "jk_global.h"
+#include "jk_pool.h"
+#include "jk_util.h"
+#include "jk_map.h"
+
+#define CAPACITY_INC_SIZE   (50)
+#define LENGTH_OF_LINE      (8192)
+#define JK_MAP_RECURSION    (20)
+#define JK_MAP_REFERENCE    (".reference")
+#define JK_MAP_REFERENCE_SZ (strlen(JK_MAP_REFERENCE))
+
+/* Compute the "checksum" for a key, consisting of the first
+ * 4 bytes, 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 strcmp
+ */
+#define COMPUTE_KEY_CHECKSUM(key, checksum)    \
+{                                              \
+    const char *k = (key);                     \
+    unsigned int c = (unsigned int)*k;         \
+    (checksum) = c;                            \
+    (checksum) <<= 8;                          \
+    if (c) {                                   \
+        c = (unsigned int)*++k;                \
+        checksum |= c;                         \
+    }                                          \
+    (checksum) <<= 8;                          \
+    if (c) {                                   \
+        c = (unsigned int)*++k;                \
+        checksum |= c;                         \
+    }                                          \
+    (checksum) <<= 8;                          \
+    if (c) {                                   \
+        c = (unsigned int)*++k;                \
+        checksum |= c;                         \
+    }                                          \
+}
+
+struct jk_map
+{
+    jk_pool_t p;
+    jk_pool_atom_t buf[SMALL_POOL_SIZE];
+
+    const char **names;
+    const void **values;
+    unsigned int *keys;
+
+    unsigned int capacity;
+    unsigned int size;
+};
+
+static void trim_prp_comment(char *prp);
+static size_t trim(char *s);
+static int map_realloc(jk_map_t *m);
+
+int jk_map_alloc(jk_map_t **m)
+{
+    if (m) {
+        return jk_map_open(*m = (jk_map_t *)malloc(sizeof(jk_map_t)));
+    }
+
+    return JK_FALSE;
+}
+
+int jk_map_free(jk_map_t **m)
+{
+    int rc = JK_FALSE;
+
+    if (m && *m) {
+        jk_map_close(*m);
+        free(*m);
+        *m = NULL;
+    }
+
+    return rc;
+}
+
+int jk_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->keys  = NULL;
+        m->names = NULL;
+        m->values = NULL;
+        rc = JK_TRUE;
+    }
+
+    return rc;
+}
+
+int jk_map_close(jk_map_t *m)
+{
+    int rc = JK_FALSE;
+
+    if (m) {
+        jk_close_pool(&m->p);
+        rc = JK_TRUE;
+    }
+
+    return rc;
+}
+
+void *jk_map_get(jk_map_t *m, const char *name, const void *def)
+{
+    const void *rc = (void *)def;
+
+    if (m && name) {
+        unsigned int i;
+        unsigned int key;
+        COMPUTE_KEY_CHECKSUM(name, key)
+        for (i = 0; i < m->size; i++) {
+            if (m->keys[i] == key && strcmp(m->names[i], name) == 0) {
+                rc = m->values[i];
+                break;
+            }
+        }
+    }
+
+    return (void *)rc;          /* DIRTY */
+}
+
+int jk_map_get_id(jk_map_t *m, const char *name)
+{
+    int rc = -1;
+    if (m && name) {
+        unsigned int i;
+        unsigned int key;
+        COMPUTE_KEY_CHECKSUM(name, key)
+        for (i = 0; i < m->size; i++) {
+            if (m->keys[i] == key && strcmp(m->names[i], name) == 0) {
+                rc = i;
+                break;
+            }
+        }
+    }
+
+    return rc;
+}
+
+const char *jk_map_get_string(jk_map_t *m, const char *name, const char *def)
+{
+    const char *rc = def;
+
+    if (m && name) {
+        unsigned int i;
+        unsigned int key;
+        COMPUTE_KEY_CHECKSUM(name, key)
+        for (i = 0; i < m->size; i++) {
+            if (m->keys[i] == key && strcmp(m->names[i], name) == 0) {
+                rc = m->values[i];
+                break;
+            }
+        }
+    }
+
+    return rc;
+}
+
+
+int jk_map_get_int(jk_map_t *m, const char *name, int def)
+{
+    char buf[100];
+    const char *rc;
+    size_t len;
+    int int_res;
+    int multit = 1;
+
+    sprintf(buf, "%d", def);
+    rc = jk_map_get_string(m, name, buf);
+
+    len = strlen(rc);
+    if (len) {
+        char *lastchar = &buf[0] + len - 1;
+        strcpy(buf, rc);
+        if ('m' == *lastchar || 'M' == *lastchar) {
+            *lastchar = '\0';
+            multit = 1024 * 1024;
+        }
+        else if ('k' == *lastchar || 'K' == *lastchar) {
+            *lastchar = '\0';
+            multit = 1024;
+        }
+        int_res = atoi(buf);
+    }
+    else
+        int_res = def;
+
+    return int_res * multit;
+}
+
+double jk_map_get_double(jk_map_t *m, const char *name, double def)
+{
+    char buf[100];
+    const char *rc;
+
+    sprintf(buf, "%f", def);
+    rc = jk_map_get_string(m, name, buf);
+
+    return atof(rc);
+}
+
+int jk_map_get_bool(jk_map_t *m, const char *name, int def)
+{
+    char buf[100];
+    const char *rc;
+
+    sprintf(buf, "%d", def);
+    rc = jk_map_get_string(m, name, buf);
+
+    return jk_get_bool_code(rc, def);
+}
+
+char **jk_map_get_string_list(jk_map_t *m,
+                              const char *name,
+                              unsigned int *list_len, const char *def)
+{
+    const char *l = jk_map_get_string(m, name, def);
+    char **ar = NULL;
+
+#ifdef _REENTRANT
+    char *lasts;
+#endif
+
+    *list_len = 0;
+
+    if (l) {
+        unsigned capacity = 0;
+        unsigned idex = 0;
+        char *p;
+        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 "*"
+         */
+#ifdef _REENTRANT
+        for (p = strtok_r(v, " \t,", &lasts);
+             p; p = strtok_r(NULL, " \t,", &lasts))
+#else
+        for (p = strtok(v, " \t,"); p; p = 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, p);
+            idex++;
+        }
+
+        *list_len = idex;
+    }
+
+    return ar;
+}
+
+int jk_map_get_int_list(jk_map_t *m,
+                        const char *name,
+                        int *list,
+                        unsigned int list_len,
+                        const char *def)
+{
+    const char *l = jk_map_get_string(m, name, def);
+
+#ifdef _REENTRANT
+    char *lasts;
+#endif
+
+    if (!list_len)
+        return 0;
+
+    if (l) {
+        unsigned int capacity = list_len;
+        unsigned int index = 0;
+        char *p;
+        char *v = jk_pool_strdup(&m->p, l);
+
+        if (!v) {
+            return 0;
+        }
+
+        /*
+         * GS, in addition to VG's patch, we now need to
+         * strtok also by a "*"
+         */
+#ifdef _REENTRANT
+        for (p = strtok_r(v, " \t,", &lasts);
+             p; p = strtok_r(NULL, " \t,", &lasts))
+#else
+        for (p = strtok(v, " \t,"); p; p = strtok(NULL, " \t,"))
+#endif
+
+        {
+            if (index < capacity) {
+                list[index] = atoi(p);
+                index++;
+            }
+            else
+                break;
+        }
+        return index;
+    }
+    return 0;
+}
+
+int jk_map_add(jk_map_t *m, const char *name, const void *value)
+{
+    int rc = JK_FALSE;
+
+    if (m && name) {
+        unsigned int key;
+        COMPUTE_KEY_CHECKSUM(name, key)
+        map_realloc(m);
+
+        if (m->size < m->capacity) {
+            m->values[m->size] = value;
+            m->names[m->size] = jk_pool_strdup(&m->p, name);
+            m->keys[m->size] = key;
+            m->size++;
+            rc = JK_TRUE;
+        }
+    }
+
+    return rc;
+}
+
+int jk_map_put(jk_map_t *m, const char *name, const void *value, void **old)
+{
+    int rc = JK_FALSE;
+
+    if (m && name) {
+        unsigned int i;
+        unsigned int key;
+        COMPUTE_KEY_CHECKSUM(name, key)
+        for (i = 0; i < m->size; i++) {
+            if (m->keys[i] == key && strcmp(m->names[i], name) == 0) {
+                break;
+            }
+        }
+
+        if (i < m->size) {
+            if (old)
+                *old = (void *)m->values[i];        /* DIRTY */
+            m->values[i] = value;
+            rc = JK_TRUE;
+        }
+        else {
+            rc = jk_map_add(m, name, value);
+        }
+    }
+
+    return rc;
+}
+
+int jk_map_read_property(jk_map_t *m, const char *str, int allow_duplicates, jk_logger_t *l)
+{
+    int rc = JK_TRUE;
+    char buf[LENGTH_OF_LINE + 1];
+    char *prp = &buf[0];
+
+    if (strlen(str) > LENGTH_OF_LINE) {
+        jk_log(l, JK_LOG_WARNING,
+               "Line to long (%d > %d), ignoring entry",
+               strlen(str), LENGTH_OF_LINE);
+        return JK_FALSE;
+    }
+
+    strcpy(prp, str);
+    if (trim(prp)) {
+        char *v = strchr(prp, '=');
+        if (v) {
+            *v = '\0';
+            v++;
+            trim(prp);
+            trim(v);
+            if (strlen(v) && strlen(prp)) {
+                const char *oldv;
+                int off = (int)strlen(prp) - (int)JK_MAP_REFERENCE_SZ;
+                /* check the worker properties */
+                if (off <= 0 || strncmp(&prp[off], JK_MAP_REFERENCE, JK_MAP_REFERENCE_SZ) ) {
+                    if (!jk_is_valid_property(prp)) {
+                        jk_log(l, JK_LOG_ERROR,
+                               "The attribute '%s' is not supported - please check"
+                               " the documentation for the supported attributes.",
+                               prp);
+                        return JK_FALSE;
+                    }
+                    if (jk_is_deprecated_property(prp)) {
+                        jk_log(l, JK_LOG_WARNING,
+                               "The attribute '%s' is deprecated - please check"
+                               " the documentation for the correct replacement.",
+                               prp);
+                    }
+                }
+                oldv = jk_map_get_string(m, prp, NULL);
+                v = jk_map_replace_properties(m, v);
+                if (oldv) {
+                    if (allow_duplicates && jk_is_unique_property(prp) == JK_FALSE) {
+                        char *tmpv = jk_pool_alloc(&m->p,
+                                           strlen(v) + strlen(oldv) + 3);
+                        if (tmpv) {
+                            char sep = '*';
+                            if (jk_is_path_property(prp))
+                                sep = PATH_SEPERATOR;
+                            else if (jk_is_cmd_line_property(prp))
+                                sep = ' ';
+                            else if (jk_is_list_property(prp))
+                                sep = ',';
+                            sprintf(tmpv, "%s%c%s", oldv, sep, v);
+                        }
+                        v = tmpv;
+                    }
+                    else {
+                        jk_log(l, JK_LOG_WARNING,
+                               "Duplicate key '%s' detected - previous value '%s'"
+                               " will be overwritten with '%s'.",
+                               prp, oldv ? oldv : "(null)", v ? v : "(null)");
+                        v = jk_pool_strdup(&m->p, v);
+                    }
+                }
+                else {
+                    v = jk_pool_strdup(&m->p, v);
+                }
+                if (v) {
+                    jk_map_put(m, prp, v, NULL);
+                }
+                else {
+                    JK_LOG_NULL_PARAMS(l);
+                    rc = JK_FALSE;
+                }
+            }
+        }
+    }
+    return rc;
+}
+
+
+int jk_map_read_properties(jk_map_t *m, const char *f, time_t *modified, int allow_duplicates, jk_logger_t *l)
+{
+    int rc = JK_FALSE;
+
+    if (m && f) {
+        struct stat statbuf;
+        FILE *fp;
+        if (jk_stat(f, &statbuf) == -1)
+            return JK_FALSE;
+#if defined(AS400) && !defined(AS400_UTF8)
+        fp = fopen(f, "r, o_ccsid=0");
+#else
+        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 (*prp) {
+                    if ((rc = jk_map_read_property(m, prp, allow_duplicates, l)) == JK_FALSE)
+                        break;
+                }
+            }
+            fclose(fp);
+            if (modified)
+                *modified = statbuf.st_mtime;
+        }
+    }
+
+    return rc;
+}
+
+
+int jk_map_size(jk_map_t *m)
+{
+    if (m) {
+        return m->size;
+    }
+
+    return -1;
+}
+
+const char *jk_map_name_at(jk_map_t *m, int idex)
+{
+    if (m && idex >= 0) {
+        return m->names[idex];  /* DIRTY */
+    }
+
+    return NULL;
+}
+
+void *jk_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)
+{
+#if defined(AS400) && !defined(AS400_UTF8)
+    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 size_t trim(char *s)
+{
+    size_t i;
+
+    /* check for empty strings */
+    if (!(i = strlen(s)))
+        return 0;
+    for (i = i - 1; (i >= 0) &&
+         isspace((int)((unsigned char)s[i])); i--);
+
+    s[i + 1] = '\0';
+
+    for (i = 0; ('\0' != s[i]) &&
+         isspace((int)((unsigned char)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;
+        unsigned int *keys;
+        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);
+        keys = (unsigned int *)jk_pool_alloc(&m->p, sizeof(unsigned int) * 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);
+
+            if (m->capacity && m->keys)
+                memcpy(keys, m->keys, sizeof(unsigned int) * m->capacity);
+
+            m->names = (const char **)names;
+            m->values = (const void **)values;
+            m->keys = keys;
+            m->capacity = capacity;
+
+            return JK_TRUE;
+        }
+    }
+
+    return JK_FALSE;
+}
+
+/**
+ *  Replace $(property) in value.
+ *
+ */
+char *jk_map_replace_properties(jk_map_t *m, const char *value)
+{
+    char *rc = (char *)value;
+    char *env_start = rc;
+    int rec = 0;
+
+    while ((env_start = strstr(env_start, "$(")) != NULL) {
+        char *env_end = strstr(env_start, ")");
+        if (rec++ > 20)
+            return rc;
+        if (env_end) {
+            char env_name[LENGTH_OF_LINE + 1] = "";
+            const char *env_value;
+#if defined(WIN32)
+            char env_buf[LENGTH_OF_LINE + 1];
+#endif
+            *env_end = '\0';
+            strcpy(env_name, env_start + 2);
+            *env_end = ')';
+
+            env_value = jk_map_get_string(m, env_name, NULL);
+            if (!env_value) {
+                env_value = getenv(env_name);
+            }
+#if defined(WIN32)
+            if (!env_value) {
+                /* Try the env block from calling process */
+                if (GetEnvironmentVariable(env_name, env_buf,
+                                           sizeof(env_buf)))
+                    env_value = &env_buf[0];
+            }
+#endif
+            if (env_value) {
+                size_t 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;
+}
+
+/**
+ *  Resolve references
+ *
+ */
+int jk_map_resolve_references(jk_map_t *m, const char *prefix,
+                              int wildcard, int depth, jk_logger_t *l)
+{
+    int rc = JK_FALSE;
+
+    JK_TRACE_ENTER(l);
+
+    if (m && prefix) {
+        if (depth <= JK_MAP_RECURSION) {
+            size_t prelen = strlen(prefix);
+            unsigned int i;
+            rc = JK_TRUE;
+            if (JK_IS_DEBUG_LEVEL(l))
+                jk_log(l, JK_LOG_DEBUG,
+                       "Checking for references with prefix %s with%s wildcard (recursion %d)",
+                       prefix, wildcard? "" : "out", depth);
+            for (i = 0; i < m->size; i++) {
+                if (m->values[i] && !strncmp(m->names[i], prefix, prelen)) {
+                    size_t remain = strlen(m->names[i]) - prelen;
+                    if ((remain == JK_MAP_REFERENCE_SZ ) || (wildcard && remain > JK_MAP_REFERENCE_SZ)) {
+                        remain = strlen(m->names[i]) - JK_MAP_REFERENCE_SZ;
+                        if (!strncmp(m->names[i] + remain, JK_MAP_REFERENCE, JK_MAP_REFERENCE_SZ)) {
+                            char *from = jk_pool_alloc(&m->p,
+                                                       (sizeof(char) *
+                                                       (strlen(m->values[i]) + 2)));
+                            char *to = jk_pool_alloc(&m->p,
+                                                     (sizeof(char) *
+                                                     (remain + 2)));
+                            if (!from || !to) {
+                                jk_log(l, JK_LOG_ERROR,
+                                       "Error in string allocation");
+                                rc = JK_FALSE;
+                                break;
+                            }
+                            strcpy(from, m->values[i]);
+                            *(from+strlen(m->values[i]))   = '.';
+                            *(from+strlen(m->values[i])+1) = '\0';
+                            strncpy(to, m->names[i], remain);
+                            *(to+remain)   = '.';
+                            *(to+remain+1) = '\0';
+
+                            rc = jk_map_resolve_references(m, m->values[i], 0, depth+1, l);
+                            if (rc == JK_FALSE) {
+                                break;
+                            }
+                            if (JK_IS_DEBUG_LEVEL(l))
+                                jk_log(l, JK_LOG_DEBUG,
+                                       "Copying values from %s to %s",
+                                       from, to);
+                            rc = jk_map_inherit_properties(m, from, to, l);
+                            if (rc == JK_FALSE) {
+                                break;
+                            }
+                            m->values[i] = NULL;
+                        }
+                    }
+                }
+            }
+        }
+        else {
+            jk_log(l, JK_LOG_ERROR,
+                   "Recursion limit %d for worker references with prefix '%s' reached",
+                   JK_MAP_RECURSION, prefix);
+        }
+    }
+    else {
+        JK_LOG_NULL_PARAMS(l);
+    }
+    JK_TRACE_EXIT(l);
+    return rc;
+}
+
+/**
+ *  Inherit properties
+ *
+ */
+int jk_map_inherit_properties(jk_map_t *m, const char *from, const char *to, jk_logger_t *l)
+{
+    int rc = JK_FALSE;
+    const char *prp;
+    char *to_prp;
+
+    if (m && from && to) {
+        unsigned int i;
+        for (i = 0; i < m->size; i++) {
+            if (!strncmp(m->names[i], from, strlen(from))) {
+                rc = JK_TRUE;
+                prp = m->names[i] + strlen(from);
+                to_prp = jk_pool_alloc(&m->p,
+                                       (sizeof(char) *
+                                       (strlen(to) +
+                                       strlen(prp) + 1)));
+                if (!to_prp) {
+                    jk_log(l, JK_LOG_ERROR,
+                           "Error in string allocation for attribute '%s.%s'",
+                           to, prp);
+                    rc = JK_FALSE;
+                    break;
+                }
+                strcpy(to_prp, to);
+                strcat(to_prp, prp);
+                if (jk_map_get_id(m, to_prp) < 0 ) {
+                    rc = jk_map_add(m, to_prp, m->values[i]);
+                    if (rc == JK_FALSE) {
+                        jk_log(l, JK_LOG_ERROR,
+                               "Error when adding attribute '%s'",
+                               to_prp);
+                        break;
+                    }
+                }
+            }
+        }
+        if ( rc == JK_FALSE) {
+            jk_log(l, JK_LOG_ERROR,
+                   "Reference '%s' not found",
+                   from);
+        }
+    }
+    else {
+        JK_LOG_NULL_PARAMS(l);
+    }
+    return rc;
+}
+
+int jk_map_load_property(jk_map_t *m, const char *str, jk_logger_t *l)
+{
+    int rc = JK_TRUE;
+    char buf[LENGTH_OF_LINE + 1];
+    char *prp = &buf[0];
+
+    if (strlen(str) > LENGTH_OF_LINE) {
+        jk_log(l, JK_LOG_WARNING,
+               "Line to long (%d > %d), ignoring entry",
+               strlen(str), LENGTH_OF_LINE);
+        return JK_FALSE;
+    }
+
+    strcpy(prp, str);
+    if (trim(prp)) {
+        char *v = strchr(prp, '=');
+        if (v) {
+            *v = '\0';
+            v++;
+            trim(prp);
+            trim(v);
+            if (strlen(v) && strlen(prp)) {
+                v = jk_pool_strdup(&m->p, v);
+                if (v) {
+                    jk_map_put(m, prp, v, NULL);
+                }
+                else {
+                    JK_LOG_NULL_PARAMS(l);
+                    rc = JK_FALSE;
+                }
+            }
+        }
+    }
+    return rc;
+}
+
+
+int jk_map_load_properties(jk_map_t *m, const char *f, time_t *modified, jk_logger_t *l)
+{
+    int rc = JK_FALSE;
+
+    if (m && f) {
+        FILE *fp;
+        struct stat statbuf;
+        if (jk_stat(f, &statbuf) == -1)
+            return JK_FALSE;
+#if defined(AS400) && !defined(AS400_UTF8)
+        fp = fopen(f, "r, o_ccsid=0");
+#else
+        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 (*prp) {
+                    if ((rc = jk_map_load_property(m, prp, l)) == JK_FALSE)
+                        break;
+                }
+            }
+            fclose(fp);
+            if (modified)
+                *modified = statbuf.st_mtime;
+        }
+    }
+
+    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..2584fb7
--- /dev/null
+++ b/connectors/jk/native/common/jk_map.h
@@ -0,0 +1,98 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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 jk_map_alloc(jk_map_t **m);
+
+int jk_map_free(jk_map_t **m);
+
+int jk_map_open(jk_map_t *m);
+
+int jk_map_close(jk_map_t *m);
+
+void *jk_map_get(jk_map_t *m, const char *name, const void *def);
+
+int jk_map_get_id(jk_map_t *m, const char *name);
+
+int jk_map_get_int(jk_map_t *m, const char *name, int def);
+
+double jk_map_get_double(jk_map_t *m, const char *name, double def);
+
+int jk_map_get_bool(jk_map_t *m, const char *name, int def);
+
+const char *jk_map_get_string(jk_map_t *m, const char *name, const char *def);
+
+char **jk_map_get_string_list(jk_map_t *m,
+                           const char *name,
+                           unsigned *list_len, const char *def);
+
+int jk_map_get_int_list(jk_map_t *m,
+                        const char *name,
+                        int *list, unsigned int list_len,
+                        const char *def);
+
+int jk_map_add(jk_map_t *m, const char *name, const void *value);
+
+int jk_map_put(jk_map_t *m, const char *name, const void *value, void **old);
+
+int jk_map_read_property(jk_map_t *m, const char *str, int allow_duplicates, jk_logger_t *l);
+
+int jk_map_read_properties(jk_map_t *m, const char *f, time_t *modified, int allow_duplicates, jk_logger_t *l);
+
+int jk_map_size(jk_map_t *m);
+
+const char *jk_map_name_at(jk_map_t *m, int idex);
+
+void *jk_map_value_at(jk_map_t *m, int idex);
+
+int jk_map_load_property(jk_map_t *m, const char *str, jk_logger_t *l);
+
+int jk_map_load_properties(jk_map_t *m, const char *f, time_t *modified, jk_logger_t *l);
+
+/**
+ *  Replace $(property) in value.
+ *
+ */
+char *jk_map_replace_properties(jk_map_t *m, const char *value);
+
+int jk_map_resolve_references(jk_map_t *m, const char *prefix, int wildcard, int depth, jk_logger_t *l);
+
+int jk_map_inherit_properties(jk_map_t *m, const char *from, const char *to, jk_logger_t *l);
+
+#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..2147c06
--- /dev/null
+++ b/connectors/jk/native/common/jk_md5.c
@@ -0,0 +1,475 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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_uint32_t state[4], const unsigned char block[64]);
+static void Encode(unsigned char *output, const jk_uint32_t * input, size_t len);
+static void Decode(jk_uint32_t * output, const unsigned char *input, size_t len);
+static void jk_MD5Init(JK_MD5_CTX * context);
+static void jk_MD5Update(JK_MD5_CTX * context, const unsigned char *input,
+                         size_t 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_uint32_t)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+#define GG(a, b, c, d, x, s, ac) { \
+ (a) += G ((b), (c), (d)) + (x) + (jk_uint32_t)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+#define HH(a, b, c, d, x, s, ac) { \
+ (a) += H ((b), (c), (d)) + (x) + (jk_uint32_t)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+#define II(a, b, c, d, x, s, ac) { \
+ (a) += I ((b), (c), (d)) + (x) + (jk_uint32_t)(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,
+                         size_t inputLen)
+{
+    size_t i, idx, partLen;
+
+    /* Compute number of bytes mod 64 */
+    idx = (size_t) ((context->count[0] >> 3) & 0x3F);
+
+    /* Update number of bits */
+    if ((context->count[0] += ((jk_uint32_t) inputLen << 3))
+        < ((jk_uint32_t) inputLen << 3)) {
+        context->count[1]++;
+    }
+    context->count[1] += (jk_uint32_t) 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.
+ */
+static void JK_METHOD jk_MD5Final(unsigned char digest[16], JK_MD5_CTX * context)
+{
+    unsigned char bits[8];
+    size_t 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 = (size_t) ((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_uint32_t state[4], const unsigned char block[64])
+{
+    jk_uint32_t 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_uint32_t) into output (unsigned char). Assumes len is
+   a multiple of 4.
+ */
+static void Encode(unsigned char *output, const jk_uint32_t * input, size_t len)
+{
+    size_t i, j;
+    jk_uint32_t 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_uint32_t). Assumes len is
+ * a multiple of 4.
+ */
+static void Decode(jk_uint32_t * output, const unsigned char *input, size_t len)
+{
+    size_t i, j;
+
+    for (i = 0, j = 0; j < len; i++, j += 4)
+        output[i] = ((jk_uint32_t) input[j]) | (((jk_uint32_t) input[j + 1]) << 8) |
+            (((jk_uint32_t) input[j + 2]) << 16) | (((jk_uint32_t) input[j + 3]) <<
+                                                 24);
+}
+
+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..f740048
--- /dev/null
+++ b/connectors/jk/native/common/jk_md5.h
@@ -0,0 +1,84 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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
+
+/* MD5 context. */
+typedef struct
+{
+    jk_uint32_t state[4];      /* state (ABCD) */
+    jk_uint32_t 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..12053ae
--- /dev/null
+++ b/connectors/jk/native/common/jk_msg_buff.c
@@ -0,0 +1,376 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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"
+
+static char *jk_HEX = "0123456789ABCDEFX";
+
+/*
+ * Simple marshaling code.
+ */
+
+void jk_b_reset(jk_msg_buf_t *msg)
+{
+    msg->len = 4;
+    msg->pos = 4;
+}
+
+int jk_b_append_long(jk_msg_buf_t *msg, unsigned long val)
+{
+    if (msg->len + 4 > msg->maxlen) {
+        return -1;
+    }
+
+    msg->buf[msg->len++] = (unsigned char)((val >> 24) & 0xFF);
+    msg->buf[msg->len++] = (unsigned char)((val >> 16) & 0xFF);
+    msg->buf[msg->len++] = (unsigned char)((val >> 8) & 0xFF);
+    msg->buf[msg->len++] = (unsigned char)((val) & 0xFF);
+
+    return 0;
+}
+
+
+int jk_b_append_int(jk_msg_buf_t *msg, unsigned short val)
+{
+    if (msg->len + 2 > msg->maxlen) {
+        return -1;
+    }
+
+    msg->buf[msg->len++] = (unsigned char)((val >> 8) & 0xFF);
+    msg->buf[msg->len++] = (unsigned char)((val) & 0xFF);
+
+    return 0;
+}
+
+
+int jk_b_append_byte(jk_msg_buf_t *msg, unsigned char val)
+{
+    if (msg->len + 1 > msg->maxlen) {
+        return -1;
+    }
+
+    msg->buf[msg->len++] = val;
+
+    return 0;
+}
+
+
+void jk_b_end(jk_msg_buf_t *msg, int protoh)
+{
+    /*
+     * Ugly way to set the size in the right position
+     */
+    int hlen = msg->len - 4;
+
+    msg->buf[0] = (unsigned char)((protoh >> 8) & 0xFF);
+    msg->buf[1] = (unsigned char)((protoh) & 0xFF);
+    msg->buf[2] = (unsigned char)((hlen >> 8) & 0xFF);
+    msg->buf[3] = (unsigned char)((hlen) & 0xFF);
+
+}
+
+
+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, unsigned char *data, int buffSize)
+{
+    if (!msg) {
+        return -1;
+    }
+
+    msg->len = 0;
+    msg->buf = 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, data, buffSize);
+    return 0;
+}
+
+#if defined(AS400) && !defined(AS400_UTF8)
+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)
+{
+    unsigned short len;
+
+    if (!param) {
+        jk_b_append_int(msg, 0xFFFF);
+        return 0;
+    }
+
+    len = (unsigned short)strlen(param);
+    if (msg->len + len + 2 > msg->maxlen) {
+        return -1;
+    }
+
+    /* ignore error - we checked once */
+    jk_b_append_int(msg, len);
+
+    /* We checked for space !!  */
+    strncpy((char *)msg->buf + msg->len, param, len + 1);       /* including \0 */
+#if (defined(AS400) && !defined(AS400_UTF8)) || 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) {
+        return 0xFFFFFFFF;
+    }
+    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)
+{
+    unsigned short i;
+    if (msg->pos + 1 > msg->len) {
+        return 0xFFFF;
+    }
+    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)
+{
+    unsigned short 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) {
+        return 0xFF;
+    }
+    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)
+{
+    unsigned short size = jk_b_get_int(msg);
+    int start = msg->pos;
+
+    if ((size == 0xFFFF) || (size + start > msg->maxlen)) {
+        /* TODO: return NULL and deal with that in the code */
+        return (unsigned char *)"ERROR";
+    }
+
+    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)) {
+        return (-1);
+    }
+
+    memcpy(buf, msg->buf + start, len);
+    msg->pos += len;
+    return (len);
+}
+
+
+
+/** Helpie dump function
+ */
+void jk_dump_buff(jk_logger_t *l,
+                  const char *file,
+                  int line, const char *funcname,
+                  int level, char *what, jk_msg_buf_t *msg)
+{
+    int i = 0;
+    char lb[80];
+    char *current;
+    int j;
+    int len = msg->len;
+
+    if (l == NULL)
+        return;
+    if (l->level != JK_LOG_TRACE_LEVEL && len > 1024)
+        len = 1024;
+
+    jk_log(l, file, line, funcname, level,
+           "%s pos=%d len=%d max=%d",
+           what, msg->pos, msg->len, msg->maxlen);
+
+    for (i = 0; i < len; i += 16) {
+        current = &lb[0];
+
+        for (j = 0; j < 16; j++) {
+            unsigned char x = (msg->buf[i + j]);
+            if ((i + j) >= len)
+                x = 0;
+            *current++ = jk_HEX[x >> 4];
+            *current++ = jk_HEX[x & 0x0f];
+            *current++ = ' ';
+        }
+        *current++ = ' ';
+        *current++ = '-';
+        *current++ = ' ';
+        for (j = 0; j < 16; j++) {
+            unsigned char x = msg->buf[i + j];
+            if ((i + j) >= len)
+                x = 0;
+            if (x > 0x20 && x < 0x7F) {
+#ifdef USE_CHARSET_EBCDIC
+               *current = x;
+               jk_xlate_from_ascii(current, 1);
+               current++;
+#else
+              *current++ = x;
+#endif
+            }
+            else {
+                *current++ = '.';
+            }
+        }
+        *current++ = '\0';
+
+            jk_log(l, file, line, funcname, level,
+                   "%.4x    %s", i, lb);
+    }
+}
+
+
+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..4858028
--- /dev/null
+++ b/connectors/jk/native/common/jk_msg_buff.h
@@ -0,0 +1,154 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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
+  -
+
+
+
+ */
+
+typedef struct jk_msg_buf_t jk_msg_buf_t;
+struct jk_msg_buf_t
+{
+    jk_pool_t *pool;
+    unsigned char *buf;
+    int pos;
+    int len;
+    int maxlen;
+};
+
+
+/* -------------------- 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, unsigned 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);
+
+/* -------------------- 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);
+
+#if defined(AS400) && !defined(AS400_UTF8)
+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, const char *funcname,
+                  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..fe350a8
--- /dev/null
+++ b/connectors/jk/native/common/jk_mt.h
@@ -0,0 +1,147 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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"
+
+
+#if defined(WIN32)
+#define jk_gettid()    ((int)GetCurrentThreadId())
+#elif defined(NETWARE) && !defined(__NOVELL_LIBC__)
+#define getpid()       ((int)GetThreadGroupID())
+#endif
+
+#ifdef JK_PREFORK 
+#define _MT_CODE 0
+#else
+#define _MT_CODE 1
+#endif
+
+/*
+ * Marks execution under MT compilation
+ */
+#if _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 /* !WIN32 */
+#define _MT_CODE_PTHREAD
+#include <pthread.h>
+#include <unistd.h>
+#include <fcntl.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
+
+int jk_gettid(void);
+#endif /* WIN32 */
+
+#else /* !_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
+#define jk_gettid()         0
+#endif /* MT_CODE */
+
+#if !defined(WIN32) && !defined(NETWARE)
+#include <unistd.h>
+#include <fcntl.h>
+
+
+#define USE_FLOCK_LK 0
+#if HAVE_FLOCK
+#ifdef JK_USE_FLOCK
+#define USE_FLOCK_LK 1
+#endif
+#endif
+
+#if USE_FLOCK_LK
+#include <sys/file.h>
+
+#define JK_ENTER_LOCK(x, rc)        \
+    do {                            \
+      while ((rc = flock((x), LOCK_EX) < 0) && (errno == EINTR)); \
+      rc = rc == 0 ? JK_TRUE : JK_FALSE; \
+    } while (0)
+
+#define JK_LEAVE_LOCK(x, rc)        \
+    do {                            \
+      while ((rc = flock((x), LOCK_UN) < 0) && (errno == EINTR)); \
+      rc = rc == 0 ? JK_TRUE : JK_FALSE; \
+    } while (0)
+
+#else /* !USE_FLOCK_LK */
+
+#define JK_ENTER_LOCK(x, rc)        \
+    do {                            \
+      struct flock _fl;             \
+      _fl.l_type   = F_WRLCK;       \
+      _fl.l_whence = SEEK_SET;      \
+      _fl.l_start  = 0;             \
+      _fl.l_len    = 1L;            \
+      _fl.l_pid    = 0;             \
+      while ((rc = fcntl((x), F_SETLKW, &_fl) < 0) && (errno == EINTR)); \
+      rc = rc == 0 ? JK_TRUE : JK_FALSE; \
+    } while (0)
+
+#define JK_LEAVE_LOCK(x, rc)        \
+    do {                            \
+      struct flock _fl;             \
+      _fl.l_type   = F_UNLCK;       \
+      _fl.l_whence = SEEK_SET;      \
+      _fl.l_start  = 0;             \
+      _fl.l_len    = 1L;            \
+      _fl.l_pid    = 0;             \
+      while ((rc = fcntl((x), F_SETLKW, &_fl) < 0) && (errno == EINTR)); \
+      rc = rc == 0 ? JK_TRUE : JK_FALSE; \
+    } while (0)
+
+#endif /* USE_FLOCK_LK */
+
+#else  /* WIN32 || NETWARE */
+#define JK_ENTER_LOCK(x, rc) rc = JK_TRUE
+#define JK_LEAVE_LOCK(x, rc) rc = JK_TRUE
+#endif
+
+#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..22072a6
--- /dev/null
+++ b/connectors/jk/native/common/jk_nwmain.c
@@ -0,0 +1,103 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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..ceb0e85
--- /dev/null
+++ b/connectors/jk/native/common/jk_pool.c
@@ -0,0 +1,165 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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, size_t 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)
+{
+    jk_reset_pool(p);
+    if (p->dynamic) {
+        free(p->dynamic);
+    }
+}
+
+void jk_reset_pool(jk_pool_t *p)
+{
+    if (p->dyn_pos && p->dynamic) {
+        size_t 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;
+
+    size = JK_ALIGN_DEFAULT(size);
+    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)
+{
+    void *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;
+}
+
+#if defined (DEBUG) || defined(_DEBUG)
+static void jk_dump_pool(jk_pool_t *p, FILE * f)
+{
+    fprintf(f, "Dumping for pool [%p]\n",  p);
+    fprintf(f, "size             [%ld]\n", p->size);
+    fprintf(f, "pos              [%ld]\n", p->pos);
+    fprintf(f, "buf              [%p]\n",  p->buf);
+    fprintf(f, "dyn_size         [%ld]\n", p->dyn_size);
+    fprintf(f, "dyn_pos          [%ld]\n", p->dyn_pos);
+    fprintf(f, "dynamic          [%p]\n",  p->dynamic);
+
+    fflush(f);
+}
+#endif
+
+static void *jk_pool_dyn_alloc(jk_pool_t *p, size_t size)
+{
+    void *rc;
+
+    if (p->dyn_size == p->dyn_pos) {
+        size_t new_dyn_size = p->dyn_size * 2 + DEFAULT_DYNAMIC;
+        void **new_dynamic = (void **)malloc(new_dyn_size * sizeof(void *));
+        if (new_dynamic) {
+            if (p->dynamic) {
+                /* Copy old dynamic slots */
+                memcpy(new_dynamic, p->dynamic, p->dyn_size * sizeof(void *));
+
+                free(p->dynamic);
+            }
+
+            p->dynamic = new_dynamic;
+            p->dyn_size = new_dyn_size;
+        }
+        else {
+#if defined (DEBUG) || defined(_DEBUG)
+            jk_dump_pool(p, stderr);
+#endif            
+            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..7a97d03
--- /dev/null
+++ b/connectors/jk/native/common/jk_pool.h
@@ -0,0 +1,126 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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
+
+/**
+ * Alignment macros
+ */
+
+/* JK_ALIGN() is only to be used to align on a power of 2 boundary */
+#define JK_ALIGN(size, boundary) \
+    (((size) + ((boundary) - 1)) & ~((boundary) - 1))
+
+/** Default alignment */
+#ifdef AS400
+#define JK_ALIGN_DEFAULT(size) JK_ALIGN(size, 16)
+#else
+#define JK_ALIGN_DEFAULT(size) JK_ALIGN(size, 8)
+#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. */
+
+/** jk pool structure */
+struct jk_pool
+{
+    size_t size;
+    size_t pos;
+    char *buf;
+    size_t dyn_size;
+    size_t dyn_pos;
+    void **dynamic;
+};
+
+typedef struct jk_pool jk_pool_t;
+
+void jk_open_pool(jk_pool_t *p, jk_pool_atom_t *buf, size_t 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..5145d61
--- /dev/null
+++ b/connectors/jk/native/common/jk_service.h
@@ -0,0 +1,480 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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_logger.h"
+#include "jk_pool.h"
+#include "jk_map.h"
+#include "jk_uri_worker_map.h"
+#include "jk_msg_buff.h"
+
+#define JK_RETRIES 2
+
+#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;
+
+    unsigned int num_of_workers;
+    char **worker_list;
+
+    /* 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
+     */
+    const char *method;
+    const char *protocol;
+    char *req_uri;
+    const char *remote_addr;
+    const char *remote_host;
+    const char *remote_user;
+    const char *auth_type;
+    const char *query_string;
+    const 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 route is in use when the adapter load balance among
+     * several workers. It is the ID of a specific target in the load balance
+     * group. We are using this variable to implement target session
+     * affinity
+     */
+    const char *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.
+     */
+    const char *secret;
+
+    /*
+     * Area to get POST data for fail-over recovery in POST
+     */
+    jk_msg_buf_t *reco_buf;
+    int reco_status;
+
+    /* Number of retries. Defaults to JK_RETRIES
+     */
+    int retries;
+
+    /*
+     * If set call flush after each write
+     */
+    int flush_packets;
+
+    /*
+     * If set call flush after AJP13_SEND_HEADERS.
+     */
+    int flush_header;
+
+    /*
+     * HTTP status sent from container.
+     */
+    int http_response_status;
+
+    /* Uri worker map. Added for virtual host support
+     */
+    jk_uri_worker_map_t *uw_map;
+
+    /*
+     * 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.
+     */
+    /*
+     * 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);
+
+    /*
+     * Flush a chunk of response data back to the browser.
+     */
+    void (JK_METHOD * flush) (jk_ws_service_t *s);
+
+    /*
+     * If set do not reuse socket after each full response
+     */
+    int disable_reuse;
+
+    /*
+     * Add more data to log facilities.
+     */
+    void (JK_METHOD * add_log_items) (jk_ws_service_t *s,
+                                      const char *const *log_names,
+                                      const char *const *log_values,
+                                      unsigned num_of_items);
+};
+
+/*
+ * 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
+{
+    size_t rd;
+    size_t wr;
+
+    /*
+     * 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.
+     * is_error is either 0 meaning recoverable or set to
+     * the HTTP error code.
+     */
+    int (JK_METHOD * service) (jk_endpoint_t *e,
+                               jk_ws_service_t *s,
+                               jk_logger_t *l, int *is_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
+{
+
+    /*
+     * Public property to enable the number of retry attempts
+     * on this worker.
+     */
+    int retries;
+    /*
+     * 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;
+
+    int   type;
+    /*
+     * 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);
+
+    /*
+     * Update worker either from jk_status or reloading from workers.properties
+     */
+    int (JK_METHOD * update) (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);
+
+    /*
+     * Maintain this worker.
+     */
+    int (JK_METHOD * maintain) (jk_worker_t *w, time_t now, 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);
+
+void jk_init_ws_service(jk_ws_service_t *s);
+
+
+#ifdef __cplusplus
+}
+#endif                          /* __cplusplus */
+
+#endif                          /* JK_SERVICE_H */
diff --git a/connectors/jk/native/common/jk_shm.c b/connectors/jk/native/common/jk_shm.c
new file mode 100644
index 0000000..109e09c
--- /dev/null
+++ b/connectors/jk/native/common/jk_shm.c
@@ -0,0 +1,602 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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: Shared Memory support                                      *
+ * Author:      Mladen Turk <mturk@jboss.com>                              *
+ * Author:      Rainer Jung <rjung@apache.org>                             *
+ * Version:     $Revision$                                        *
+ ***************************************************************************/
+
+#include "jk_global.h"
+#include "jk_pool.h"
+#include "jk_util.h"
+#include "jk_mt.h"
+#include "jk_shm.h"
+
+/** jk shm header core data structure */
+struct jk_shm_header_data
+{
+    /* Shared memory magic JK_SHM_MAGIC */
+    char   magic[JK_SHM_MAGIC_SIZ];
+    size_t size;
+    size_t pos;
+    unsigned int childs;
+    unsigned int workers;
+    time_t modified;
+};
+
+typedef struct jk_shm_header_data jk_shm_header_data_t;
+
+/** jk shm header record structure */
+struct jk_shm_header
+{
+    union {
+        jk_shm_header_data_t data;
+        char alignbuf[JK_SHM_ALIGN(sizeof(jk_shm_header_data_t))];
+    } h;
+    char   buf[1];
+};
+
+typedef struct jk_shm_header jk_shm_header_t;
+
+/** jk shm structure */
+struct jk_shm
+{
+    size_t     size;
+    char       *filename;
+    char       *lockname;
+    int        fd;
+    int        fd_lock;
+    int        attached;
+    jk_shm_header_t  *hdr;
+    JK_CRIT_SEC       cs;
+};
+
+typedef struct jk_shm jk_shm_t;
+
+static const char shm_signature[] = { JK_SHM_MAGIC };
+static jk_shm_t jk_shmem = { 0, NULL, NULL, -1, -1, 0, NULL};
+static time_t jk_workers_modified_time = 0;
+static time_t jk_workers_access_time = 0;
+#if defined (WIN32)
+static HANDLE jk_shm_map = NULL;
+#endif
+
+#if defined (WIN32) || defined(NETWARE)
+
+/* Use plain memory */
+int jk_shm_open(const char *fname, size_t sz, jk_logger_t *l)
+{
+    int rc;
+    int attached = 0;
+    JK_TRACE_ENTER(l);
+    if (jk_shmem.hdr) {
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG, "Shared memory is already opened");
+        JK_TRACE_EXIT(l);
+        return 0;
+    }
+
+    jk_shmem.size =  JK_SHM_ALIGN(sizeof(jk_shm_header_t) + sz);
+
+#if defined (WIN32)
+    if (fname) {
+        jk_shm_map = CreateFileMapping(INVALID_HANDLE_VALUE,
+                                       NULL,
+                                       PAGE_READWRITE,
+                                       0,
+                                       (DWORD)(sizeof(jk_shm_header_t) + sz),
+                                       fname);
+        if (GetLastError() == ERROR_ALREADY_EXISTS) {
+            attached = 1;
+            if (jk_shm_map == NULL || jk_shm_map == INVALID_HANDLE_VALUE) {
+                jk_shm_map = OpenFileMapping(PAGE_READWRITE, FALSE, fname);
+            }
+        }
+        if (jk_shm_map == NULL || jk_shm_map == INVALID_HANDLE_VALUE) {
+            JK_TRACE_EXIT(l);
+            return -1;
+        }
+        jk_shmem.hdr = (jk_shm_header_t *)MapViewOfFile(jk_shm_map,
+                                                        FILE_MAP_ALL_ACCESS,
+                                                        0,
+                                                        0,
+                                                        0);
+    }
+    else
+#endif
+    jk_shmem.hdr = (jk_shm_header_t *)calloc(1, jk_shmem.size);
+    if (!jk_shmem.hdr) {
+#if defined (WIN32)
+        if (jk_shm_map) {
+            CloseHandle(jk_shm_map);
+            jk_shm_map = NULL;
+        }
+#endif
+        JK_TRACE_EXIT(l);
+        return -1;
+    }
+    if (!jk_shmem.filename) {
+        if (fname)
+            jk_shmem.filename = strdup(fname);
+        else
+            jk_shmem.filename = strdup("memory");
+    }
+    jk_shmem.fd       = 0;
+    jk_shmem.attached = attached;
+    if (!attached) {
+        memcpy(jk_shmem.hdr->h.data.magic, shm_signature,
+               JK_SHM_MAGIC_SIZ);
+        jk_shmem.hdr->h.data.size = sz;
+        jk_shmem.hdr->h.data.childs = 1;
+    }
+    else {
+        jk_shmem.hdr->h.data.childs++;
+        /*
+         * Reset the shared memory so that
+         * alloc works even for attached memory.
+         * XXX: This might break already used memory
+         * if the number of workers change between
+         * open and attach or between two attach operations.
+         */
+        if (jk_shmem.hdr->h.data.childs > 1) {
+            if (JK_IS_DEBUG_LEVEL(l)) {
+                jk_log(l, JK_LOG_DEBUG,
+                       "Reseting the shared memory for child %d",
+                       jk_shmem.hdr->h.data.childs);
+            }
+        }
+        jk_shmem.hdr->h.data.pos     = 0;
+        jk_shmem.hdr->h.data.workers = 0;
+    }
+    JK_INIT_CS(&(jk_shmem.cs), rc);
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+               "%s shared memory size=%u free=%u addr=%#lx",
+               attached ? "Attached" : "Initialized",
+               jk_shmem.size, jk_shmem.hdr->h.data.size, jk_shmem.hdr);
+    JK_TRACE_EXIT(l);
+    return 0;
+}
+
+int jk_shm_attach(const char *fname, size_t sz, jk_logger_t *l)
+{
+    JK_TRACE_ENTER(l);
+    if (!jk_shm_open(fname, sz, l)) {
+        if (!jk_shmem.attached) {
+            jk_shmem.attached = 1;
+            if (JK_IS_DEBUG_LEVEL(l)) {
+                jk_log(l, JK_LOG_DEBUG,
+                   "Attached shared memory [%d] size=%u free=%u addr=%#lx",
+                   jk_shmem.hdr->h.data.childs, jk_shmem.hdr->h.data.size,
+                   jk_shmem.hdr->h.data.size - jk_shmem.hdr->h.data.pos,
+                   jk_shmem.hdr);
+            }
+        }
+        JK_TRACE_EXIT(l);
+        return 0;
+    }
+    else {
+        JK_TRACE_EXIT(l);
+        return -1;
+    }
+}
+
+void jk_shm_close()
+{
+    if (jk_shmem.hdr) {
+        int rc;
+#if defined (WIN32)
+        if (jk_shm_map) {
+            --jk_shmem.hdr->h.data.childs;
+            UnmapViewOfFile(jk_shmem.hdr);
+            CloseHandle(jk_shm_map);
+            jk_shm_map = NULL;
+        }
+        else
+#endif
+        free(jk_shmem.hdr);
+        JK_DELETE_CS(&(jk_shmem.cs), rc);
+    }
+    jk_shmem.hdr = NULL;
+    if (jk_shmem.filename) {
+        free(jk_shmem.filename);
+        jk_shmem.filename = NULL;
+    }
+}
+
+#else
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/uio.h>
+
+#ifndef MAP_FAILED
+#define MAP_FAILED  (-1)
+#endif
+
+#ifndef MAP_FILE
+#define MAP_FILE    (0)
+#endif
+
+static int do_shm_open_lock(const char *fname, int attached, jk_logger_t *l)
+{
+    int rc;
+    char flkname[256];
+    JK_TRACE_ENTER(l);
+
+    if (attached && jk_shmem.lockname) {
+#ifdef JK_SHM_LOCK_REOPEN
+        jk_shmem.fd_lock = open(jk_shmem.lockname, O_RDWR, 0666);
+#else
+        errno = EINVAL;
+#endif        
+        if (jk_shmem.fd_lock == -1) {
+            rc = errno;
+            JK_TRACE_EXIT(l);
+            return rc;
+        }
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "Duplicated shared memory lock %s", jk_shmem.lockname);
+        JK_TRACE_EXIT(l);
+        return 0;
+    }
+
+    if (!jk_shmem.lockname) {
+#ifdef JK_SHM_LOCK_REOPEN
+        int i;
+        jk_shmem.fd_lock = -1;
+        mode_t mask = umask(0);
+        for (i = 0; i < 8; i++) {
+            strcpy(flkname, "/tmp/jkshmlock.XXXXXX");
+            if (mktemp(flkname)) {
+                jk_shmem.fd_lock = open(flkname, O_RDWR|O_CREAT|O_TRUNC, 0666);
+                if (jk_shmem.fd_lock >= 0)
+                    break;
+            }
+        }
+        umask(mask);
+#else
+        strcpy(flkname, fname);
+        strcat(flkname, ".lock");
+        jk_shmem.fd_lock = open(flkname, O_RDWR|O_CREAT|O_TRUNC, 0666);
+#endif
+        if (jk_shmem.fd_lock == -1) {
+            rc = errno;
+            JK_TRACE_EXIT(l);
+            return rc;
+        }
+        jk_shmem.lockname = strdup(flkname);
+    }
+    else {
+        /* Nothing to do */
+        JK_TRACE_EXIT(l);
+        return 0;
+    }
+
+    if (ftruncate(jk_shmem.fd_lock, 1)) {
+        rc = errno;
+        close(jk_shmem.fd_lock);
+        jk_shmem.fd_lock = -1;
+        JK_TRACE_EXIT(l);
+        return rc;
+    }
+    if (lseek(jk_shmem.fd_lock, 0, SEEK_SET) != 0) {
+        rc = errno;
+        close(jk_shmem.fd_lock);
+        jk_shmem.fd_lock = -1;
+        return rc;
+    }
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+               "Opened shared memory lock %s", jk_shmem.lockname);
+    JK_TRACE_EXIT(l);
+    return 0;
+}
+
+static int do_shm_open(const char *fname, int attached,
+                       size_t sz, jk_logger_t *l)
+{
+    int rc;
+    int fd;
+    void *base;
+
+    JK_TRACE_ENTER(l);
+    if (jk_shmem.hdr) {
+        /* Probably a call from vhost */
+        if (!attached)
+            attached = 1;
+    }
+    else if (attached) {
+        /* We should already have a header
+         * Use memory if we don't
+         */
+        JK_TRACE_EXIT(l);
+        return 0;
+    }
+    jk_shmem.size = JK_SHM_ALIGN(sizeof(jk_shm_header_t) + sz);
+
+    if (!fname) {
+        /* Use plain memory in case there is no file name */
+        if (!jk_shmem.filename)
+            jk_shmem.filename  = strdup("memory");
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "Using process memory as shared memory");
+        JK_TRACE_EXIT(l);
+        return 0;
+    }
+
+    if (!jk_shmem.filename) {
+        jk_shmem.filename = (char *)malloc(strlen(fname) + 32);
+        sprintf(jk_shmem.filename, "%s.%d", fname, (int)getpid());
+    }
+    if (!attached) {
+        size_t size;
+        jk_shmem.attached = 0;
+        fd = open(jk_shmem.filename, O_RDWR|O_CREAT|O_TRUNC, 0666);
+        if (fd == -1) {
+            jk_shmem.size = 0;
+            JK_TRACE_EXIT(l);
+            return errno;
+        }
+        size = lseek(fd, 0, SEEK_END);
+        if (size < jk_shmem.size) {
+            size = jk_shmem.size;
+            if (ftruncate(fd, jk_shmem.size)) {
+                rc = errno;
+                close(fd);
+                unlink(jk_shmem.filename);
+                jk_shmem.size = 0;
+                JK_TRACE_EXIT(l);
+                return rc;
+            }
+            if (JK_IS_DEBUG_LEVEL(l))
+                jk_log(l, JK_LOG_DEBUG,
+                       "Truncated shared memory to %u", size);
+        }
+        if (lseek(fd, 0, SEEK_SET) != 0) {
+            rc = errno;
+            close(fd);
+            unlink(jk_shmem.filename);
+            jk_shmem.size = 0;
+            JK_TRACE_EXIT(l);
+            return rc;
+        }
+
+        base = mmap((caddr_t)0, jk_shmem.size,
+                    PROT_READ | PROT_WRITE,
+                    MAP_FILE | MAP_SHARED,
+                    fd, 0);
+        if (base == (caddr_t)MAP_FAILED || base == (caddr_t)0) {
+            rc = errno;
+            close(fd);
+            unlink(jk_shmem.filename);
+            jk_shmem.size = 0;
+            JK_TRACE_EXIT(l);
+            return rc;
+        }
+        jk_shmem.hdr = base;
+        jk_shmem.fd  = fd;
+        memset(jk_shmem.hdr, 0, jk_shmem.size);
+        memcpy(jk_shmem.hdr->h.data.magic, shm_signature, JK_SHM_MAGIC_SIZ);
+        jk_shmem.hdr->h.data.size = sz;
+        jk_shmem.hdr->h.data.childs = 1;
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "Initialized shared memory size=%u free=%u addr=%#lx",
+                   jk_shmem.size, jk_shmem.hdr->h.data.size, jk_shmem.hdr);
+    }
+    else {
+        unsigned int nchild;
+        jk_shmem.hdr->h.data.childs++;
+        jk_shmem.attached = (int)getpid();
+        nchild = jk_shmem.hdr->h.data.childs;
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "Attached shared memory [%d] size=%u free=%u addr=%#lx",
+                   nchild, jk_shmem.hdr->h.data.size,
+                   jk_shmem.hdr->h.data.size - jk_shmem.hdr->h.data.pos,
+                   jk_shmem.hdr);
+        /*
+         * Reset the shared memory so that
+         * alloc works even for attached memory.
+         * XXX: This might break already used memory
+         * if the number of workers change between
+         * open and attach or between two attach operations.
+         */
+        if (nchild > 1) {
+            if (JK_IS_DEBUG_LEVEL(l)) {
+                jk_log(l, JK_LOG_DEBUG,
+                       "Reseting the shared memory for child %d",
+                       nchild);
+            }
+        }
+        jk_shmem.hdr->h.data.pos     = 0;
+        jk_shmem.hdr->h.data.workers = 0;
+    }
+    JK_INIT_CS(&(jk_shmem.cs), rc);
+    if ((rc = do_shm_open_lock(jk_shmem.filename, attached, l))) {
+        if (!attached) {
+            munmap((void *)jk_shmem.hdr, jk_shmem.size);
+            close(jk_shmem.fd);
+            unlink(jk_shmem.filename);
+        }
+        jk_shmem.hdr = NULL;
+        jk_shmem.fd  = -1;
+        JK_TRACE_EXIT(l);
+        return rc;
+    }
+    JK_TRACE_EXIT(l);
+    return 0;
+}
+
+int jk_shm_open(const char *fname, size_t sz, jk_logger_t *l)
+{
+    return do_shm_open(fname, 0, sz, l);
+}
+
+int jk_shm_attach(const char *fname, size_t sz, jk_logger_t *l)
+{
+    return do_shm_open(fname, 1, sz, l);
+}
+
+void jk_shm_close()
+{
+    int rc;
+    if (jk_shmem.hdr) {
+        --jk_shmem.hdr->h.data.childs;
+
+#ifdef JK_SHM_LOCK_REOPEN
+        if (jk_shmem.fd_lock >= 0) {
+            close(jk_shmem.fd_lock);
+            jk_shmem.fd_lock = -1;
+        }
+#endif
+        JK_DELETE_CS(&(jk_shmem.cs), rc);
+        if (jk_shmem.attached) {
+            int p = (int)getpid();
+            if (p == jk_shmem.attached) {
+                /* In case this is a forked child
+                 * do not close the shared memory.
+                 * It will be closed by the parent.
+                 */
+                jk_shmem.size = 0;
+                jk_shmem.hdr  = NULL;
+                jk_shmem.fd   = -1;
+                return;
+            }
+        }
+        if (jk_shmem.fd >= 0) {
+            munmap((void *)jk_shmem.hdr, jk_shmem.size);
+            close(jk_shmem.fd);
+        }
+        if (jk_shmem.fd_lock >= 0)
+            close(jk_shmem.fd_lock);
+        if (jk_shmem.lockname) {
+            unlink(jk_shmem.lockname);
+            free(jk_shmem.lockname);
+            jk_shmem.lockname = NULL;
+        }
+        if (jk_shmem.filename) {
+            unlink(jk_shmem.filename);
+            free(jk_shmem.filename);
+            jk_shmem.filename = NULL;
+        }
+    }
+    jk_shmem.size    = 0;
+    jk_shmem.hdr     = NULL;
+    jk_shmem.fd      = -1;
+    jk_shmem.fd_lock = -1;
+}
+
+
+#endif
+
+void *jk_shm_alloc(jk_pool_t *p, size_t size)
+{
+    void *rc = NULL;
+
+    if (jk_shmem.hdr) {
+        size = JK_ALIGN_DEFAULT(size);
+        if ((jk_shmem.hdr->h.data.size - jk_shmem.hdr->h.data.pos) >= size) {
+            rc = &(jk_shmem.hdr->buf[jk_shmem.hdr->h.data.pos]);
+            jk_shmem.hdr->h.data.pos += size;
+        }
+    }
+    else if (p)
+        rc = jk_pool_alloc(p, size);
+
+    return rc;
+}
+
+const char *jk_shm_name()
+{
+    return jk_shmem.filename;
+}
+
+
+time_t jk_shm_get_workers_time()
+{
+    if (jk_shmem.hdr)
+        return jk_shmem.hdr->h.data.modified;
+    else
+        return jk_workers_modified_time;
+}
+
+void jk_shm_set_workers_time(time_t t)
+{
+    if (jk_shmem.hdr)
+        jk_shmem.hdr->h.data.modified = t;
+    else
+        jk_workers_modified_time = t;
+    jk_workers_access_time = t;
+}
+
+int jk_shm_is_modified()
+{
+    time_t m = jk_shm_get_workers_time();
+    if (m != jk_workers_access_time)
+        return 1;
+    else
+        return 0;
+}
+
+void jk_shm_sync_access_time()
+{
+    jk_workers_access_time = jk_shm_get_workers_time();
+}
+
+int jk_shm_lock()
+{
+    int rc;
+    JK_ENTER_CS(&(jk_shmem.cs), rc);
+    if (rc == JK_TRUE && jk_shmem.fd_lock != -1) {
+        JK_ENTER_LOCK(jk_shmem.fd_lock, rc);
+    }
+    return rc;
+}
+
+int jk_shm_unlock()
+{
+    int rc;
+    JK_LEAVE_CS(&(jk_shmem.cs), rc);
+    if (rc == JK_TRUE && jk_shmem.fd_lock != -1) {
+        JK_LEAVE_LOCK(jk_shmem.fd_lock, rc);
+    }
+    return rc;
+}
+
+jk_shm_worker_t *jk_shm_alloc_worker(jk_pool_t *p)
+{
+    jk_shm_worker_t *w = (jk_shm_worker_t *)jk_shm_alloc(p, sizeof(jk_shm_worker_t));
+    if (w) {
+        memset(w, 0, sizeof(jk_shm_worker_t));
+        if (jk_shmem.hdr) {
+            jk_shmem.hdr->h.data.workers++;
+            w->id = jk_shmem.hdr->h.data.workers;
+        }
+        else
+            w->id = -1;
+    }
+    return w;
+}
diff --git a/connectors/jk/native/common/jk_shm.h b/connectors/jk/native/common/jk_shm.h
new file mode 100644
index 0000000..8f59b96
--- /dev/null
+++ b/connectors/jk/native/common/jk_shm.h
@@ -0,0 +1,178 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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: Shared Memory object header file                           *
+ * Author:      Mladen Turk <mturk@jboss.com>                              *
+ * Author:      Rainer Jung <rjung@apache.org>                             *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+#ifndef _JK_SHM_H
+#define _JK_SHM_H
+
+#include "jk_global.h"
+#include "jk_pool.h"
+#include "jk_util.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif                          /* __cplusplus */
+
+/**
+ * @file jk_shm.h
+ * @brief Jk shared memory management
+ *
+ *
+ */
+
+#define JK_SHM_MAJOR    '1'
+#define JK_SHM_MINOR    '2'
+#define JK_SHM_STR_SIZ  63
+#define JK_SHM_URI_SIZ  127
+#define JK_SHM_DYNAMIC  16
+#define JK_SHM_MAGIC    '!', 'J', 'K', 'S', 'H', 'M', JK_SHM_MAJOR, JK_SHM_MINOR
+#define JK_SHM_MAGIC_SIZ  8
+
+/* Really huge numbers, but 64 workers should be enough */
+#define JK_SHM_MAX_WORKERS  64
+#define JK_SHM_WORKER_SIZE  JK_SHM_ALIGN(sizeof(jk_shm_worker_t))
+#define JK_SHM_DEF_SIZE     (JK_SHM_MAX_WORKERS * JK_SHM_WORKER_SIZE)
+#define JK_SHM_ALIGNMENT    64
+#define JK_SHM_ALIGN(x)     JK_ALIGN(x, JK_SHM_ALIGNMENT)
+
+/** jk shm worker record structure */
+struct jk_shm_worker
+{
+    int     id;
+    /* Sequence counter starting at 0 and increasing
+     * every time we change the config
+     */
+    volatile unsigned int sequence;
+    /* Number of currently busy channels */
+    volatile int busy;
+    /* Maximum number of busy channels */
+    volatile int max_busy;
+    /* Number of currently connected channels */
+    volatile int connected;
+    /* worker name */
+    char    name[JK_SHM_STR_SIZ+1];
+    /* route */
+    char    route[JK_SHM_STR_SIZ+1];
+    /* worker domain */
+    char    domain[JK_SHM_STR_SIZ+1];
+    /* worker redirect route */
+    char    redirect[JK_SHM_STR_SIZ+1];
+    /* worker distance */
+    volatile int distance;
+    /* current activation state (config) of the worker */
+    volatile int activation;
+    /* current error state (runtime) of the worker */
+    volatile int state;
+    /* Current lb factor */
+    volatile int lb_factor;
+    /* Current lb reciprocal factor */
+    volatile jk_uint64_t lb_mult;
+    /* Current lb value  */
+    volatile jk_uint64_t lb_value;
+    int     sticky_session;
+    int     sticky_session_force;
+    int     recover_wait_time;
+    int     retries;
+    int     lbmethod;
+    int     lblock;
+    unsigned int max_packet_size;
+    /* Statistical data */
+    volatile time_t  error_time;
+    /* Service transfer rate time */
+    volatile time_t  last_maintain_time;
+    /* Number of bytes read from remote */
+    volatile jk_uint64_t readed;
+    /* Number of bytes transferred to remote */
+    volatile jk_uint64_t transferred;
+    /* Number of times the worker was elected */
+    volatile jk_uint64_t  elected;
+    /* Number of times the worker was elected - snapshot during maintenance */
+    volatile jk_uint64_t  elected_snapshot;
+    /* Number of non 200 responses */
+    volatile jk_uint32_t  errors;
+    /* Number of recovery attempts */
+    volatile jk_uint32_t  recoveries;
+    /* Number of recovery failures */
+    volatile jk_uint32_t  recovery_errors;
+    /* Number of client errors */
+    volatile jk_uint32_t  client_errors;
+};
+typedef struct jk_shm_worker jk_shm_worker_t;
+
+const char *jk_shm_name(void);
+
+/* Open the shared memory creating file if needed
+ */
+int jk_shm_open(const char *fname, size_t sz, jk_logger_t *l);
+
+/* Close the shared memory
+ */
+void jk_shm_close(void);
+
+/* Attach the shared memory in child process.
+ * File has to be opened in parent.
+ */
+int jk_shm_attach(const char *fname, size_t sz, jk_logger_t *l);
+
+/* allocate shm memory
+ * If there is no shm present the pool will be used instead
+ */
+void *jk_shm_alloc(jk_pool_t *p, size_t size);
+
+/* allocate shm worker record
+ * If there is no shm present the pool will be used instead
+ */
+jk_shm_worker_t *jk_shm_alloc_worker(jk_pool_t *p);
+
+/* Return workers.properties last modified time
+ */
+time_t jk_shm_get_workers_time(void);
+
+/* Set workers.properties last modified time
+ */
+void jk_shm_set_workers_time(time_t t);
+
+/* Check if the shared memory has been modified
+ * by some other process.
+ */
+int jk_shm_is_modified(void);
+
+/* Synchronize access and modification time.
+ * This function should be called when the shared memory
+ * is modified and after we update the config acording
+ * to the current shared memory data.
+ */
+void jk_shm_sync_access_time(void);
+
+
+/* Lock shared memory for thread safe access */
+int jk_shm_lock(void);
+
+/* Unlock shared memory for thread safe access */
+int jk_shm_unlock(void);
+
+
+#ifdef __cplusplus
+}
+#endif  /* __cplusplus */
+#endif  /* _JK_SHM_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..7eef313
--- /dev/null
+++ b/connectors/jk/native/common/jk_sockbuf.c
@@ -0,0 +1,195 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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, jk_sock_t 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..d53a102
--- /dev/null
+++ b/connectors/jk/native/common/jk_sockbuf.h
@@ -0,0 +1,45 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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 int start;
+    unsigned int end;
+    jk_sock_t sd;
+};
+typedef struct jk_sockbuf jk_sockbuf_t;
+
+int jk_sb_open(jk_sockbuf_t *sb, jk_sock_t 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_status.c b/connectors/jk/native/common/jk_status.c
new file mode 100644
index 0000000..8a6f3f3
--- /dev/null
+++ b/connectors/jk/native/common/jk_status.c
@@ -0,0 +1,3629 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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: Status worker, display and manages JK workers              *
+ * Author:      Mladen Turk <mturk@jboss.com>                              *
+ * Author:      Rainer Jung <rjung@apache.org>                             *
+ * Version:     $Revision$                                        *
+ ***************************************************************************/
+
+#include "jk_pool.h"
+#include "jk_service.h"
+#include "jk_util.h"
+#include "jk_worker.h"
+#include "jk_status.h"
+#include "jk_mt.h"
+#include "jk_shm.h"
+#include "jk_ajp_common.h"
+#include "jk_lb_worker.h"
+#include "jk_ajp13_worker.h"
+#include "jk_ajp14_worker.h"
+#include "jk_connect.h"
+#include "jk_uri_worker_map.h"
+
+#define HUGE_BUFFER_SIZE (8*1024)
+
+/**
+ * Command line reference:
+ * cmd=list (default) display configuration
+ * cmd=show display detailed configuration
+ * cmd=edit form to change configuration
+ * cmd=update commit update configuration
+ * cmd=reset reset lb runtime states, or lb member runtime states
+ * cmd=version show only software version
+ * Query arguments:
+ * re=n (refresh time in seconds, n=0: disabled)
+ * w=worker (cmd should be executed for worker "worker")
+ * sw=sub_worker (cmd should be executed for "sub_worker" of worker "worker")
+ * from=lastcmd (the last viewing command was "lastcmd")
+ * opt=option (changes meaning of edit and list/show)
+ */
+
+#define JK_STATUS_ARG_CMD                  "cmd"
+#define JK_STATUS_ARG_MIME                 "mime"
+#define JK_STATUS_ARG_FROM                 "from"
+#define JK_STATUS_ARG_REFRESH              "re"
+#define JK_STATUS_ARG_WORKER               "w"
+#define JK_STATUS_ARG_SUB_WORKER           "sw"
+#define JK_STATUS_ARG_ATTRIBUTE            "att"
+#define JK_STATUS_ARG_MULT_VALUE_BASE      "val"
+#define JK_STATUS_ARG_OPTIONS              "opt"
+
+#define JK_STATUS_ARG_OPTION_NO_MEMBERS    0x0001
+#define JK_STATUS_ARG_OPTION_NO_MAPS       0x0002
+#define JK_STATUS_ARG_OPTION_NO_LEGEND     0x0004
+#define JK_STATUS_ARG_OPTION_NO_LB         0x0008
+#define JK_STATUS_ARG_OPTION_NO_AJP        0x0010
+#define JK_STATUS_ARG_OPTION_READ_ONLY     0x0020
+
+#define JK_STATUS_ARG_LB_RETRIES           ("lr")
+#define JK_STATUS_ARG_LB_RECOVER_TIME      ("lt")
+#define JK_STATUS_ARG_LB_STICKY            ("ls")
+#define JK_STATUS_ARG_LB_STICKY_FORCE      ("lf")
+#define JK_STATUS_ARG_LB_METHOD            ("lm")
+#define JK_STATUS_ARG_LB_LOCK              ("ll")
+
+#define JK_STATUS_ARG_LB_TEXT_RETRIES      "Retries"
+#define JK_STATUS_ARG_LB_TEXT_RECOVER_TIME "Recover Wait Time"
+#define JK_STATUS_ARG_LB_TEXT_STICKY       "Sticky Sessions"
+#define JK_STATUS_ARG_LB_TEXT_STICKY_FORCE "Force Sticky Sessions"
+#define JK_STATUS_ARG_LB_TEXT_METHOD       "LB Method"
+#define JK_STATUS_ARG_LB_TEXT_LOCK         "Locking"
+
+#define JK_STATUS_ARG_LBM_ACTIVATION       ("wa")
+#define JK_STATUS_ARG_LBM_FACTOR           ("wf")
+#define JK_STATUS_ARG_LBM_ROUTE            ("wn")
+#define JK_STATUS_ARG_LBM_REDIRECT         ("wr")
+#define JK_STATUS_ARG_LBM_DOMAIN           ("wc")
+#define JK_STATUS_ARG_LBM_DISTANCE         ("wd")
+
+#define JK_STATUS_ARG_LBM_TEXT_ACTIVATION  "Activation"
+#define JK_STATUS_ARG_LBM_TEXT_FACTOR      "LB Factor"
+#define JK_STATUS_ARG_LBM_TEXT_ROUTE       "Route"
+#define JK_STATUS_ARG_LBM_TEXT_REDIRECT    "Redirect Route"
+#define JK_STATUS_ARG_LBM_TEXT_DOMAIN      "Cluster Domain"
+#define JK_STATUS_ARG_LBM_TEXT_DISTANCE    "Distance"
+
+#define JK_STATUS_CMD_UNKNOWN              (0)
+#define JK_STATUS_CMD_LIST                 (1)
+#define JK_STATUS_CMD_SHOW                 (2)
+#define JK_STATUS_CMD_EDIT                 (3)
+#define JK_STATUS_CMD_UPDATE               (4)
+#define JK_STATUS_CMD_RESET                (5)
+#define JK_STATUS_CMD_VERSION              (6)
+#define JK_STATUS_CMD_RECOVER              (7)
+#define JK_STATUS_CMD_DEF                  (JK_STATUS_CMD_LIST)
+#define JK_STATUS_CMD_MAX                  (JK_STATUS_CMD_RECOVER)
+#define JK_STATUS_CMD_TEXT_UNKNOWN         ("unknown")
+#define JK_STATUS_CMD_TEXT_LIST            ("list")
+#define JK_STATUS_CMD_TEXT_SHOW            ("show")
+#define JK_STATUS_CMD_TEXT_EDIT            ("edit")
+#define JK_STATUS_CMD_TEXT_UPDATE          ("update")
+#define JK_STATUS_CMD_TEXT_RESET           ("reset")
+#define JK_STATUS_CMD_TEXT_VERSION         ("version")
+#define JK_STATUS_CMD_TEXT_RECOVER         ("recover")
+#define JK_STATUS_CMD_TEXT_DEF             (JK_STATUS_CMD_TEXT_LIST)
+
+#define JK_STATUS_MIME_UNKNOWN             (0)
+#define JK_STATUS_MIME_HTML                (1)
+#define JK_STATUS_MIME_XML                 (2)
+#define JK_STATUS_MIME_TXT                 (3)
+#define JK_STATUS_MIME_PROP                (4)
+#define JK_STATUS_MIME_DEF                 (JK_STATUS_MIME_HTML)
+#define JK_STATUS_MIME_MAX                 (JK_STATUS_MIME_PROP)
+#define JK_STATUS_MIME_TEXT_UNKNOWN        ("unknown")
+#define JK_STATUS_MIME_TEXT_HTML           ("html")
+#define JK_STATUS_MIME_TEXT_XML            ("xml")
+#define JK_STATUS_MIME_TEXT_TXT            ("txt")
+#define JK_STATUS_MIME_TEXT_PROP           ("prop")
+#define JK_STATUS_MIME_TEXT_DEF            (JK_STATUS_MIME_TEXT_HTML)
+
+#define JK_STATUS_MASK_ACTIVE              0x000000FF
+#define JK_STATUS_MASK_DISABLED            0x0000FF00
+#define JK_STATUS_MASK_STOPPED             0x00FF0000
+#define JK_STATUS_MASK_OK                  0x00010101
+#define JK_STATUS_MASK_NA                  0x00020202
+#define JK_STATUS_MASK_BUSY                0x00040404
+#define JK_STATUS_MASK_RECOVER             0x00080808
+#define JK_STATUS_MASK_ERROR               0x00101010
+#define JK_STATUS_MASK_GOOD_DEF            0x0000000F
+#define JK_STATUS_MASK_BAD_DEF             0x00FF1010
+
+#define JK_STATUS_WAIT_AFTER_UPDATE        "3"
+#define JK_STATUS_REFRESH_DEF              "10"
+#define JK_STATUS_ESC_CHARS                ("<>?&")
+
+#define JK_STATUS_HEAD                     "<!DOCTYPE HTML PUBLIC \"-//W3C//" \
+                                           "DTD HTML 3.2 Final//EN\">\n"      \
+                                           "<html><head><title>JK Status Manager</title>"
+
+#define JK_STATUS_COPYRIGHT                "Copyright &#169; 1999-2006, The Apache Software Foundation<br />" \
+                                           "Licensed under the <a href=\"http://www.apache.org/licenses/LICENSE-2.0\">" \
+                                           "Apache License, Version 2.0</a>."
+
+#define JK_STATUS_HEND                     "</head>\n<body>\n"
+#define JK_STATUS_BEND                     "</body>\n</html>\n"
+
+#define JK_STATUS_XMLH                     "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
+
+#define JK_STATUS_NS_DEF                   "jk:"
+#define JK_STATUS_XMLNS_DEF                "xmlns:jk=\"http://tomcat.apache.org\""
+#define JK_STATUS_PREFIX_DEF               "worker"
+
+#define JK_STATUS_FORM_START               "<form method=\"%s\" action=\"%s\">\n"
+#define JK_STATUS_FORM_HIDDEN_INT          "<input type=\"hidden\" name=\"%s\" value=\"%d\"/>\n"
+#define JK_STATUS_FORM_HIDDEN_STRING       "<input type=\"hidden\" name=\"%s\" value=\"%s\"/>\n"
+#define JK_STATUS_TABLE_HEAD_3_STRING      "<tr><th>%s</th><th>%s</th><th>%s</th></tr>\n"
+#define JK_STATUS_TABLE_ROW_3_STRING       "<tr><td>%s</td><td>%s</td><td>%s</td></tr>\n"
+#define JK_STATUS_SHOW_AJP_HEAD            "<tr>" \
+                                           "<th>Type</th>" \
+                                           "<th>Host</th>" \
+                                           "<th>Addr</th>" \
+                                           "</tr>\n"
+#define JK_STATUS_SHOW_AJP_ROW             "<tr>" \
+                                           "<td>%s</td>" \
+                                           "<td>%s:%d</td>" \
+                                           "<td>%s</td>" \
+                                           "</tr>\n"
+#define JK_STATUS_SHOW_LB_HEAD             "<tr>" \
+                                           "<th>Type</th>" \
+                                           "<th>" JK_STATUS_ARG_LB_TEXT_STICKY "</th>" \
+                                           "<th>" JK_STATUS_ARG_LB_TEXT_STICKY_FORCE "</th>" \
+                                           "<th>" JK_STATUS_ARG_LB_TEXT_RETRIES "</th>" \
+                                           "<th>" JK_STATUS_ARG_LB_TEXT_METHOD "</th>" \
+                                           "<th>" JK_STATUS_ARG_LB_TEXT_LOCK "</th>" \
+                                           "<th>" JK_STATUS_ARG_LB_TEXT_RECOVER_TIME "</th>" \
+                                           "</tr>\n"
+#define JK_STATUS_SHOW_LB_ROW              "<tr>" \
+                                           "<td>%s</td>" \
+                                           "<td>%s</td>" \
+                                           "<td>%s</td>" \
+                                           "<td>%d</td>" \
+                                           "<td>%s</td>" \
+                                           "<td>%s</td>" \
+                                           "<td>%d</td>" \
+                                           "</tr>\n"
+#define JK_STATUS_SHOW_MEMBER_HEAD         "<tr>" \
+                                           "<th>&nbsp;</th><th>Name</th><th>Type</th>" \
+                                           "<th>Host</th><th>Addr</th>" \
+                                           "<th>Act</th><th>Stat</th>" \
+                                           "<th>D</th><th>F</th><th>M</th>" \
+                                           "<th>V</th><th>Acc</th>" \
+                                           "<th>Err</th><th>CE</th>" \
+                                           "<th>Wr</th><th>Rd</th><th>Busy</th><th>Max</th>" \
+                                           "<th>" JK_STATUS_ARG_LBM_TEXT_ROUTE "</th>" \
+                                           "<th>RR</th><th>Cd</th><th>Rs</th>" \
+                                           "</tr>\n"
+#define JK_STATUS_SHOW_MEMBER_ROW          "<td>%s</td>" \
+                                           "<td>%s</td>" \
+                                           "<td>%s:%d</td>" \
+                                           "<td>%s</td>" \
+                                           "<td>%s</td>" \
+                                           "<td>%s</td>" \
+                                           "<td>%d</td>" \
+                                           "<td>%d</td>" \
+                                           "<td>%" JK_UINT64_T_FMT "</td>" \
+                                           "<td>%" JK_UINT64_T_FMT "</td>" \
+                                           "<td>%" JK_UINT64_T_FMT "</td>" \
+                                           "<td>%" JK_UINT32_T_FMT "</td>" \
+                                           "<td>%" JK_UINT32_T_FMT "</td>" \
+                                           "<td>%s</td>" \
+                                           "<td>%s</td>" \
+                                           "<td>%d</td>" \
+                                           "<td>%d</td>" \
+                                           "<td>%s</td>" \
+                                           "<td>%s</td>" \
+                                           "<td>%s</td>" \
+                                           "<td>%d/%d</td>" \
+                                           "</tr>\n"
+
+typedef struct status_worker status_worker_t;
+
+struct status_endpoint
+{
+    status_worker_t *worker;
+
+    jk_map_t        *req_params;
+    char            *msg;
+
+    jk_endpoint_t   endpoint;
+
+};
+
+typedef struct status_endpoint status_endpoint_t;
+
+struct status_worker
+{
+    jk_pool_t         p;
+    jk_pool_atom_t    buf[TINY_POOL_SIZE];
+    const char        *name;
+    const char        *css;
+    const char        *ns;
+    const char        *xmlns;
+    const char        *doctype;
+    const char        *prefix;
+    int               read_only;
+    char              **user_names;
+    unsigned int      num_of_users;
+    int               user_case_insensitive;
+    jk_uint32_t       good_mask;
+    jk_uint32_t       bad_mask;
+    jk_worker_t       worker;
+    jk_worker_env_t   *we;
+};
+
+static const char *worker_type[] = {
+    "unknown",
+    "ajp12",
+    "ajp13",
+    "ajp14",
+    "jni",
+    "lb",
+    "status",
+    NULL
+};
+
+static const char *headers_names[] = {
+    "Content-Type",
+    "Cache-Control",
+    "Pragma",
+    NULL
+};
+
+static const char *cmd_type[] = {
+    JK_STATUS_CMD_TEXT_UNKNOWN,
+    JK_STATUS_CMD_TEXT_LIST,
+    JK_STATUS_CMD_TEXT_SHOW,
+    JK_STATUS_CMD_TEXT_EDIT,
+    JK_STATUS_CMD_TEXT_UPDATE,
+    JK_STATUS_CMD_TEXT_RESET,
+    JK_STATUS_CMD_TEXT_VERSION,
+    JK_STATUS_CMD_TEXT_RECOVER,
+    NULL
+};
+
+static const char *mime_type[] = {
+    JK_STATUS_MIME_TEXT_UNKNOWN,
+    JK_STATUS_MIME_TEXT_HTML,
+    JK_STATUS_MIME_TEXT_XML,
+    JK_STATUS_MIME_TEXT_TXT,
+    JK_STATUS_MIME_TEXT_PROP,
+    NULL
+};
+
+#define HEADERS_NO_CACHE "no-cache", "no-cache", NULL
+
+static const char *headers_vhtml[] = {
+    "text/html",
+    HEADERS_NO_CACHE
+};
+
+static const char *headers_vxml[] = {
+    "text/xml",
+    HEADERS_NO_CACHE
+};
+
+static const char *headers_vtxt[] = {
+    "text/plain",
+    HEADERS_NO_CACHE
+};
+
+static void jk_puts(jk_ws_service_t *s, const char *str)
+{
+    if (str)
+        s->write(s, str, (unsigned int)strlen(str));
+    else
+        s->write(s, "(null)", 6);
+}
+
+static void jk_putv(jk_ws_service_t *s, ...)
+{
+    va_list va;
+    const char *str;
+
+    va_start(va, s);
+    while (1) {
+        str = va_arg(va, const char *);
+        if (str == NULL)
+            break;
+        s->write(s, str, (unsigned int)strlen(str));
+    }
+    va_end(va);
+}
+
+static int jk_printf(jk_ws_service_t *s, const char *fmt, ...)
+{
+    int rc = 0;
+    va_list args;
+#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
+
+    if (!s || !fmt) {
+        return -1;
+    }
+    va_start(args, fmt);
+
+#ifdef NETWARE
+        buf = (char *)malloc(HUGE_BUFFER_SIZE);
+        if (NULL == buf)
+            return -1;
+#endif
+    rc = vsnprintf(buf, HUGE_BUFFER_SIZE, fmt, args);
+    va_end(args);
+    if (rc > 0)
+        s->write(s, buf, rc);
+#ifdef NETWARE
+        free(buf);
+#endif
+    return rc;
+}
+
+static void jk_print_xml_start_elt(jk_ws_service_t *s, status_worker_t *w,
+                                   int indentation, int close_tag,
+                                   const char *name)
+{
+    if (close_tag) {
+        jk_printf(s, "%*s<%s%s>\n", indentation, "", w->ns, name);
+    }
+    else {
+        jk_printf(s, "%*s<%s%s\n", indentation, "", w->ns, name);
+    }
+}
+
+static void jk_print_xml_close_elt(jk_ws_service_t *s, status_worker_t *w,
+                                   int indentation,
+                                   const char *name)
+{
+    jk_printf(s, "%*s</%s%s>\n", indentation, "", w->ns, name);
+}
+
+static void jk_print_xml_stop_elt(jk_ws_service_t *s,
+                                  int indentation, int close_tag)
+{
+    if (close_tag) {
+        jk_printf(s, "%*s/>\n", indentation, "");
+    }
+    else {
+        jk_printf(s, "%*s>\n", indentation, "");
+    }
+}
+
+static void jk_print_xml_att_string(jk_ws_service_t *s,
+                                    int indentation,
+                                    const char *key, const char *value)
+{
+    jk_printf(s, "%*s%s=\"%s\"\n", indentation, "", key, value ? value : "");
+}
+
+static void jk_print_xml_att_int(jk_ws_service_t *s,
+                                 int indentation,
+                                 const char *key, int value)
+{
+    jk_printf(s, "%*s%s=\"%d\"\n", indentation, "", key, value);
+}
+
+static void jk_print_xml_att_uint32(jk_ws_service_t *s,
+                                    int indentation,
+                                    const char *key, jk_uint32_t value)
+{
+    jk_printf(s, "%*s%s=\"%" JK_UINT32_T_FMT "\"\n", indentation, "", key, value);
+}
+
+static void jk_print_xml_att_uint64(jk_ws_service_t *s,
+                                    int indentation,
+                                    const char *key, jk_uint64_t value)
+{
+    jk_printf(s, "%*s%s=\"%" JK_UINT64_T_FMT "\"\n", indentation, "", key, value);
+}
+
+static void jk_print_prop_att_string(jk_ws_service_t *s, status_worker_t *w,
+                                     const char *name,
+                                     const char *key, const char *value)
+{
+    if (name) {
+        jk_printf(s, "%s.%s.%s=%s\n", w->prefix, name, key, value ? value : "");
+    }
+    else {
+        jk_printf(s, "%s.%s=%s\n", w->prefix, key, value ? value : "");
+    }
+}
+
+static void jk_print_prop_att_int(jk_ws_service_t *s, status_worker_t *w,
+                                  const char *name,
+                                  const char *key, int value)
+{
+    if (name) {
+        jk_printf(s, "%s.%s.%s=%d\n", w->prefix, name, key, value);
+    }
+    else {
+        jk_printf(s, "%s.%s=%d\n", w->prefix, key, value);
+    }
+}
+
+static void jk_print_prop_att_uint32(jk_ws_service_t *s, status_worker_t *w,
+                                     const char *name,
+                                     const char *key, jk_uint32_t value)
+{
+    if (name) {
+        jk_printf(s, "%s.%s.%s=%" JK_UINT32_T_FMT "\n", w->prefix, name, key, value);
+    }
+    else {
+        jk_printf(s, "%s.%s=%" JK_UINT32_T_FMT "\n", w->prefix, key, value);
+    }
+}
+
+static void jk_print_prop_att_uint64(jk_ws_service_t *s, status_worker_t *w,
+                                     const char *name,
+                                     const char *key, jk_uint64_t value)
+{
+    if (name) {
+        jk_printf(s, "%s.%s.%s=%" JK_UINT64_T_FMT "\n", w->prefix, name, key, value);
+    }
+    else {
+        jk_printf(s, "%s.%s=%" JK_UINT64_T_FMT "\n", w->prefix, key, value);
+    }
+}
+
+static void jk_print_prop_item_string(jk_ws_service_t *s, status_worker_t *w,
+                                      const char *name, const char *list, int num,
+                                      const char *key, const char *value)
+{
+    if (name) {
+        jk_printf(s, "%s.%s.%s.%d.%s=%s\n", w->prefix, name, list, num, key, value ? value : "");
+    }
+    else {
+        jk_printf(s, "%s.%s.%d.%s=%s\n", w->prefix, list, num, key, value ? value : "");
+    }
+}
+
+/* Actually APR's apr_strfsize */
+static char *status_strfsize(jk_uint64_t size, char *buf)
+{
+    const char ord[] = "KMGTPE";
+    const char *o = ord;
+    unsigned int remain, siz;
+
+    if (size < 973) {
+        if (sprintf(buf, "%3d ", (int) size) < 0)
+            return strcpy(buf, "****");
+        return buf;
+    }
+    do {
+        remain = (unsigned int)(size & 0x03FF);
+        size >>= 10;
+        if (size >= 973) {
+            ++o;
+            continue;
+        }
+        siz = (unsigned int)(size & 0xFFFF);
+        if (siz < 9 || (siz == 9 && remain < 973)) {
+            if ((remain = ((remain * 5) + 256) / 512) >= 10)
+                ++siz, remain = 0;
+            if (sprintf(buf, "%d.%d%c", siz, remain, *o) < 0)
+                return strcpy(buf, "****");
+            return buf;
+        }
+        if (remain >= 512)
+            ++siz;
+        if (sprintf(buf, "%3d%c", siz, *o) < 0)
+            return strcpy(buf, "****");
+        return buf;
+    } while (1);
+}
+
+static int status_rate(worker_record_t *wr, status_worker_t *w,
+                       jk_logger_t *l)
+{
+    jk_uint32_t mask = 0;
+    int activation = wr->s->activation;
+    int state = wr->s->state;
+    jk_uint32_t good = w->good_mask;
+    jk_uint32_t bad = w->bad_mask;
+    int rv = 0;
+
+    switch (activation)
+    {
+    case JK_LB_ACTIVATION_ACTIVE:
+        mask = JK_STATUS_MASK_ACTIVE;
+        break;
+    case JK_LB_ACTIVATION_DISABLED:
+        mask = JK_STATUS_MASK_DISABLED;
+        break;
+    case JK_LB_ACTIVATION_STOPPED:
+        mask = JK_STATUS_MASK_STOPPED;
+        break;
+    default:
+        jk_log(l, JK_LOG_WARNING,
+               "Status worker '%s' unknown activation type '%d'",
+               w->name, activation);
+    }
+    switch (state)
+    {
+    case JK_LB_STATE_OK:
+        mask &= JK_STATUS_MASK_OK;
+        break;
+    case JK_LB_STATE_NA:
+        mask &= JK_STATUS_MASK_NA;
+        break;
+    case JK_LB_STATE_BUSY:
+        mask &= JK_STATUS_MASK_BUSY;
+        break;
+    case JK_LB_STATE_ERROR:
+        mask &= JK_STATUS_MASK_ERROR;
+        break;
+    case JK_LB_STATE_RECOVER:
+        mask &= JK_STATUS_MASK_RECOVER;
+        break;
+    case JK_LB_STATE_FORCE:
+        mask &= JK_STATUS_MASK_RECOVER;
+        break;
+    case JK_LB_STATE_PROBE:
+        mask &= JK_STATUS_MASK_RECOVER;
+        break;
+    default:
+        jk_log(l, JK_LOG_WARNING,
+               "Status worker '%s' unknown state type '%d'",
+               w->name, state);
+    }
+    if (mask&bad)
+        rv = -1;
+    else if (mask&good)
+        rv = 1;
+    else
+        rv = 0;
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+               "Status worker '%s' rating of activation '%s' and state '%s' for good '%08" JK_UINT32_T_HEX_FMT
+               "' and bad '%08" JK_UINT32_T_HEX_FMT "' is %d",
+               w->name, jk_lb_get_activation(wr, l), jk_lb_get_state(wr, l),
+               good, bad, rv);
+    return rv;
+}
+
+static jk_uint32_t status_get_single_rating(const char rating, jk_logger_t *l)
+{
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+               "rating retrieval for '%c'",
+               rating);
+    switch (rating)
+    {
+    case 'A':
+    case 'a':
+        return JK_STATUS_MASK_ACTIVE;
+    case 'D':
+    case 'd':
+        return JK_STATUS_MASK_DISABLED;
+    case 'S':
+    case 's':
+        return JK_STATUS_MASK_STOPPED;
+    case 'O':
+    case 'o':
+        return JK_STATUS_MASK_OK;
+    case 'N':
+    case 'n':
+        return JK_STATUS_MASK_NA;
+    case 'B':
+    case 'b':
+        return JK_STATUS_MASK_BUSY;
+    case 'R':
+    case 'r':
+        return JK_STATUS_MASK_RECOVER;
+    case 'E':
+    case 'e':
+        return JK_STATUS_MASK_ERROR;
+    default:
+        jk_log(l, JK_LOG_WARNING,
+               "Unknown rating type '%c'",
+               rating);
+        return 0;
+    }
+}
+
+static jk_uint32_t status_get_rating(const char *rating,
+                                     jk_logger_t *l)
+{
+    int off = 0;
+    jk_uint32_t mask = 0;
+
+    while (rating[off] == ' ' || rating[off] == '\t' || rating[off] == '.') {
+        off++;
+    }
+    mask = status_get_single_rating(rating[off], l);
+    while (rating[off] != '\0' && rating[off] != '.') {
+        off++;
+    }
+    if (rating[off] == '.') {
+        off++;
+    }
+    if (rating[off] != '\0') {
+        mask &= status_get_single_rating(rating[off], l);
+    }
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+               "rating for '%s' is '%08" JK_UINT32_T_HEX_FMT "'",
+               rating, mask);
+    return mask;
+}
+
+static const char *status_worker_type(int t)
+{
+    if (t < 0 || t > 6)
+        t = 0;
+    return worker_type[t];
+}
+
+
+static int status_get_string(status_endpoint_t *p,
+                             const char *param,
+                             const char *def,
+                             const char **result,
+                             jk_logger_t *l)
+{
+    int rv;
+
+    *result = jk_map_get_string(p->req_params,
+                                param, NULL);
+    if (*result) {
+        rv = JK_TRUE;
+    }
+    else {
+        *result = def;
+        rv = JK_FALSE;
+    }
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+               "retrieved string arg '%s' as '%s'%s",
+               param, *result ? *result : "(null)",
+               rv == JK_FALSE ? " (default)" : "");
+    return rv;
+}
+
+static int status_get_int(status_endpoint_t *p,
+                          const char *param,
+                          int def,
+                          jk_logger_t *l)
+{
+    const char *arg;
+    int rv = def;
+
+    if (status_get_string(p, param, NULL, &arg, l) == JK_TRUE) {
+        rv = atoi(arg);
+    }
+    return rv;
+}
+
+static int status_get_bool(status_endpoint_t *p,
+                           const char *param,
+                           int def,
+                           jk_logger_t *l)
+{
+    const char *arg;
+
+    if (status_get_string(p, param, NULL, &arg, l) == JK_TRUE) {
+        return jk_get_bool_code(arg, def);
+    }
+    return def;
+}
+
+const char *status_cmd_text(int cmd)
+{
+    return cmd_type[cmd];
+}
+
+static int status_cmd_int(const char *cmd)
+{
+    if (!cmd)
+        return JK_STATUS_CMD_DEF;
+    if (!strcmp(cmd, JK_STATUS_CMD_TEXT_LIST))
+        return JK_STATUS_CMD_LIST;
+    else if (!strcmp(cmd, JK_STATUS_CMD_TEXT_SHOW))
+        return JK_STATUS_CMD_SHOW;
+    else if (!strcmp(cmd, JK_STATUS_CMD_TEXT_EDIT))
+        return JK_STATUS_CMD_EDIT;
+    else if (!strcmp(cmd, JK_STATUS_CMD_TEXT_UPDATE))
+        return JK_STATUS_CMD_UPDATE;
+    else if (!strcmp(cmd, JK_STATUS_CMD_TEXT_RESET))
+        return JK_STATUS_CMD_RESET;
+    else if (!strcmp(cmd, JK_STATUS_CMD_TEXT_VERSION))
+        return JK_STATUS_CMD_VERSION;
+    else if (!strcmp(cmd, JK_STATUS_CMD_TEXT_RECOVER))
+        return JK_STATUS_CMD_RECOVER;
+    return JK_STATUS_CMD_UNKNOWN;
+}
+
+const char *status_mime_text(int mime)
+{
+    return mime_type[mime];
+}
+
+static int status_mime_int(const char *mime)
+{
+    if (!mime)
+        return JK_STATUS_MIME_DEF;
+    if (!strcmp(mime, JK_STATUS_MIME_TEXT_HTML))
+        return JK_STATUS_MIME_HTML;
+    else if (!strcmp(mime, JK_STATUS_MIME_TEXT_XML))
+        return JK_STATUS_MIME_XML;
+    else if (!strcmp(mime, JK_STATUS_MIME_TEXT_TXT))
+        return JK_STATUS_MIME_TXT;
+    else if (!strcmp(mime, JK_STATUS_MIME_TEXT_PROP))
+        return JK_STATUS_MIME_PROP;
+    return JK_STATUS_MIME_UNKNOWN;
+}
+
+static void status_start_form(jk_ws_service_t *s,
+                              status_endpoint_t *p,
+                              const char *method,
+                              int cmd,
+                              jk_logger_t *l)
+{
+
+    int i;
+    int sz;
+    jk_map_t *m = p->req_params;
+
+    if (method)
+        jk_printf(s, JK_STATUS_FORM_START, method, s->req_uri);
+    else
+        return;
+    if (cmd != JK_STATUS_CMD_UNKNOWN) {
+        jk_printf(s, JK_STATUS_FORM_HIDDEN_STRING,
+                  JK_STATUS_ARG_CMD, status_cmd_text(cmd));
+    }
+
+    sz = jk_map_size(m);
+    for (i = 0; i < sz; i++) {
+        const char *k = jk_map_name_at(m, i);
+        const char *v = jk_map_value_at(m, i);
+        if (strcmp(k, JK_STATUS_ARG_CMD) || cmd == JK_STATUS_CMD_UNKNOWN) {
+            jk_printf(s, JK_STATUS_FORM_HIDDEN_STRING, k, v);
+        }
+    }
+}
+
+static void status_write_uri(jk_ws_service_t *s,
+                             status_endpoint_t *p,
+                             const char *text,
+                             int cmd, int mime,
+                             const char *worker, const char *sub_worker,
+                             unsigned int add_options, unsigned int rm_options,
+                             const char *attribute,
+                             jk_logger_t *l)
+{
+    int i;
+    int sz;
+    int started = 0;
+    int from;
+    int prev;
+    unsigned int opt = 0;
+    const char *arg;
+    jk_map_t *m = p->req_params;
+
+    if (text)
+        jk_puts(s, "<a href=\"");
+    jk_puts(s, s->req_uri);
+    status_get_string(p, JK_STATUS_ARG_FROM, NULL, &arg, l);
+    from = status_cmd_int(arg);
+    status_get_string(p, JK_STATUS_ARG_CMD, NULL, &arg, l);
+    prev = status_cmd_int(arg);
+    if (cmd == JK_STATUS_CMD_UNKNOWN) {
+        if (prev == JK_STATUS_CMD_UPDATE ||
+            prev == JK_STATUS_CMD_RESET ||
+            prev == JK_STATUS_CMD_RECOVER) {
+            cmd = from;
+        }
+    }
+    if (cmd != JK_STATUS_CMD_UNKNOWN) {
+        jk_printf(s, "%s%s=%s", started ? "&amp;" : "?",
+                  JK_STATUS_ARG_CMD, status_cmd_text(cmd));
+        if (cmd == JK_STATUS_CMD_EDIT ||
+            cmd == JK_STATUS_CMD_RESET ||
+            cmd == JK_STATUS_CMD_RECOVER) {
+            jk_printf(s, "%s%s=%s", "&amp;",
+                      JK_STATUS_ARG_FROM, status_cmd_text(prev));
+        }
+        started=1;
+    }
+    if (mime != JK_STATUS_MIME_UNKNOWN) {
+        jk_printf(s, "%s%s=%s", started ? "&amp;" : "?",
+                  JK_STATUS_ARG_MIME, status_mime_text(mime));
+        started=1;
+    }
+    if (worker && worker[0]) {
+        jk_printf(s, "%s%s=%s", started ? "&amp;" : "?",
+                  JK_STATUS_ARG_WORKER, worker);
+        started=1;
+    }
+    if (sub_worker && sub_worker[0]) {
+        jk_printf(s, "%s%s=%s", started ? "&amp;" : "?",
+                  JK_STATUS_ARG_SUB_WORKER, sub_worker);
+        started=1;
+    }
+    if (attribute && attribute[0]) {
+        jk_printf(s, "%s%s=%s", started ? "&amp;" : "?",
+                  JK_STATUS_ARG_ATTRIBUTE, attribute);
+        started=1;
+    }
+
+    sz = jk_map_size(m);
+    for (i = 0; i < sz; i++) {
+        const char *k = jk_map_name_at(m, i);
+        const char *v = jk_map_value_at(m, i);
+        if (!strcmp(k, JK_STATUS_ARG_CMD) && cmd != JK_STATUS_CMD_UNKNOWN) {
+            continue;
+        }
+        if (!strcmp(k, JK_STATUS_ARG_MIME) && mime != JK_STATUS_MIME_UNKNOWN) {
+            continue;
+        }
+        if (!strcmp(k, JK_STATUS_ARG_FROM)) {
+            continue;
+        }
+        if (!strcmp(k, JK_STATUS_ARG_WORKER) && worker) {
+            continue;
+        }
+        if (!strcmp(k, JK_STATUS_ARG_SUB_WORKER) && sub_worker) {
+            continue;
+        }
+        if (!strcmp(k, JK_STATUS_ARG_ATTRIBUTE) && attribute) {
+            continue;
+        }
+        if (!strcmp(k, JK_STATUS_ARG_ATTRIBUTE) && cmd != JK_STATUS_CMD_UPDATE && cmd != JK_STATUS_CMD_EDIT) {
+            continue;
+        }
+        if (!strncmp(k, JK_STATUS_ARG_MULT_VALUE_BASE, 3) && cmd != JK_STATUS_CMD_UPDATE) {
+            continue;
+        }
+        if (strlen(k) == 2 && (k[0] == 'l' || k[0] == 'w') && cmd != JK_STATUS_CMD_UPDATE) {
+            continue;
+        }
+        if (!strcmp(k, JK_STATUS_ARG_OPTIONS)) {
+            opt = atoi(v);
+            continue;
+        }
+        jk_printf(s, "%s%s=%s", started ? "&amp;" : "?", k, v);
+        started=1;
+    }
+    if (opt | add_options | rm_options)
+        jk_printf(s, "%s%s=%u", started ? "&amp;" : "?",
+                  JK_STATUS_ARG_OPTIONS, (opt | add_options) & ~rm_options);
+    if (text)
+        jk_putv(s, "\">", text, "</a>", NULL);
+}
+
+static int status_parse_uri(jk_ws_service_t *s,
+                            status_endpoint_t *p,
+                            jk_logger_t *l)
+{
+    jk_map_t *m;
+    status_worker_t *w = p->worker;
+#ifdef _REENTRANT
+    char *lasts;
+#endif
+    char *param;
+    char *query;
+
+    JK_TRACE_ENTER(l);
+
+    if (!jk_map_alloc(&(p->req_params))) {
+        jk_log(l, JK_LOG_ERROR,
+               "Status worker '%s' could not alloc map for request parameters",
+               w->name);
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    if (!s->query_string) {
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "Status worker '%s' query string is empty",
+                   w->name);
+        JK_TRACE_EXIT(l);
+        return JK_TRUE;
+    }
+    m = p->req_params;
+    query = jk_pool_strdup(s->pool, s->query_string);
+    if (!query) {
+        jk_log(l, JK_LOG_ERROR,
+               "Status worker '%s' could not copy string",
+               w->name);
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+#ifdef _REENTRANT
+    for (param = strtok_r(query, "&", &lasts);
+         param; param = strtok_r(NULL, "&", &lasts)) {
+#else
+    for (param = strtok(query, "&"); param; param = strtok(NULL, "&")) {
+#endif
+        char *key = jk_pool_strdup(s->pool, param);
+        char *value;
+        if (!key) {
+            jk_log(l, JK_LOG_ERROR,
+                   "Status worker '%s' could not copy string",
+                   w->name);
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+        value = strchr(key, '=');
+        if (value) {
+            char *off;
+            *value = '\0';
+            value++;
+    /* XXX Depending on the params values, we might need to trim and decode */
+    /* XXX For now we simply mask special chars with '@' to prevent cross code injection */
+            off = value;
+            while ((off = strpbrk(off, JK_STATUS_ESC_CHARS)))
+                off[0] = '@';
+            if (strlen(key)) {
+                if (JK_IS_DEBUG_LEVEL(l))
+                    jk_log(l, JK_LOG_DEBUG,
+                           "Status worker '%s' adding request param '%s' with value '%s'",
+                           w->name, key, value);
+                jk_map_put(m, key, value, NULL);
+            }
+        }
+    }
+
+    JK_TRACE_EXIT(l);
+    return JK_TRUE;
+}
+
+static int fetch_worker_and_sub_worker(status_endpoint_t *p,
+                                       const char *operation,
+                                       const char **worker,
+                                       const char **sub_worker,
+                                       jk_logger_t *l)
+{
+    status_worker_t *w = p->worker;
+
+    JK_TRACE_ENTER(l);
+    status_get_string(p, JK_STATUS_ARG_WORKER, NULL, worker, l);
+    status_get_string(p, JK_STATUS_ARG_SUB_WORKER, NULL, sub_worker, l);
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+               "Status worker '%s' %s worker '%s' sub worker '%s'",
+               w->name, operation,
+               *worker ? *worker : "(null)", *sub_worker ? *sub_worker : "(null)");
+    JK_TRACE_EXIT(l);
+    return JK_TRUE;
+}
+
+static int check_valid_lb(jk_ws_service_t *s,
+                          status_endpoint_t *p,
+                          jk_worker_t *jw,
+                          const char *worker,
+                          lb_worker_t **lbp,
+                          int implemented,
+                          jk_logger_t *l)
+{
+    status_worker_t *w = p->worker;
+
+    JK_TRACE_ENTER(l);
+    if (jw->type != JK_LB_WORKER_TYPE) {
+        if (implemented) {
+            jk_log(l, JK_LOG_WARNING,
+                   "Status worker '%s' worker type of worker '%s' has no sub workers",
+                   w->name, worker);
+            p->msg = "worker type has no sub workers";
+        }
+        else {
+            jk_log(l, JK_LOG_WARNING,
+                   "Status worker '%s' worker type of worker '%s' not implemented",
+                   w->name, worker);
+                   p->msg = "worker type not implemented";
+        }
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    *lbp = (lb_worker_t *)jw->worker_private;
+    if (!*lbp) {
+        jk_log(l, JK_LOG_WARNING,
+               "Status worker '%s' lb structure of worker '%s' is (null)",
+               w->name, worker);
+        p->msg = "lb structure is (null)";
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    p->msg = "OK";
+    JK_TRACE_EXIT(l);
+    return JK_TRUE;
+}
+
+static int search_worker(jk_ws_service_t *s,
+                         status_endpoint_t *p,
+                         jk_worker_t **jwp,
+                         const char *worker,
+                         jk_logger_t *l)
+{
+    status_worker_t *w = p->worker;
+
+    JK_TRACE_ENTER(l);
+    *jwp = NULL;
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+               "Status worker '%s' searching worker '%s'",
+               w->name, worker ? worker : "(null)");
+    if (!worker || !worker[0]) {
+        jk_log(l, JK_LOG_WARNING,
+               "Status worker '%s' NULL or EMPTY worker param",
+               w->name);
+        p->msg = "NULL or EMPTY worker param";
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    *jwp = wc_get_worker_for_name(worker, l);
+    if (!*jwp) {
+        jk_log(l, JK_LOG_WARNING,
+               "Status worker '%s' could not find worker '%s'",
+               w->name, worker);
+        p->msg = "Could not find given worker";
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    p->msg = "OK";
+    JK_TRACE_EXIT(l);
+    return JK_TRUE;
+}
+
+static int search_sub_worker(jk_ws_service_t *s,
+                             status_endpoint_t *p,
+                             jk_worker_t *jw,
+                             const char *worker,
+                             worker_record_t **wrp,
+                             const char *sub_worker,
+                             jk_logger_t *l)
+{
+    lb_worker_t *lb = NULL;
+    worker_record_t *wr = NULL;
+    status_worker_t *w = p->worker;
+    unsigned int i;
+
+    JK_TRACE_ENTER(l);
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+               "Status worker '%s' searching sub worker '%s' of worker '%s'",
+               w->name, sub_worker ? sub_worker : "(null)",
+               worker ? worker : "(null)");
+    if (!sub_worker || !sub_worker[0]) {
+        jk_log(l, JK_LOG_WARNING,
+               "Status worker '%s' NULL or EMPTY sub_worker param",
+               w->name);
+        p->msg = "NULL or EMPTY sub_worker param";
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    if (check_valid_lb(s, p, jw, worker, &lb, 1, l) == JK_FALSE) {
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    for (i = 0; i < (int)lb->num_of_workers; i++) {
+        wr = &(lb->lb_workers[i]);
+        if (strcmp(sub_worker, wr->s->name) == 0)
+            break;
+    }
+    *wrp = wr;
+    if (!wr || i == (int)lb->num_of_workers) {
+        jk_log(l, JK_LOG_WARNING,
+               "Status worker '%s' could not find sub worker '%s' of worker '%s'",
+               w->name, sub_worker, worker ? worker : "(null)");
+        p->msg = "could not find sub worker";
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    p->msg = "OK";
+    JK_TRACE_EXIT(l);
+    return JK_TRUE;
+}
+
+static int count_maps(jk_ws_service_t *s,
+                      const char *worker,
+                      jk_logger_t *l)
+{
+    unsigned int i;
+    int count=0;
+    jk_uri_worker_map_t *uw_map = s->uw_map;
+
+    JK_TRACE_ENTER(l);
+    for (i = 0; i < uw_map->size; i++) {
+        uri_worker_record_t *uwr = uw_map->maps[i];
+        if (strcmp(uwr->worker_name, worker)) {
+            continue;
+        }
+        count++;
+    }
+    JK_TRACE_EXIT(l);
+    return count;
+}
+
+static void display_maps(jk_ws_service_t *s,
+                         status_endpoint_t *p,
+                         const char *worker,
+                         jk_logger_t *l)
+{
+    char buf[64];
+    unsigned int i;
+    int mime;
+    unsigned int hide;
+    int count=0;
+    const char *arg;
+    status_worker_t *w = p->worker;
+    jk_uri_worker_map_t *uw_map = s->uw_map;
+
+    JK_TRACE_ENTER(l);
+    status_get_string(p, JK_STATUS_ARG_MIME, NULL, &arg, l);
+    mime = status_mime_int(arg);
+    hide = status_get_int(p, JK_STATUS_ARG_OPTIONS, 0, l) &
+                          JK_STATUS_ARG_OPTION_NO_MAPS;
+    count = count_maps(s, worker, l);
+
+    if (count) {
+
+        if (hide) {
+            if (mime == JK_STATUS_MIME_HTML) {
+                jk_puts(s, "<p>\n");
+                status_write_uri(s, p, "Show URI Mappings", JK_STATUS_CMD_UNKNOWN, JK_STATUS_MIME_UNKNOWN,
+                                 NULL, NULL, 0, JK_STATUS_ARG_OPTION_NO_MAPS, NULL, l);
+                jk_puts(s, "</p>\n");
+            }
+        }
+        else {
+            if (mime == JK_STATUS_MIME_HTML) {
+                jk_printf(s, "<hr/><h3>URI Mappings for %s (%d maps) [", worker, count);
+                status_write_uri(s, p, "Hide", JK_STATUS_CMD_UNKNOWN, JK_STATUS_MIME_UNKNOWN,
+                                 NULL, NULL, JK_STATUS_ARG_OPTION_NO_MAPS, 0, NULL, l);
+                jk_puts(s, "]</h3><table>\n");
+                jk_printf(s, JK_STATUS_TABLE_HEAD_3_STRING,
+                          "Match Type", "Uri", "Source");
+            }
+        }
+    }
+
+    if (hide) {
+        return;
+        JK_TRACE_EXIT(l);
+    }
+
+    count = 0;
+    for (i = 0; i < uw_map->size; i++) {
+        uri_worker_record_t *uwr = uw_map->maps[i];
+        if (strcmp(uwr->worker_name, worker)) {
+            continue;
+        }
+        count++;
+
+        if (mime == JK_STATUS_MIME_HTML) {
+            jk_printf(s, JK_STATUS_TABLE_ROW_3_STRING,
+                      uri_worker_map_get_match(uwr, buf, l), uwr->uri,
+                      uri_worker_map_get_source(uwr, l));
+        }
+        else if (mime == JK_STATUS_MIME_XML) {
+            jk_print_xml_start_elt(s, w, 6, 0, "map");
+            jk_print_xml_att_int(s, 8, "id", count);
+            jk_print_xml_att_string(s, 8, "type", uri_worker_map_get_match(uwr, buf, l));
+            jk_print_xml_att_string(s, 8, "uri", uwr->uri);
+            jk_print_xml_att_string(s, 8, "source", uri_worker_map_get_source(uwr, l));
+            jk_print_xml_stop_elt(s, 6, 1);
+        }
+        else if (mime == JK_STATUS_MIME_TXT) {
+            jk_puts(s, "Map:");
+            jk_printf(s, " id=%d", count);
+            jk_printf(s, " type=\"%s\"", uri_worker_map_get_match(uwr, buf, l));
+            jk_printf(s, " uri=\"%s\"", uwr->uri);
+            jk_printf(s, " source=\"%s\"", uri_worker_map_get_source(uwr, l));
+            jk_puts(s, "\n");
+        }
+        else if (mime == JK_STATUS_MIME_PROP) {
+            char *mount = jk_pool_alloc(s->pool, sizeof(char *) * (strlen(uwr->uri)+3));
+            char *off = mount;
+            if (uwr->match_type & MATCH_TYPE_DISABLED) {
+                *off = '-';
+                off++;
+            }
+            if (uwr->match_type & MATCH_TYPE_NO_MATCH) {
+                *off = '!';
+                off++;
+            }
+            strcpy(off, uwr->uri);
+            jk_print_prop_att_string(s, w, worker, "mount", mount);
+            jk_print_prop_item_string(s, w, worker, "map", count, "type", uri_worker_map_get_match(uwr, buf, l));
+            jk_print_prop_item_string(s, w, worker, "map", count, "uri", uwr->uri);
+            jk_print_prop_item_string(s, w, worker, "map", count, "source", uri_worker_map_get_source(uwr, l));
+        }
+    }
+    if (count) {
+        if (mime == JK_STATUS_MIME_HTML) {
+            jk_puts(s, "</table>\n");
+        }
+    }
+    else {
+        if (mime == JK_STATUS_MIME_HTML) {
+            jk_putv(s, "<hr/><h3>Warning: No URI Mappings defined for ",
+                    worker, " !</h3>\n", NULL);
+        }
+    }
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+               "Status worker '%s' displayed %d maps for worker '%s'",
+               w->name, count, worker);
+    JK_TRACE_EXIT(l);
+}
+
+static void display_worker_lb(jk_ws_service_t *s,
+                              status_endpoint_t *p,
+                              lb_worker_t *lb,
+                              jk_logger_t *l)
+{
+    char buf[32];
+    char buf_rd[32];
+    char buf_wr[32];
+    int cmd;
+    int mime;
+    int read_only = 0;
+    int single = 0;
+    unsigned int hide_members;
+    const char *arg;
+    time_t now = time(NULL);
+    unsigned int good = 0;
+    unsigned int degraded = 0;
+    unsigned int bad = 0;
+    int map_count;
+    int ms_min;
+    int ms_max;
+    unsigned int j;
+    const char *name = lb->s->name;
+    status_worker_t *w = p->worker;
+
+    JK_TRACE_ENTER(l);
+    status_get_string(p, JK_STATUS_ARG_CMD, NULL, &arg, l);
+    cmd = status_cmd_int(arg);
+    status_get_string(p, JK_STATUS_ARG_MIME, NULL, &arg, l);
+    mime = status_mime_int(arg);
+    hide_members = status_get_int(p, JK_STATUS_ARG_OPTIONS, 0, l) &
+                                JK_STATUS_ARG_OPTION_NO_MEMBERS;
+    if (w->read_only) {
+        read_only = 1;
+    }
+    else {
+        read_only = status_get_int(p, JK_STATUS_ARG_OPTIONS, 0, l) &
+                    JK_STATUS_ARG_OPTION_READ_ONLY;
+    }
+    if (cmd == JK_STATUS_CMD_SHOW) {
+        single = 1;
+    }
+
+    jk_shm_lock();
+    if (lb->sequence != lb->s->sequence)
+        jk_lb_pull(lb, l);
+    jk_shm_unlock();
+
+    for (j = 0; j < lb->num_of_workers; j++) {
+        worker_record_t *wr = &(lb->lb_workers[j]);
+        int rate;
+        rate = status_rate(wr, w, l);
+        if (rate > 0 )
+           good++;
+        else if (rate < 0 )
+           bad++;
+        else
+           degraded++;
+    }
+
+    map_count = count_maps(s, name, l);
+    ms_min = lb->maintain_time - (int)difftime(now, lb->s->last_maintain_time);
+    ms_max = ms_min + lb->maintain_time;
+    ms_min -= JK_LB_MAINTAIN_TOLERANCE;
+    if (ms_min < 0) {
+        ms_min = 0;
+    }
+    if (ms_max < 0) {
+        ms_max = 0;
+    }
+
+    if (mime == JK_STATUS_MIME_HTML) {
+
+        jk_puts(s, "<hr/><h3>[");
+        if (single) {
+            jk_puts(s, "S");
+        }
+        else {
+            status_write_uri(s, p, "S", JK_STATUS_CMD_SHOW, JK_STATUS_MIME_UNKNOWN,
+                             name, "", 0, 0, "", l);
+        }
+        if (!read_only) {
+            jk_puts(s, "|");
+            status_write_uri(s, p, "E", JK_STATUS_CMD_EDIT, JK_STATUS_MIME_UNKNOWN,
+                             name, "", 0, 0, "", l);
+            jk_puts(s, "|");
+            status_write_uri(s, p, "R", JK_STATUS_CMD_RESET, JK_STATUS_MIME_UNKNOWN,
+                             name, "", 0, 0, "", l);
+        }
+        jk_puts(s, "]&nbsp;&nbsp;");
+        jk_putv(s, "Worker Status for ", name, "</h3>\n", NULL);
+        jk_puts(s, "<table>" JK_STATUS_SHOW_LB_HEAD);
+        jk_printf(s, JK_STATUS_SHOW_LB_ROW,
+                  status_worker_type(JK_LB_WORKER_TYPE),
+                  jk_get_bool(lb->sticky_session),
+                  jk_get_bool(lb->sticky_session_force),
+                  lb->retries,
+                  jk_lb_get_method(lb, l),
+                  jk_lb_get_lock(lb, l),
+                  lb->recover_wait_time);
+        jk_puts(s, "</table>\n<br/>\n");
+
+        jk_puts(s, "<table><tr>"
+                "<th>Good</th><th>Degraded</th><th>Bad/Stopped</th><th>Busy</th><th>Max Busy</th><th>Next Maintenance</th>"
+                "</tr>\n<tr>");
+        jk_printf(s, "<td>%d</td>", good);
+        jk_printf(s, "<td>%d</td>", degraded);
+        jk_printf(s, "<td>%d</td>", bad);
+        jk_printf(s, "<td>%d</td>", lb->s->busy);
+        jk_printf(s, "<td>%d</td>", lb->s->max_busy);
+        jk_printf(s, "<td>%d/%d</td>", ms_min, ms_max);
+        jk_puts(s, "</tr>\n</table>\n\n");
+
+    }
+    else if (mime == JK_STATUS_MIME_XML) {
+
+        jk_print_xml_start_elt(s, w, 2, 0, "balancer");
+        jk_print_xml_att_string(s, 4, "name", name);
+        jk_print_xml_att_string(s, 4, "type", status_worker_type(JK_LB_WORKER_TYPE));
+        jk_print_xml_att_string(s, 4, "sticky_session", jk_get_bool(lb->sticky_session));
+        jk_print_xml_att_string(s, 4, "sticky_session_force", jk_get_bool(lb->sticky_session_force));
+        jk_print_xml_att_int(s, 4, "retries", lb->retries);
+        jk_print_xml_att_int(s, 4, "recover_time", lb->recover_wait_time);
+        jk_print_xml_att_string(s, 4, "method", jk_lb_get_method(lb, l));
+        jk_print_xml_att_string(s, 4, "lock", jk_lb_get_lock(lb, l));
+        jk_print_xml_att_int(s, 4, "member_count", lb->num_of_workers);
+        jk_print_xml_att_int(s, 4, "good", good);
+        jk_print_xml_att_int(s, 4, "degraded", degraded);
+        jk_print_xml_att_int(s, 4, "bad", bad);
+        jk_print_xml_att_int(s, 4, "busy", lb->s->busy);
+        jk_print_xml_att_int(s, 4, "max_busy", lb->s->max_busy);
+        jk_print_xml_att_int(s, 4, "map_count", map_count);
+        jk_print_xml_att_int(s, 4, "time_to_maintenance_min", ms_min);
+        jk_print_xml_att_int(s, 4, "time_to_maintenance_max", ms_max);
+        jk_print_xml_stop_elt(s, 2, 0);
+
+    }
+    else if (mime == JK_STATUS_MIME_TXT) {
+
+        jk_puts(s, "Balancer Worker:");
+        jk_printf(s, " name=%s", name);
+        jk_printf(s, " type=%s", status_worker_type(JK_LB_WORKER_TYPE));
+        jk_printf(s, " sticky_session=%s", jk_get_bool(lb->sticky_session));
+        jk_printf(s, " sticky_session_force=%s", jk_get_bool(lb->sticky_session_force));
+        jk_printf(s, " retries=%d", lb->retries);
+        jk_printf(s, " recover_time=%d", lb->recover_wait_time);
+        jk_printf(s, " method=%s", jk_lb_get_method(lb, l));
+        jk_printf(s, " lock=%s", jk_lb_get_lock(lb, l));
+        jk_printf(s, " member_count=%d", lb->num_of_workers);
+        jk_printf(s, " good=%d", good);
+        jk_printf(s, " degraded=%d", degraded);
+        jk_printf(s, " bad=%d", bad);
+        jk_printf(s, " busy=%d", lb->s->busy);
+        jk_printf(s, " max_busy=%d", lb->s->max_busy);
+        jk_printf(s, " map_count=%d", map_count);
+        jk_printf(s, " time_to_maintenance_min=%d", ms_min);
+        jk_printf(s, " time_to_maintenance_max=%d", ms_max);
+        jk_puts(s, "\n");
+
+    }
+    else if (mime == JK_STATUS_MIME_PROP) {
+
+        jk_print_prop_att_string(s, w, NULL, "list", name);
+        jk_print_prop_att_string(s, w, name, "type", status_worker_type(JK_LB_WORKER_TYPE));
+        jk_print_prop_att_string(s, w, name, "sticky_session", jk_get_bool(lb->sticky_session));
+        jk_print_prop_att_string(s, w, name, "sticky_session_force", jk_get_bool(lb->sticky_session_force));
+        jk_print_prop_att_int(s, w, name, "retries", lb->retries);
+        jk_print_prop_att_int(s, w, name, "recover_time", lb->recover_wait_time);
+        jk_print_prop_att_string(s, w, name, "method", jk_lb_get_method(lb, l));
+        jk_print_prop_att_string(s, w, name, "lock", jk_lb_get_lock(lb, l));
+        jk_print_prop_att_int(s, w, name, "member_count", lb->num_of_workers);
+        jk_print_prop_att_int(s, w, name, "good", good);
+        jk_print_prop_att_int(s, w, name, "degraded", degraded);
+        jk_print_prop_att_int(s, w, name, "bad", bad);
+        jk_print_prop_att_int(s, w, name, "busy", lb->s->busy);
+        jk_print_prop_att_int(s, w, name, "max_busy", lb->s->max_busy);
+        jk_print_prop_att_int(s, w, name, "map_count", map_count);
+        jk_print_prop_att_int(s, w, name, "time_to_maintenance_min", ms_min);
+        jk_print_prop_att_int(s, w, name, "time_to_maintenance_max", ms_max);
+
+    }
+
+    if (!hide_members) {
+
+        if (mime == JK_STATUS_MIME_HTML) {
+
+            jk_puts(s, "<h4>Balancer Members [");
+            if (single) {
+                status_write_uri(s, p, "Hide", JK_STATUS_CMD_SHOW, JK_STATUS_MIME_UNKNOWN,
+                                 NULL, NULL, JK_STATUS_ARG_OPTION_NO_MEMBERS, 0, "", l);
+            }
+            else {
+                status_write_uri(s, p, "Hide", JK_STATUS_CMD_LIST, JK_STATUS_MIME_UNKNOWN,
+                                 NULL, NULL, JK_STATUS_ARG_OPTION_NO_MEMBERS, 0, "", l);
+            }
+            jk_puts(s, "]</h4>\n");
+            jk_puts(s, "<table>" JK_STATUS_SHOW_MEMBER_HEAD);
+
+        }
+
+        for (j = 0; j < lb->num_of_workers; j++) {
+            worker_record_t *wr = &(lb->lb_workers[j]);
+            ajp_worker_t *a = (ajp_worker_t *)wr->w->worker_private;
+            int rs_min = 0;
+            int rs_max = 0;
+            if (wr->s->state == JK_LB_STATE_ERROR) {
+                rs_min = lb->recover_wait_time - (int)difftime(now, wr->s->error_time);
+                if (rs_min < 0) {
+                    rs_min = 0;
+                }
+                rs_max = rs_min + lb->maintain_time;
+                if (rs_min < ms_min) {
+                    rs_min = ms_min;
+                }
+            }
+
+            if (mime == JK_STATUS_MIME_HTML) {
+
+                jk_puts(s, "<tr>\n<td>");
+                if (!read_only) {
+                    jk_puts(s, "[");
+                    status_write_uri(s, p, "E", JK_STATUS_CMD_EDIT, JK_STATUS_MIME_UNKNOWN,
+                                     name, wr->s->name, 0, 0, "", l);
+                    jk_puts(s, "|");
+                    status_write_uri(s, p, "R", JK_STATUS_CMD_RESET, JK_STATUS_MIME_UNKNOWN,
+                                     name, wr->s->name, 0, 0, "", l);
+                    if (wr->s->state == JK_LB_STATE_ERROR) {
+                        jk_puts(s, "|");
+                        status_write_uri(s, p, "T", JK_STATUS_CMD_RECOVER, JK_STATUS_MIME_UNKNOWN,
+                                         name, wr->s->name, 0, 0, "", l);
+                    }
+                    jk_puts(s, "]");
+                }
+                jk_puts(s, "&nbsp;</td>");
+                jk_printf(s, JK_STATUS_SHOW_MEMBER_ROW,
+                          wr->s->name,
+                          status_worker_type(wr->w->type),
+                          a->host, a->port,
+                          jk_dump_hinfo(&a->worker_inet_addr, buf),
+                          jk_lb_get_activation(wr, l),
+                          jk_lb_get_state(wr, l),
+                          wr->s->distance,
+                          wr->s->lb_factor,
+                          wr->s->lb_mult,
+                          wr->s->lb_value,
+                          wr->s->elected,
+                          wr->s->errors,
+                          wr->s->client_errors,
+                          status_strfsize(wr->s->transferred, buf_wr),
+                          status_strfsize(wr->s->readed, buf_rd),
+                          wr->s->busy,
+                          wr->s->max_busy,
+                          wr->s->route,
+                          wr->s->redirect ? (*wr->s->redirect ? wr->s->redirect : "&nbsp;") : "&nbsp",
+                          wr->s->domain ? (*wr->s->domain ? wr->s->domain : "&nbsp;") : "&nbsp",
+                          rs_min,
+                          rs_max);
+
+            }
+            else if (mime == JK_STATUS_MIME_XML) {
+
+                jk_print_xml_start_elt(s, w, 6, 0, "member");
+                jk_print_xml_att_string(s, 8, "name", wr->s->name);
+                jk_print_xml_att_string(s, 8, "type", status_worker_type(wr->w->type));
+                jk_print_xml_att_string(s, 8, "host", a->host);
+                jk_print_xml_att_int(s, 8, "port", a->port);
+                jk_print_xml_att_string(s, 8, "address", jk_dump_hinfo(&a->worker_inet_addr, buf));
+                jk_print_xml_att_string(s, 8, "activation", jk_lb_get_activation(wr, l));
+                jk_print_xml_att_int(s, 8, "lbfactor", wr->s->lb_factor);
+                jk_print_xml_att_string(s, 8, "route", wr->s->route);
+                jk_print_xml_att_string(s, 8, "redirect", wr->s->redirect);
+                jk_print_xml_att_string(s, 8, "domain", wr->s->domain);
+                jk_print_xml_att_int(s, 8, "distance", wr->s->distance);
+                jk_print_xml_att_string(s, 8, "state", jk_lb_get_state(wr, l));
+                jk_print_xml_att_uint64(s, 8, "lbmult", wr->s->lb_mult);
+                jk_print_xml_att_uint64(s, 8, "lbvalue", wr->s->lb_value);
+                jk_print_xml_att_uint64(s, 8, "elected", wr->s->elected);
+                jk_print_xml_att_uint32(s, 8, "errors", wr->s->errors);
+                jk_print_xml_att_uint32(s, 8, "client_errors", wr->s->client_errors);
+                jk_print_xml_att_uint64(s, 8, "transferred", wr->s->transferred);
+                jk_print_xml_att_uint64(s, 8, "read", wr->s->readed);
+                jk_print_xml_att_int(s, 8, "busy", wr->s->busy);
+                jk_print_xml_att_int(s, 8, "max_busy", wr->s->max_busy);
+                jk_print_xml_att_int(s, 8, "time_to_recover_min", rs_min);
+                jk_print_xml_att_int(s, 8, "time_to_recover_max", rs_max);
+                /* Terminate the tag */
+                jk_print_xml_stop_elt(s, 6, 1);
+
+            }
+            else if (mime == JK_STATUS_MIME_TXT) {
+
+                jk_puts(s, "Member:");
+                jk_printf(s, " name=%s", wr->s->name);
+                jk_printf(s, " type=%s", status_worker_type(wr->w->type));
+                jk_printf(s, " host=%s", a->host);
+                jk_printf(s, " port=%d", a->port);
+                jk_printf(s, " address=%s", jk_dump_hinfo(&a->worker_inet_addr, buf));
+                jk_printf(s, " activation=%s", jk_lb_get_activation(wr, l));
+                jk_printf(s, " lbfactor=%d", wr->s->lb_factor);
+                jk_printf(s, " route=\"%s\"", wr->s->route ? wr->s->route : "");
+                jk_printf(s, " redirect=\"%s\"", wr->s->redirect ? wr->s->redirect : "");
+                jk_printf(s, " domain=\"%s\"", wr->s->domain ? wr->s->domain : "");
+                jk_printf(s, " distance=%d", wr->s->distance);
+                jk_printf(s, " state=%s", jk_lb_get_state(wr, l));
+                jk_printf(s, " lbmult=%" JK_UINT64_T_FMT, wr->s->lb_mult);
+                jk_printf(s, " lbvalue=%" JK_UINT64_T_FMT, wr->s->lb_value);
+                jk_printf(s, " elected=%" JK_UINT64_T_FMT, wr->s->elected);
+                jk_printf(s, " errors=%" JK_UINT32_T_FMT, wr->s->errors);
+                jk_printf(s, " client_errors=%" JK_UINT32_T_FMT, wr->s->client_errors);
+                jk_printf(s, " transferred=%" JK_UINT64_T_FMT, wr->s->transferred);
+                jk_printf(s, " read=%" JK_UINT64_T_FMT, wr->s->readed);
+                jk_printf(s, " busy=%d", wr->s->busy);
+                jk_printf(s, " max_busy=%d", wr->s->max_busy);
+                jk_printf(s, " time_to_recover_min=%d", rs_min);
+                jk_printf(s, " time_to_recover_max=%d", rs_max);
+                jk_puts(s, "\n");
+
+            }
+            else if (mime == JK_STATUS_MIME_PROP) {
+
+                jk_print_prop_att_string(s, w, name, "balance_workers", wr->s->name);
+                jk_print_prop_att_string(s, w, wr->s->name, "type", status_worker_type(wr->w->type));
+                jk_print_prop_att_string(s, w, wr->s->name, "host", a->host);
+                jk_print_prop_att_int(s, w, wr->s->name, "port", a->port);
+                jk_print_prop_att_string(s, w, wr->s->name, "address", jk_dump_hinfo(&a->worker_inet_addr, buf));
+                jk_print_prop_att_string(s, w, wr->s->name, "activation", jk_lb_get_activation(wr, l));
+                jk_print_prop_att_int(s, w, wr->s->name, "lbfactor", wr->s->lb_factor);
+                jk_print_prop_att_string(s, w, wr->s->name, "route", wr->s->route);
+                jk_print_prop_att_string(s, w, wr->s->name, "redirect", wr->s->redirect);
+                jk_print_prop_att_string(s, w, wr->s->name, "domain", wr->s->domain);
+                jk_print_prop_att_int(s, w, wr->s->name, "distance", wr->s->distance);
+                jk_print_prop_att_string(s, w, wr->s->name, "state", jk_lb_get_state(wr, l));
+                jk_print_prop_att_uint64(s, w, wr->s->name, "lbmult", wr->s->lb_mult);
+                jk_print_prop_att_uint64(s, w, wr->s->name, "lbvalue", wr->s->lb_value);
+                jk_print_prop_att_uint64(s, w, wr->s->name, "elected", wr->s->elected);
+                jk_print_prop_att_uint32(s, w, wr->s->name, "errors", wr->s->errors);
+                jk_print_prop_att_uint32(s, w, wr->s->name, "client_errors", wr->s->client_errors);
+                jk_print_prop_att_uint64(s, w, wr->s->name, "transferred", wr->s->transferred);
+                jk_print_prop_att_uint64(s, w, wr->s->name, "read", wr->s->readed);
+                jk_print_prop_att_int(s, w, wr->s->name, "busy", wr->s->busy);
+                jk_print_prop_att_int(s, w, wr->s->name, "max_busy", wr->s->max_busy);
+                jk_print_prop_att_int(s, w, wr->s->name, "time_to_recover_min", rs_min);
+                jk_print_prop_att_int(s, w, wr->s->name, "time_to_recover_max", rs_max);
+
+            }
+        }
+
+        if (mime == JK_STATUS_MIME_HTML) {
+
+            jk_puts(s, "</table><br/>\n");
+            if (!read_only) {
+                jk_puts(s, "<b>E</b>dit one attribute for all members: [");
+                status_write_uri(s, p, JK_STATUS_ARG_LBM_TEXT_ACTIVATION, JK_STATUS_CMD_EDIT, JK_STATUS_MIME_UNKNOWN,
+                                 name, "", 0, 0, JK_STATUS_ARG_LBM_ACTIVATION, l);
+                jk_puts(s, "\n|");
+                status_write_uri(s, p, JK_STATUS_ARG_LBM_TEXT_FACTOR, JK_STATUS_CMD_EDIT, JK_STATUS_MIME_UNKNOWN,
+                                 name, "", 0, 0, JK_STATUS_ARG_LBM_FACTOR, l);
+                jk_puts(s, "\n|");
+                status_write_uri(s, p, JK_STATUS_ARG_LBM_TEXT_ROUTE, JK_STATUS_CMD_EDIT, JK_STATUS_MIME_UNKNOWN,
+                                 name, "", 0, 0, JK_STATUS_ARG_LBM_ROUTE, l);
+                jk_puts(s, "\n|");
+                status_write_uri(s, p, JK_STATUS_ARG_LBM_TEXT_REDIRECT, JK_STATUS_CMD_EDIT, JK_STATUS_MIME_UNKNOWN,
+                                 name, "", 0, 0, JK_STATUS_ARG_LBM_REDIRECT, l);
+                jk_puts(s, "\n|");
+                status_write_uri(s, p, JK_STATUS_ARG_LBM_TEXT_DOMAIN, JK_STATUS_CMD_EDIT, JK_STATUS_MIME_UNKNOWN,
+                                 name, "", 0, 0, JK_STATUS_ARG_LBM_DOMAIN, l);
+                jk_puts(s, "\n|");
+                status_write_uri(s, p, JK_STATUS_ARG_LBM_TEXT_DISTANCE, JK_STATUS_CMD_EDIT, JK_STATUS_MIME_UNKNOWN,
+                                 name, "", 0, 0, JK_STATUS_ARG_LBM_DISTANCE, l);
+                jk_puts(s, "\n]<br/>\n");
+            }
+
+        }
+
+    }
+    else {
+
+        if (mime == JK_STATUS_MIME_HTML) {
+
+            jk_puts(s, "<p>\n");
+            if (single) {
+                status_write_uri(s, p, "Show Balancer Members", JK_STATUS_CMD_SHOW, JK_STATUS_MIME_UNKNOWN,
+                                 NULL, NULL, 0, JK_STATUS_ARG_OPTION_NO_MEMBERS, "", l);
+            }
+            else {
+                status_write_uri(s, p, "Show Balancer Members", JK_STATUS_CMD_LIST, JK_STATUS_MIME_UNKNOWN,
+                                 NULL, NULL, 0, JK_STATUS_ARG_OPTION_NO_MEMBERS, "", l);
+            }
+            jk_puts(s, "</p>\n");
+        }
+
+    }
+
+    if (name)
+        display_maps(s, p, name, l);
+
+    if (mime == JK_STATUS_MIME_XML) {
+        jk_print_xml_close_elt(s, w, 2, "balancer");
+    }
+
+    JK_TRACE_EXIT(l);
+}
+
+static void display_worker_ajp(jk_ws_service_t *s,
+                               status_endpoint_t *p,
+                               ajp_worker_t *aw,
+                               jk_logger_t *l)
+{
+    char buf[32];
+    int cmd;
+    int mime;
+    int single = 0;
+    const char *arg;
+    int map_count;
+    const char *name = aw->name;
+    status_worker_t *w = p->worker;
+
+    JK_TRACE_ENTER(l);
+    status_get_string(p, JK_STATUS_ARG_CMD, NULL, &arg, l);
+    cmd = status_cmd_int(arg);
+    status_get_string(p, JK_STATUS_ARG_MIME, NULL, &arg, l);
+    mime = status_mime_int(arg);
+    if (cmd == JK_STATUS_CMD_SHOW) {
+        single = 1;
+    }
+
+    map_count = count_maps(s, name, l);
+
+    if (mime == JK_STATUS_MIME_HTML) {
+
+        jk_puts(s, "<hr/><h3>[");
+        if (single)
+            jk_puts(s, "S");
+        else
+            status_write_uri(s, p, "S", JK_STATUS_CMD_SHOW, JK_STATUS_MIME_UNKNOWN,
+                             name, "", 0, 0, "", l);
+        jk_puts(s, "]&nbsp;&nbsp;");
+        jk_putv(s, "Worker Status for ", name, "</h3>\n", NULL);
+        jk_puts(s, "<table>" JK_STATUS_SHOW_AJP_HEAD);
+        jk_printf(s, JK_STATUS_SHOW_AJP_ROW,
+                  status_worker_type(aw->worker.type),
+                  aw->host, aw->port,
+                  jk_dump_hinfo(&aw->worker_inet_addr, buf));
+        jk_puts(s, "</table>\n");
+
+    }
+    else if (mime == JK_STATUS_MIME_XML) {
+
+        jk_print_xml_start_elt(s, w, 0, 0, "ajp");
+        jk_print_xml_att_string(s, 2, "name", name);
+        jk_print_xml_att_string(s, 2, "type", status_worker_type(aw->worker.type));
+        jk_print_xml_att_string(s, 2, "host", aw->host);
+        jk_print_xml_att_int(s, 2, "port", aw->port);
+        jk_print_xml_att_string(s, 2, "address", jk_dump_hinfo(&aw->worker_inet_addr, buf));
+        jk_print_xml_att_int(s, 2, "map_count", map_count);
+        /* Terminate the tag */
+        jk_print_xml_stop_elt(s, 0, 0);
+
+    }
+    else if (mime == JK_STATUS_MIME_TXT) {
+
+        jk_puts(s, "AJP Worker:");
+        jk_printf(s, " name=%s", name);
+        jk_printf(s, " type=%s", status_worker_type(aw->worker.type));
+        jk_printf(s, " host=%s", aw->host);
+        jk_printf(s, " port=%d", aw->port);
+        jk_printf(s, " address=%s", jk_dump_hinfo(&aw->worker_inet_addr, buf));
+        jk_printf(s, " map_count=%d", map_count);
+        jk_puts(s, "\n");
+
+    }
+    else if (mime == JK_STATUS_MIME_PROP) {
+
+        jk_print_prop_att_string(s, w, NULL, "list", name);
+        jk_print_prop_att_string(s, w, name, "type", status_worker_type(aw->worker.type));
+        jk_print_prop_att_string(s, w, name, "host", aw->host);
+        jk_print_prop_att_int(s, w, name, "port", aw->port);
+        jk_print_prop_att_string(s, w, name, "address", jk_dump_hinfo(&aw->worker_inet_addr, buf));
+        jk_print_prop_att_int(s, w, name, "map_count", map_count);
+
+    }
+    if (name)
+        display_maps(s, p, name, l);
+
+    if (mime == JK_STATUS_MIME_XML) {
+        jk_print_xml_close_elt(s, w, 0, "ajp");
+    }
+
+    JK_TRACE_EXIT(l);
+}
+
+static void display_worker(jk_ws_service_t *s,
+                           status_endpoint_t *p,
+                           jk_worker_t *jw,
+                           jk_logger_t *l)
+{
+    status_worker_t *w = p->worker;
+
+    JK_TRACE_ENTER(l);
+    if (jw->type == JK_LB_WORKER_TYPE) {
+        lb_worker_t *lb = (lb_worker_t *)jw->worker_private;
+        if (lb) {
+            if (JK_IS_DEBUG_LEVEL(l))
+                jk_log(l, JK_LOG_DEBUG,
+                       "Status worker '%s' %s lb worker '%s'",
+                       w->name, "displaying", lb->s->name);
+            display_worker_lb(s, p, lb, l);
+        }
+        else {
+            jk_log(l, JK_LOG_WARNING,
+                   "Status worker '%s' lb worker is (null)",
+                   w->name);
+        }
+    }
+    else if (jw->type == JK_AJP13_WORKER_TYPE ||
+             jw->type == JK_AJP14_WORKER_TYPE) {
+        ajp_worker_t *aw = (ajp_worker_t *)jw->worker_private;
+        if (aw) {
+            if (JK_IS_DEBUG_LEVEL(l))
+                jk_log(l, JK_LOG_DEBUG,
+                       "Status worker '%s' %s ajp worker '%s'",
+                       w->name, "displaying", aw->name);
+            display_worker_ajp(s, p, aw, l);
+        }
+        else {
+            jk_log(l, JK_LOG_WARNING,
+                   "Status worker '%s' aw worker is (null)",
+                   w->name);
+        }
+    }
+    else {
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "Status worker '%s' worker type not implemented",
+                   w->name);
+        JK_TRACE_EXIT(l);
+        return;
+    }
+}
+
+static void form_worker(jk_ws_service_t *s,
+                        status_endpoint_t *p,
+                        jk_worker_t *jw,
+                        jk_logger_t *l)
+{
+    const char *name = NULL;
+    lb_worker_t *lb = NULL;
+    status_worker_t *w = p->worker;
+
+    JK_TRACE_ENTER(l);
+    if (jw->type == JK_LB_WORKER_TYPE) {
+        lb = (lb_worker_t *)jw->worker_private;
+        name = lb->s->name;
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "Status worker '%s' producing edit form for lb worker '%s'",
+                   w->name, name);
+    }
+    else {
+        jk_log(l, JK_LOG_WARNING,
+               "Status worker '%s' worker type not implemented",
+               w->name);
+        JK_TRACE_EXIT(l);
+        return;
+    }
+
+    if (!lb) {
+        jk_log(l, JK_LOG_WARNING,
+               "Status worker '%s' lb structure is (null)",
+               w->name);
+        JK_TRACE_EXIT(l);
+        return;
+    }
+
+    jk_shm_lock();
+    if (lb->sequence != lb->s->sequence)
+        jk_lb_pull(lb, l);
+    jk_shm_unlock();
+
+    jk_putv(s, "<hr/><h3>Edit load balancer settings for ",
+            name, "</h3>\n", NULL);
+
+    status_start_form(s, p, "GET", JK_STATUS_CMD_UPDATE, l);
+
+    jk_putv(s, "<table>\n<tr><td>", JK_STATUS_ARG_LB_TEXT_RETRIES,
+            ":</td><td><input name=\"",
+            JK_STATUS_ARG_LB_RETRIES, "\" type=\"text\" ", NULL);
+    jk_printf(s, "value=\"%d\"/></td></tr>\n", lb->retries);
+    jk_putv(s, "<tr><td>", JK_STATUS_ARG_LB_TEXT_RECOVER_TIME,
+            ":</td><td><input name=\"",
+            JK_STATUS_ARG_LB_RECOVER_TIME, "\" type=\"text\" ", NULL);
+    jk_printf(s, "value=\"%d\"/></td></tr>\n", lb->recover_wait_time);
+    jk_putv(s, "<tr><td>", JK_STATUS_ARG_LB_TEXT_STICKY,
+            ":</td><td><input name=\"",
+            JK_STATUS_ARG_LB_STICKY, "\" type=\"checkbox\"", NULL);
+    if (lb->sticky_session)
+        jk_puts(s, " checked=\"checked\"");
+    jk_puts(s, "/></td></tr>\n");
+    jk_putv(s, "<tr><td>", JK_STATUS_ARG_LB_TEXT_STICKY_FORCE,
+            ":</td><td><input name=\"",
+            JK_STATUS_ARG_LB_STICKY_FORCE, "\" type=\"checkbox\"", NULL);
+    if (lb->sticky_session_force)
+        jk_puts(s, " checked=\"checked\"");
+    jk_puts(s, "/></td></tr>\n");
+    jk_putv(s, "<tr><td>", JK_STATUS_ARG_LB_TEXT_METHOD,
+            ":</td><td></td></tr>\n", NULL);
+    jk_putv(s, "<tr><td>&nbsp;&nbsp;Requests</td><td><input name=\"",
+            JK_STATUS_ARG_LB_METHOD, "\" type=\"radio\"", NULL);
+    jk_printf(s, " value=\"%d\"", JK_LB_METHOD_REQUESTS);
+    if (lb->lbmethod == JK_LB_METHOD_REQUESTS)
+        jk_puts(s, " checked=\"checked\"");
+    jk_puts(s, "/></td></tr>\n");
+    jk_putv(s, "<tr><td>&nbsp;&nbsp;Traffic</td><td><input name=\"",
+            JK_STATUS_ARG_LB_METHOD, "\" type=\"radio\"", NULL);
+    jk_printf(s, " value=\"%d\"", JK_LB_METHOD_TRAFFIC);
+    if (lb->lbmethod == JK_LB_METHOD_TRAFFIC)
+        jk_puts(s, " checked=\"checked\"");
+    jk_puts(s, "/></td></tr>\n");
+    jk_putv(s, "<tr><td>&nbsp;&nbsp;Busyness</td><td><input name=\"",
+            JK_STATUS_ARG_LB_METHOD, "\" type=\"radio\"", NULL);
+    jk_printf(s, " value=\"%d\"", JK_LB_METHOD_BUSYNESS);
+    if (lb->lbmethod == JK_LB_METHOD_BUSYNESS)
+        jk_puts(s, " checked=\"checked\"");
+    jk_puts(s, "/></td></tr>\n");
+    jk_putv(s, "<tr><td>&nbsp;&nbsp;Sessions</td><td><input name=\"",
+            JK_STATUS_ARG_LB_METHOD, "\" type=\"radio\"", NULL);
+    jk_printf(s, " value=\"%d\"", JK_LB_METHOD_SESSIONS);
+    if (lb->lbmethod == JK_LB_METHOD_SESSIONS)
+        jk_puts(s, " checked=\"checked\"");
+    jk_puts(s, "/></td></tr>\n");
+    jk_putv(s, "<tr><td>", JK_STATUS_ARG_LB_TEXT_LOCK,
+            ":</td><td></td></tr>\n", NULL);
+    jk_putv(s, "<tr><td>&nbsp;&nbsp;Optimistic</td><td><input name=\"",
+            JK_STATUS_ARG_LB_LOCK, "\" type=\"radio\"", NULL);
+    jk_printf(s, " value=\"%d\"", JK_LB_LOCK_OPTIMISTIC);
+    if (lb->lblock == JK_LB_LOCK_OPTIMISTIC)
+        jk_puts(s, " checked=\"checked\"");
+    jk_puts(s, "/></td></tr>\n");
+    jk_putv(s, "<tr><td>&nbsp;&nbsp;Pessimistic</td><td><input name=\"",
+            JK_STATUS_ARG_LB_LOCK, "\" type=\"radio\"", NULL);
+    jk_printf(s, " value=\"%d\"", JK_LB_LOCK_PESSIMISTIC);
+    if (lb->lblock == JK_LB_LOCK_PESSIMISTIC)
+        jk_puts(s, " checked=\"checked\"");
+    jk_puts(s, "/></td></tr>\n");
+    jk_puts(s, "</table>\n");
+    jk_puts(s, "<br/><input type=\"submit\" value=\"Update Balancer\"/></form>\n");
+
+    JK_TRACE_EXIT(l);
+}
+
+static void form_member(jk_ws_service_t *s,
+                        status_endpoint_t *p,
+                        worker_record_t *wr,
+                        const char *lb_name,
+                        jk_logger_t *l)
+{
+    status_worker_t *w = p->worker;
+
+    JK_TRACE_ENTER(l);
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+               "Status worker '%s' producing edit form for sub worker '%s' of lb worker '%s'",
+               w->name, wr->s->name, lb_name);
+
+    jk_putv(s, "<hr/><h3>Edit worker settings for ",
+            wr->s->name, "</h3>\n", NULL);
+    status_start_form(s, p, "GET", JK_STATUS_CMD_UPDATE, l);
+
+    jk_puts(s, "<table>\n");
+    jk_putv(s, "<tr><td>", JK_STATUS_ARG_LBM_TEXT_ACTIVATION,
+            ":</td><td></td></tr>\n", NULL);
+    jk_putv(s, "<tr><td>&nbsp;&nbsp;Active</td><td><input name=\"",
+            JK_STATUS_ARG_LBM_ACTIVATION, "\" type=\"radio\"", NULL);
+    jk_printf(s, " value=\"%d\"", JK_LB_ACTIVATION_ACTIVE);
+    if (wr->s->activation == JK_LB_ACTIVATION_ACTIVE)
+        jk_puts(s, " checked=\"checked\"");
+    jk_puts(s, "/></td></tr>\n");
+    jk_putv(s, "<tr><td>&nbsp;&nbsp;Disabled</td><td><input name=\"",
+            JK_STATUS_ARG_LBM_ACTIVATION, "\" type=\"radio\"", NULL);
+    jk_printf(s, " value=\"%d\"", JK_LB_ACTIVATION_DISABLED);
+    if (wr->s->activation == JK_LB_ACTIVATION_DISABLED)
+        jk_puts(s, " checked=\"checked\"");
+    jk_puts(s, "/></td></tr>\n");
+    jk_putv(s, "<tr><td>&nbsp;&nbsp;Stopped</td><td><input name=\"",
+            JK_STATUS_ARG_LBM_ACTIVATION, "\" type=\"radio\"", NULL);
+    jk_printf(s, " value=\"%d\"", JK_LB_ACTIVATION_STOPPED);
+    if (wr->s->activation == JK_LB_ACTIVATION_STOPPED)
+        jk_puts(s, " checked=\"checked\"");
+    jk_puts(s, "/></td></tr>\n");
+    jk_putv(s, "<tr><td>", JK_STATUS_ARG_LBM_TEXT_FACTOR,
+            ":</td><td><input name=\"",
+            JK_STATUS_ARG_LBM_FACTOR, "\" type=\"text\" ", NULL);
+    jk_printf(s, "value=\"%d\"/></td></tr>\n", wr->s->lb_factor);
+    jk_putv(s, "<tr><td>", JK_STATUS_ARG_LBM_TEXT_ROUTE,
+            ":</td><td><input name=\"",
+            JK_STATUS_ARG_LBM_ROUTE, "\" type=\"text\" ", NULL);
+    jk_printf(s, "value=\"%s\"/></td></tr>\n", wr->s->route);
+    jk_putv(s, "<tr><td>", JK_STATUS_ARG_LBM_TEXT_REDIRECT,
+            ":</td><td><input name=\"",
+            JK_STATUS_ARG_LBM_REDIRECT, "\" type=\"text\" ", NULL);
+    jk_putv(s, "value=\"", wr->s->redirect, NULL);
+    jk_puts(s, "\"/></td></tr>\n");
+    jk_putv(s, "<tr><td>", JK_STATUS_ARG_LBM_TEXT_DOMAIN,
+            ":</td><td><input name=\"",
+            JK_STATUS_ARG_LBM_DOMAIN, "\" type=\"text\" ", NULL);
+    jk_putv(s, "value=\"", wr->s->domain, NULL);
+    jk_puts(s, "\"/></td></tr>\n");
+    jk_putv(s, "<tr><td>", JK_STATUS_ARG_LBM_TEXT_DISTANCE,
+            ":</td><td><input name=\"",
+            JK_STATUS_ARG_LBM_DISTANCE, "\" type=\"text\" ", NULL);
+    jk_printf(s, "value=\"%d\"/></td></tr>\n", wr->s->distance);
+    jk_puts(s, "</table>\n");
+    jk_puts(s, "<br/><input type=\"submit\" value=\"Update Worker\"/>\n</form>\n");
+    JK_TRACE_EXIT(l);
+}
+
+static void form_all_members(jk_ws_service_t *s,
+                             status_endpoint_t *p,
+                             jk_worker_t *jw,
+                             const char *attribute,
+                             jk_logger_t *l)
+{
+    const char *name = NULL;
+    lb_worker_t *lb = NULL;
+    status_worker_t *w = p->worker;
+    const char *aname;
+    unsigned int i;
+
+    JK_TRACE_ENTER(l);
+    if (!attribute) {
+        jk_log(l, JK_LOG_WARNING,
+               "Status worker '%s' missing request parameter '%s'",
+               w->name, JK_STATUS_ARG_ATTRIBUTE);
+        JK_TRACE_EXIT(l);
+        return;
+    }
+    else {
+        if (!strcmp(attribute, JK_STATUS_ARG_LBM_ACTIVATION))
+            aname=JK_STATUS_ARG_LBM_TEXT_ACTIVATION;
+        else if (!strcmp(attribute, JK_STATUS_ARG_LBM_FACTOR))
+            aname=JK_STATUS_ARG_LBM_TEXT_FACTOR;
+        else if (!strcmp(attribute, JK_STATUS_ARG_LBM_ROUTE))
+            aname=JK_STATUS_ARG_LBM_TEXT_ROUTE;
+        else if (!strcmp(attribute, JK_STATUS_ARG_LBM_REDIRECT))
+            aname=JK_STATUS_ARG_LBM_TEXT_REDIRECT;
+        else if (!strcmp(attribute, JK_STATUS_ARG_LBM_DOMAIN))
+            aname=JK_STATUS_ARG_LBM_TEXT_DOMAIN;
+        else if (!strcmp(attribute, JK_STATUS_ARG_LBM_DISTANCE))
+            aname=JK_STATUS_ARG_LBM_TEXT_DISTANCE;
+        else {
+            jk_log(l, JK_LOG_WARNING,
+                   "Status worker '%s' unknown attribute '%s'",
+                   w->name, attribute);
+            JK_TRACE_EXIT(l);
+            return;
+        }
+    }
+    if (jw->type == JK_LB_WORKER_TYPE) {
+        lb = (lb_worker_t *)jw->worker_private;
+        name = lb->s->name;
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "Status worker '%s' producing edit form for attribute '%s' [%s] of all members of lb worker '%s'",
+                   w->name, attribute, aname, name);
+    }
+    else {
+        jk_log(l, JK_LOG_WARNING,
+               "Status worker '%s' worker type not implemented",
+               w->name);
+        JK_TRACE_EXIT(l);
+        return;
+    }
+
+    if (lb) {
+        jk_putv(s, "<hr/><h3>Edit attribute '", aname,
+                "' for all members of load balancer ",
+                name, "</h3>\n", NULL);
+
+        status_start_form(s, p, "GET", JK_STATUS_CMD_UPDATE, l);
+
+        jk_putv(s, "<table><tr>"
+                "<th>Balanced Worker</th><th>", aname, "</th>"
+                "</tr>", NULL);
+
+        for (i = 0; i < lb->num_of_workers; i++) {
+            worker_record_t *wr = &(lb->lb_workers[i]);
+
+            jk_putv(s, "<tr><td>", wr->s->name, "</td><td>\n", NULL);
+
+            if (!strcmp(attribute, JK_STATUS_ARG_LBM_ACTIVATION)) {
+
+                jk_printf(s, "Active:&nbsp;<input name=\"" JK_STATUS_ARG_MULT_VALUE_BASE "%d\" type=\"radio\"", i);
+                jk_printf(s, " value=\"%d\"", JK_LB_ACTIVATION_ACTIVE);
+                if (wr->s->activation == JK_LB_ACTIVATION_ACTIVE)
+                    jk_puts(s, " checked=\"checked\"");
+                jk_puts(s, "/>&nbsp;|&nbsp;\n");
+                jk_printf(s, "Disabled:&nbsp;<input name=\"" JK_STATUS_ARG_MULT_VALUE_BASE "%d\" type=\"radio\"", i);
+                jk_printf(s, " value=\"%d\"", JK_LB_ACTIVATION_DISABLED);
+                if (wr->s->activation == JK_LB_ACTIVATION_DISABLED)
+                    jk_puts(s, " checked=\"checked\"");
+                jk_puts(s, "/>&nbsp;|&nbsp;\n");
+                jk_printf(s, "Stopped:&nbsp;<input name=\"" JK_STATUS_ARG_MULT_VALUE_BASE "%d\" type=\"radio\"", i);
+                jk_printf(s, " value=\"%d\"", JK_LB_ACTIVATION_STOPPED);
+                if (wr->s->activation == JK_LB_ACTIVATION_STOPPED)
+                    jk_puts(s, " checked=\"checked\"");
+                jk_puts(s, "/>\n");
+
+            }
+            else if (!strcmp(attribute, JK_STATUS_ARG_LBM_FACTOR)) {
+                jk_printf(s, "<input name=\"" JK_STATUS_ARG_MULT_VALUE_BASE "%d\" type=\"text\"", i);
+                jk_printf(s, "value=\"%d\"/>\n", wr->s->lb_factor);
+            }
+            else if (!strcmp(attribute, JK_STATUS_ARG_LBM_ROUTE)) {
+                jk_printf(s, "<input name=\"" JK_STATUS_ARG_MULT_VALUE_BASE "%d\" type=\"text\"", i);
+                jk_putv(s, "value=\"", wr->s->route, "\"/>\n", NULL);
+            }
+            else if (!strcmp(attribute, JK_STATUS_ARG_LBM_REDIRECT)) {
+                jk_printf(s, "<input name=\"" JK_STATUS_ARG_MULT_VALUE_BASE "%d\" type=\"text\"", i);
+                jk_putv(s, "value=\"", wr->s->redirect, "\"/>\n", NULL);
+            }
+            else if (!strcmp(attribute, JK_STATUS_ARG_LBM_DOMAIN)) {
+                jk_printf(s, "<input name=\"" JK_STATUS_ARG_MULT_VALUE_BASE "%d\" type=\"text\"", i);
+                jk_putv(s, "value=\"", wr->s->domain, "\"/>\n", NULL);
+            }
+            else if (!strcmp(attribute, JK_STATUS_ARG_LBM_DISTANCE)) {
+                jk_printf(s, "<input name=\"" JK_STATUS_ARG_MULT_VALUE_BASE "%d\" type=\"text\"", i);
+                jk_printf(s, "value=\"%d\"/>\n", wr->s->distance);
+            }
+
+            jk_puts(s, "</td></tr>");
+        }
+
+        jk_puts(s, "</table>\n");
+        jk_puts(s, "<br/><input type=\"submit\" value=\"Update Balancer\"/></form>\n");
+    }
+    JK_TRACE_EXIT(l);
+}
+
+static void commit_worker(jk_ws_service_t *s,
+                          status_endpoint_t *p,
+                          jk_worker_t *jw,
+                          jk_logger_t *l)
+{
+    const char *name = NULL;
+    lb_worker_t *lb = NULL;
+    status_worker_t *w = p->worker;
+    const char *arg;
+    int i;
+
+    JK_TRACE_ENTER(l);
+    if (jw->type == JK_LB_WORKER_TYPE) {
+        lb = (lb_worker_t *)jw->worker_private;
+        name = lb->s->name;
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "Status worker '%s' committing changes for lb worker '%s'",
+                   w->name, name);
+    }
+    else {
+        jk_log(l, JK_LOG_WARNING,
+               "Status worker '%s' worker type not implemented",
+               w->name);
+        JK_TRACE_EXIT(l);
+        return;
+    }
+
+    if (!lb) {
+        jk_log(l, JK_LOG_WARNING,
+               "Status worker '%s' lb structure is (null)",
+               w->name);
+        JK_TRACE_EXIT(l);
+        return;
+    }
+
+    if (lb->sequence != lb->s->sequence)
+        jk_lb_pull(lb, l);
+
+    i = status_get_int(p, JK_STATUS_ARG_LB_RETRIES,
+                       lb->retries, l);
+    if (i != lb->retries && i > 0) {
+        jk_log(l, JK_LOG_INFO,
+               "Status worker '%s' setting 'retries' for lb worker '%s' to '%i'",
+               w->name, name, i);
+        lb->retries = i;
+    }
+    i = status_get_int(p, JK_STATUS_ARG_LB_RECOVER_TIME,
+                       lb->recover_wait_time, l);
+    if (i != lb->recover_wait_time && i > 0) {
+        jk_log(l, JK_LOG_INFO,
+               "Status worker '%s' setting 'recover_time' for lb worker '%s' to '%i'",
+               w->name, name, i);
+        lb->recover_wait_time = i;
+    }
+    i = status_get_bool(p, JK_STATUS_ARG_LB_STICKY, 0, l);
+    if (i != lb->sticky_session) {
+        jk_log(l, JK_LOG_INFO,
+               "Status worker '%s' setting 'sticky_session' for lb worker '%s' to '%i'",
+               w->name, name, i);
+        lb->sticky_session = i;
+    }
+    i = status_get_bool(p, JK_STATUS_ARG_LB_STICKY_FORCE, 0, l);
+    if (i != lb->sticky_session_force) {
+        jk_log(l, JK_LOG_INFO,
+               "Status worker '%s' setting 'sticky_session_force' for lb worker '%s' to '%i'",
+               w->name, name, i);
+        lb->sticky_session_force = i;
+    }
+    if (status_get_string(p, JK_STATUS_ARG_LB_METHOD, NULL, &arg, l) == JK_TRUE) {
+        i = jk_lb_get_method_code(arg);
+        if (i != lb->lbmethod && i >= 0 && i <= JK_LB_METHOD_MAX) {
+            lb->lbmethod = i;
+            jk_log(l, JK_LOG_INFO,
+                   "Status worker '%s' setting 'method' for lb worker '%s' to '%s'",
+                   w->name, name, jk_lb_get_method(lb, l));
+        }
+    }
+    if (status_get_string(p, JK_STATUS_ARG_LB_LOCK, NULL, &arg, l) == JK_TRUE) {
+        i = jk_lb_get_lock_code(arg);
+        if (i != lb->lblock && i >= 0 && i <= JK_LB_LOCK_MAX) {
+            lb->lblock = i;
+            jk_log(l, JK_LOG_INFO,
+                   "Status worker '%s' setting 'lock' for lb worker '%s' to '%s'",
+                   w->name, name, jk_lb_get_lock(lb, l));
+        }
+    }
+    lb->sequence++;
+    jk_lb_push(lb, l);
+}
+
+static int commit_member(jk_ws_service_t *s,
+                          status_endpoint_t *p,
+                          worker_record_t *wr,
+                          const char *lb_name,
+                          jk_logger_t *l)
+{
+    const char *arg;
+    status_worker_t *w = p->worker;
+    int rc = 0;
+    int rv;
+    int i;
+
+    JK_TRACE_ENTER(l);
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG,
+               "Status worker '%s' committing changes for sub worker '%s' of lb worker '%s'",
+               w->name, wr->s->name, lb_name);
+
+    if (status_get_string(p, JK_STATUS_ARG_LBM_ACTIVATION, NULL, &arg, l) == JK_TRUE) {
+        i = jk_lb_get_activation_code(arg);
+        if (i != wr->s->activation && i >= 0 && i <= JK_LB_ACTIVATION_MAX) {
+            wr->s->activation = i;
+            jk_log(l, JK_LOG_INFO,
+                   "Status worker '%s' setting 'activation' for sub worker '%s' of lb worker '%s' to '%s'",
+                   w->name, wr->s->name, lb_name, jk_lb_get_activation(wr, l));
+            rc |= 1;
+        }
+    }
+    i = status_get_int(p, JK_STATUS_ARG_LBM_FACTOR,
+                       wr->s->lb_factor, l);
+    if (i != wr->s->lb_factor && i > 0) {
+        jk_log(l, JK_LOG_INFO,
+               "Status worker '%s' setting 'lbfactor' for sub worker '%s' of lb worker '%s' to '%i'",
+               w->name, wr->s->name, lb_name, i);
+        wr->s->lb_factor = i;
+        /* Recalculate the load multiplicators wrt. lb_factor */
+        rc |= 2;
+    }
+    if ((rv = status_get_string(p, JK_STATUS_ARG_LBM_ROUTE,
+                                NULL, &arg, l)) == JK_TRUE) {
+        if (strncmp(wr->s->route, arg, JK_SHM_STR_SIZ)) {
+            jk_log(l, JK_LOG_INFO,
+                   "Status worker '%s' setting 'route' for sub worker '%s' of lb worker '%s' to '%s'",
+                   w->name, wr->s->name, lb_name, arg);
+            strncpy(wr->s->route, arg, JK_SHM_STR_SIZ);
+            if (!wr->s->domain[0]) {
+                char * id_domain = strchr(wr->s->route, '.');
+                if (id_domain) {
+                    *id_domain = '\0';
+                    strcpy(wr->s->domain, wr->s->route);
+                    *id_domain = '.';
+                }
+            }
+        }
+    }
+    if ((rv = status_get_string(p, JK_STATUS_ARG_LBM_REDIRECT,
+                                NULL, &arg, l)) == JK_TRUE) {
+        if (strncmp(wr->s->redirect, arg, JK_SHM_STR_SIZ)) {
+            jk_log(l, JK_LOG_INFO,
+                   "Status worker '%s' setting 'redirect' for sub worker '%s' of lb worker '%s' to '%s'",
+                   w->name, wr->s->name, lb_name, arg);
+            strncpy(wr->s->redirect, arg, JK_SHM_STR_SIZ);
+        }
+    }
+    if ((rv = status_get_string(p, JK_STATUS_ARG_LBM_DOMAIN,
+                                NULL, &arg, l)) == JK_TRUE) {
+        if (strncmp(wr->s->domain, arg, JK_SHM_STR_SIZ)) {
+            jk_log(l, JK_LOG_INFO,
+                   "Status worker '%s' setting 'domain' for sub worker '%s' of lb worker '%s' to '%s'",
+                   w->name, wr->s->name, lb_name, arg);
+            strncpy(wr->s->domain, arg, JK_SHM_STR_SIZ);
+        }
+    }
+    i = status_get_int(p, JK_STATUS_ARG_LBM_DISTANCE,
+                       wr->s->distance, l);
+    if (i != wr->s->distance && i > 0) {
+        jk_log(l, JK_LOG_INFO,
+               "Status worker '%s' setting 'distance' for sub worker '%s' of lb worker '%s' to '%i'",
+               w->name, wr->s->name, lb_name, i);
+        wr->s->distance = i;
+    }
+    return rc;
+}
+
+static void commit_all_members(jk_ws_service_t *s,
+                               status_endpoint_t *p,
+                               jk_worker_t *jw,
+                               const char *attribute,
+                               jk_logger_t *l)
+{
+    const char *arg;
+    char vname[32];
+    const char *name = NULL;
+    lb_worker_t *lb = NULL;
+    status_worker_t *w = p->worker;
+    const char *aname;
+    int i;
+    int rc = 0;
+    unsigned int j;
+
+    JK_TRACE_ENTER(l);
+    if (!attribute) {
+        jk_log(l, JK_LOG_WARNING,
+               "Status worker '%s' missing request parameter '%s'",
+               w->name, JK_STATUS_ARG_ATTRIBUTE);
+        JK_TRACE_EXIT(l);
+        return;
+    }
+    else {
+        if (!strcmp(attribute, JK_STATUS_ARG_LBM_ACTIVATION))
+            aname=JK_STATUS_ARG_LBM_TEXT_ACTIVATION;
+        else if (!strcmp(attribute, JK_STATUS_ARG_LBM_FACTOR))
+            aname=JK_STATUS_ARG_LBM_TEXT_FACTOR;
+        else if (!strcmp(attribute, JK_STATUS_ARG_LBM_ROUTE))
+            aname=JK_STATUS_ARG_LBM_TEXT_ROUTE;
+        else if (!strcmp(attribute, JK_STATUS_ARG_LBM_REDIRECT))
+            aname=JK_STATUS_ARG_LBM_TEXT_REDIRECT;
+        else if (!strcmp(attribute, JK_STATUS_ARG_LBM_DOMAIN))
+            aname=JK_STATUS_ARG_LBM_TEXT_DOMAIN;
+        else if (!strcmp(attribute, JK_STATUS_ARG_LBM_DISTANCE))
+            aname=JK_STATUS_ARG_LBM_TEXT_DISTANCE;
+        else {
+            jk_log(l, JK_LOG_WARNING,
+                   "Status worker '%s' unknown attribute '%s'",
+                   w->name, attribute);
+            JK_TRACE_EXIT(l);
+            return;
+        }
+    }
+    if (jw->type == JK_LB_WORKER_TYPE) {
+        lb = (lb_worker_t *)jw->worker_private;
+        name = lb->s->name;
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "Status worker '%s' committing changes for attribute '%s' [%s] of all members of lb worker '%s'",
+                   w->name, attribute, aname, name);
+    }
+    else {
+        jk_log(l, JK_LOG_WARNING,
+               "Status worker '%s' worker type not implemented",
+               w->name);
+        JK_TRACE_EXIT(l);
+        return;
+    }
+
+    if (lb) {
+        for (j = 0; j < lb->num_of_workers; j++) {
+            worker_record_t *wr = &(lb->lb_workers[j]);
+            snprintf(vname, 32-1, "" JK_STATUS_ARG_MULT_VALUE_BASE "%d", j);
+
+            if (!strcmp(attribute, JK_STATUS_ARG_LBM_FACTOR)) {
+                i = status_get_int(p, vname, wr->s->lb_factor, l);
+                if (i != wr->s->lb_factor && i > 0) {
+                    jk_log(l, JK_LOG_INFO,
+                           "Status worker '%s' setting 'lbfactor' for sub worker '%s' of lb worker '%s' to '%i'",
+                           w->name, wr->s->name, name, i);
+                    wr->s->lb_factor = i;
+                    rc = 2;
+                }
+            }
+            else if (!strcmp(attribute, JK_STATUS_ARG_LBM_DISTANCE)) {
+                i = status_get_int(p, vname, wr->s->distance, l);
+                if (i != wr->s->distance && i > 0) {
+                    jk_log(l, JK_LOG_INFO,
+                           "Status worker '%s' setting 'distance' for sub worker '%s' of lb worker '%s' to '%i'",
+                           w->name, wr->s->name, name, i);
+                    wr->s->lb_factor = i;
+                }
+            }
+            else {
+                int rv = status_get_string(p, vname, NULL, &arg, l);
+                if (!strcmp(attribute, JK_STATUS_ARG_LBM_ACTIVATION)) {
+                    if (rv == JK_TRUE) {
+                        i = jk_lb_get_activation_code(arg);
+                        if (i != wr->s->activation && i >= 0 && i <= JK_LB_ACTIVATION_MAX) {
+                            wr->s->activation = i;
+                            jk_log(l, JK_LOG_INFO,
+                                   "Status worker '%s' setting 'activation' for sub worker '%s' of lb worker '%s' to '%s'",
+                                   w->name, wr->s->name, name, jk_lb_get_activation(wr, l));
+                            rc = 1;
+                        }
+                    }
+                }
+                else if (!strcmp(attribute, JK_STATUS_ARG_LBM_ROUTE)) {
+                    if (rv == JK_TRUE) {
+                        if (strncmp(wr->s->route, arg, JK_SHM_STR_SIZ)) {
+                            jk_log(l, JK_LOG_INFO,
+                                   "Status worker '%s' setting 'route' for sub worker '%s' of lb worker '%s' to '%s'",
+                                   w->name, wr->s->name, name, arg);
+                            strncpy(wr->s->route, arg, JK_SHM_STR_SIZ);
+                            if (!wr->s->domain[0]) {
+                                char * id_domain = strchr(wr->s->route, '.');
+                                if (id_domain) {
+                                    *id_domain = '\0';
+                                    strcpy(wr->s->domain, wr->s->route);
+                                    *id_domain = '.';
+                                }
+                            }
+                        }
+                    }
+                }
+                else if (!strcmp(attribute, JK_STATUS_ARG_LBM_REDIRECT)) {
+                    if (rv == JK_TRUE) {
+                        if (strncmp(wr->s->redirect, arg, JK_SHM_STR_SIZ)) {
+                            jk_log(l, JK_LOG_INFO,
+                                   "Status worker '%s' setting 'redirect' for sub worker '%s' of lb worker '%s' to '%s'",
+                                   w->name, wr->s->name, name, arg);
+                            strncpy(wr->s->redirect, arg, JK_SHM_STR_SIZ);
+                        }
+                    }
+                }
+                else if (!strcmp(attribute, JK_STATUS_ARG_LBM_DOMAIN)) {
+                    if (rv == JK_TRUE) {
+                        if (strncmp(wr->s->domain, arg, JK_SHM_STR_SIZ)) {
+                            jk_log(l, JK_LOG_INFO,
+                                   "Status worker '%s' setting 'domain' for sub worker '%s' of lb worker '%s' to '%s'",
+                                   w->name, wr->s->name, name, arg);
+                            strncpy(wr->s->domain, arg, JK_SHM_STR_SIZ);
+                        }
+                    }
+                }
+            }
+        }
+        if (rc == 1)
+            reset_lb_values(lb, l);
+        else if (rc == 2)
+            /* Recalculate the load multiplicators wrt. lb_factor */
+            update_mult(lb, l);
+    }
+    JK_TRACE_EXIT(l);
+}
+
+static void display_legend(jk_ws_service_t *s,
+                           status_endpoint_t *p,
+                           jk_logger_t *l)
+{
+
+    int mime;
+    const char *arg;
+    unsigned int hide_legend;
+
+    JK_TRACE_ENTER(l);
+    status_get_string(p, JK_STATUS_ARG_MIME, NULL, &arg, l);
+    mime = status_mime_int(arg);
+    if (mime != JK_STATUS_MIME_HTML) {
+        JK_TRACE_EXIT(l);
+        return;
+    }
+    hide_legend = status_get_int(p, JK_STATUS_ARG_OPTIONS, 0, l) &
+                               JK_STATUS_ARG_OPTION_NO_LEGEND;
+    if (hide_legend) {
+        jk_puts(s, "<p>\n");
+        status_write_uri(s, p, "Show Legend", JK_STATUS_CMD_UNKNOWN, JK_STATUS_MIME_UNKNOWN,
+                         NULL, NULL, 0, JK_STATUS_ARG_OPTION_NO_LEGEND, NULL, l);
+        jk_puts(s, "</p>\n");
+    }
+    else {
+        jk_puts(s, "<h2>Legend [");
+        status_write_uri(s, p, "Hide", JK_STATUS_CMD_UNKNOWN, JK_STATUS_MIME_UNKNOWN,
+                         NULL, NULL, JK_STATUS_ARG_OPTION_NO_LEGEND, 0, NULL, l);
+        jk_puts(s, "]</h2>\n");
+
+        jk_puts(s, "<table>\n"
+            "<tbody valign=\"baseline\">\n"
+            "<tr><th>Name</th><td>Worker name</td></tr>\n"
+            "<tr><th>Type</th><td>Worker type</td></tr>\n"
+            "<tr><th>Route</th><td>Worker route</td></tr>\n"
+            "<tr><th>Addr</th><td>Backend Address info</td></tr>\n"
+            "<tr><th>Act</th><td>Worker activation configuration<br/>\n"
+            "ACT=Active, DIS=Disabled, STP=Stopped</td></tr>\n"
+            "<tr><th>Stat</th><td>Worker error status<br/>\n"
+            "OK=OK, N/A=Unknown, ERR=Error, BSY=Busy<br/>\n"
+            "REC=Recovering, PRB=Probing, FRC=Forced Recovery</td></tr>\n"
+            "<tr><th>D</th><td>Worker distance</td></tr>\n"
+            "<tr><th>F</th><td>Load Balancer factor</td></tr>\n"
+            "<tr><th>M</th><td>Load Balancer multiplicity</td></tr>\n"
+            "<tr><th>V</th><td>Load Balancer value</td></tr>\n"
+            "<tr><th>Acc</th><td>Number of requests</td></tr>\n"
+            "<tr><th>Err</th><td>Number of failed requests</td></tr>\n"
+            "<tr><th>CE</th><td>Number of client errors</td></tr>\n"
+            "<tr><th>Wr</th><td>Number of bytes transferred/min</td></tr>\n"
+            "<tr><th>Rd</th><td>Number of bytes read/min</td></tr>\n"
+            "<tr><th>Busy</th><td>Current number of busy connections</td></tr>\n"
+            "<tr><th>Max</th><td>Maximum number of busy connections</td></tr>\n"
+            "<tr><th>RR</th><td>Route redirect</td></tr>\n"
+            "<tr><th>Cd</th><td>Cluster domain</td></tr>\n"
+            "<tr><th>Rs</th><td>Recovery scheduled in app. min/max seconds</td></tr>\n"
+            "</tbody>\n"
+            "</table>\n");
+    }
+
+    JK_TRACE_EXIT(l);
+}
+
+static int check_worker(jk_ws_service_t *s,
+                        status_endpoint_t *p,
+                        jk_logger_t *l)
+{
+    const char *worker;
+    const char *sub_worker;
+    jk_worker_t *jw = NULL;
+    worker_record_t *wr = NULL;
+
+    JK_TRACE_ENTER(l);
+    fetch_worker_and_sub_worker(p, "checking", &worker, &sub_worker, l);
+    if (search_worker(s, p, &jw, worker, l) == JK_FALSE) {
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    if (sub_worker && sub_worker[0]) {
+        if(search_sub_worker(s, p, jw, worker, &wr, sub_worker, l) == JK_FALSE) {
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+    }
+
+    JK_TRACE_EXIT(l);
+    return JK_TRUE;
+}
+
+static void count_workers(jk_ws_service_t *s,
+                          status_endpoint_t *p,
+                          int *lb_cnt, int *ajp_cnt,
+                          jk_logger_t *l)
+{
+    unsigned int i;
+    jk_worker_t *jw = NULL;
+    status_worker_t *w = p->worker;
+
+    JK_TRACE_ENTER(l);
+    *lb_cnt = 0;
+    *ajp_cnt = 0;
+    for (i = 0; i < w->we->num_of_workers; i++) {
+        jw = wc_get_worker_for_name(w->we->worker_list[i], l);
+        if (!jw) {
+            jk_log(l, JK_LOG_WARNING,
+                   "Status worker '%s' could not find worker '%s'",
+                   w->name, w->we->worker_list[i]);
+            continue;
+        }
+        if (jw->type == JK_LB_WORKER_TYPE) {
+            (*lb_cnt)++;
+        }
+        else if (jw->type == JK_AJP13_WORKER_TYPE ||
+                 jw->type == JK_AJP14_WORKER_TYPE) {
+            (*ajp_cnt)++;
+        }
+    }
+    JK_TRACE_EXIT(l);
+}
+
+static void list_workers_type(jk_ws_service_t *s,
+                              status_endpoint_t *p,
+                              int list_lb, int count,
+                              jk_logger_t *l)
+{
+
+    const char *arg;
+    unsigned int i;
+    int mime;
+    unsigned int hide;
+    jk_worker_t *jw = NULL;
+    status_worker_t *w = p->worker;
+
+    JK_TRACE_ENTER(l);
+
+    status_get_string(p, JK_STATUS_ARG_MIME, NULL, &arg, l);
+    mime = status_mime_int(arg);
+    if (list_lb) {
+        hide = status_get_int(p, JK_STATUS_ARG_OPTIONS, 0, l) &
+                              JK_STATUS_ARG_OPTION_NO_LB;
+        if (hide) {
+            if (mime == JK_STATUS_MIME_HTML) {
+                jk_puts(s, "<p>\n");
+                status_write_uri(s, p, "Show Load Balancing Workers", JK_STATUS_CMD_UNKNOWN, JK_STATUS_MIME_UNKNOWN,
+                                 NULL, NULL, 0, JK_STATUS_ARG_OPTION_NO_LB, NULL, l);
+                jk_puts(s, "</p>\n");
+            }
+        }
+        else {
+            if (mime == JK_STATUS_MIME_XML) {
+                jk_print_xml_start_elt(s, w, 0, 0, "balancers");
+                jk_print_xml_att_int(s, 2, "count", count);
+                jk_print_xml_stop_elt(s, 0, 0);
+            }
+            else if (mime == JK_STATUS_MIME_TXT) {
+                jk_printf(s, "Balancer Workers: count=%d\n", count);
+            }
+            else if (mime == JK_STATUS_MIME_PROP) {
+                jk_print_prop_att_int(s, w, NULL, "lb_count", count);
+            }
+            else {
+                jk_printf(s, "<hr/><h2>Listing Load Balancing Worker%s (%d Worker%s) [",
+                          count>1 ? "s" : "", count, count>1 ? "s" : "");
+                status_write_uri(s, p, "Hide", JK_STATUS_CMD_UNKNOWN, JK_STATUS_MIME_UNKNOWN,
+                                 NULL, NULL, JK_STATUS_ARG_OPTION_NO_LB, 0, NULL, l);
+                jk_puts(s, "]</h2>\n");
+            }
+        }
+    }
+    else {
+        hide = status_get_int(p, JK_STATUS_ARG_OPTIONS, 0, l) &
+                              JK_STATUS_ARG_OPTION_NO_AJP;
+        if (hide) {
+            if (mime == JK_STATUS_MIME_HTML) {
+                jk_puts(s, "<p>\n");
+                status_write_uri(s, p, "Show AJP Workers", JK_STATUS_CMD_UNKNOWN, JK_STATUS_MIME_UNKNOWN,
+                                 NULL, NULL, 0, JK_STATUS_ARG_OPTION_NO_AJP, NULL, l);
+                jk_puts(s, "</p>\n");
+            }
+        }
+        else {
+            if (mime == JK_STATUS_MIME_XML) {
+                jk_print_xml_start_elt(s, w, 0, 0, "ajp_workers");
+                jk_print_xml_att_int(s, 2, "count", count);
+                jk_print_xml_stop_elt(s, 0, 0);
+            }
+            else if (mime == JK_STATUS_MIME_TXT) {
+                jk_printf(s, "AJP Workers: count=%d\n", count);
+            }
+            else if (mime == JK_STATUS_MIME_PROP) {
+                jk_print_prop_att_int(s, w, NULL, "ajp_count", count);
+            }
+            else {
+                jk_printf(s, "<hr/><h2>Listing AJP Worker%s (%d Worker%s) [",
+                          count>1 ? "s" : "", count, count>1 ? "s" : "");
+                status_write_uri(s, p, "Hide", JK_STATUS_CMD_UNKNOWN, JK_STATUS_MIME_UNKNOWN,
+                                 NULL, NULL, JK_STATUS_ARG_OPTION_NO_AJP, 0, NULL, l);
+                jk_puts(s, "]</h2>\n");
+            }
+        }
+    }
+
+    if (hide) {
+        JK_TRACE_EXIT(l);
+        return;
+    }
+
+    for (i = 0; i < w->we->num_of_workers; i++) {
+        jw = wc_get_worker_for_name(w->we->worker_list[i], l);
+        if (!jw) {
+            jk_log(l, JK_LOG_WARNING,
+                   "Status worker '%s' could not find worker '%s'",
+                   w->name, w->we->worker_list[i]);
+            continue;
+        }
+        if ((list_lb && jw->type == JK_LB_WORKER_TYPE) ||
+            (!list_lb && jw->type != JK_LB_WORKER_TYPE)) {
+            display_worker(s, p, jw, l);
+        }
+    }
+
+    if (list_lb) {
+        if (mime == JK_STATUS_MIME_XML) {
+            jk_print_xml_close_elt(s, w, 0, "balancers");
+        }
+        else if (mime == JK_STATUS_MIME_TXT) {
+        }
+        else if (mime == JK_STATUS_MIME_PROP) {
+        }
+        else if (mime == JK_STATUS_MIME_HTML) {
+        }
+    }
+    else {
+        if (mime == JK_STATUS_MIME_XML) {
+            jk_print_xml_close_elt(s, w, 0, "ajp_workers");
+        }
+        else if (mime == JK_STATUS_MIME_TXT) {
+        }
+        else if (mime == JK_STATUS_MIME_PROP) {
+        }
+        else if (mime == JK_STATUS_MIME_HTML) {
+        }
+    }
+
+    JK_TRACE_EXIT(l);
+}
+
+static int list_workers(jk_ws_service_t *s,
+                        status_endpoint_t *p,
+                        jk_logger_t *l)
+{
+    int lb_cnt = 0;
+    int ajp_cnt = 0;
+
+    JK_TRACE_ENTER(l);
+    count_workers(s, p, &lb_cnt, &ajp_cnt, l);
+
+    if (lb_cnt) {
+        list_workers_type(s, p, 1, lb_cnt, l);
+    }
+
+    if (ajp_cnt) {
+        list_workers_type(s, p, 0, ajp_cnt, l);
+    }
+
+    display_legend(s, p, l);
+
+    JK_TRACE_EXIT(l);
+    return JK_TRUE;
+}
+
+static int show_worker(jk_ws_service_t *s,
+                       status_endpoint_t *p,
+                       jk_logger_t *l)
+{
+    const char *worker;
+    const char *sub_worker;
+    jk_worker_t *jw = NULL;
+
+    JK_TRACE_ENTER(l);
+    fetch_worker_and_sub_worker(p, "showing", &worker, &sub_worker, l);
+    if (search_worker(s, p, &jw, worker, l) == JK_FALSE) {
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    display_worker(s, p, jw, l);
+    display_legend(s, p, l);
+
+    JK_TRACE_EXIT(l);
+    return JK_TRUE;
+}
+
+static int edit_worker(jk_ws_service_t *s,
+                       status_endpoint_t *p,
+                       jk_logger_t *l)
+{
+    const char *worker;
+    const char *sub_worker;
+    jk_worker_t *jw = NULL;
+    status_worker_t *w = p->worker;
+
+    JK_TRACE_ENTER(l);
+    fetch_worker_and_sub_worker(p, "editing", &worker, &sub_worker, l);
+    if (search_worker(s, p, &jw, worker, l) == JK_FALSE) {
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    if (!sub_worker || !sub_worker[0]) {
+        const char *arg;
+
+        if (status_get_string(p, JK_STATUS_ARG_ATTRIBUTE,
+                              NULL, &arg, l) == JK_TRUE)
+            form_all_members(s, p, jw, arg, l);
+        else
+            form_worker(s, p, jw, l);
+    }
+    else  {
+        worker_record_t *wr = NULL;
+        if (jw->type != JK_LB_WORKER_TYPE) {
+            jk_log(l, JK_LOG_WARNING,
+                   "Status worker '%s' worker type not implemented",
+                   w->name);
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+        if(search_sub_worker(s, p, jw, worker, &wr, sub_worker, l) == JK_FALSE) {
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+        form_member(s, p, wr, worker, l);
+    }
+    JK_TRACE_EXIT(l);
+    return JK_TRUE;
+}
+
+static int update_worker(jk_ws_service_t *s,
+                         status_endpoint_t *p,
+                         jk_logger_t *l)
+{
+    const char *worker;
+    const char *sub_worker;
+    jk_worker_t *jw = NULL;
+
+    JK_TRACE_ENTER(l);
+    fetch_worker_and_sub_worker(p, "updating", &worker, &sub_worker, l);
+    if (search_worker(s, p, &jw, worker, l) == JK_FALSE) {
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    if (!sub_worker || !sub_worker[0]) {
+        const char *arg;
+
+        if (status_get_string(p, JK_STATUS_ARG_ATTRIBUTE,
+                              NULL, &arg, l) == JK_TRUE)
+            commit_all_members(s, p, jw, arg, l);
+        else
+            commit_worker(s, p, jw, l);
+    }
+    else  {
+        lb_worker_t *lb = NULL;
+        worker_record_t *wr = NULL;
+        int rc = 0;
+        if (check_valid_lb(s, p, jw, worker, &lb, 0, l) == JK_FALSE) {
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+        if(search_sub_worker(s, p, jw, worker, &wr, sub_worker, l) == JK_FALSE) {
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+        rc = commit_member(s, p, wr, lb->s->name, l);
+        if (rc & 1)
+            reset_lb_values(lb, l);
+        if (rc & 2)
+            /* Recalculate the load multiplicators wrt. lb_factor */
+            update_mult(lb, l);
+    }
+    JK_TRACE_EXIT(l);
+    return JK_TRUE;
+}
+
+static int reset_worker(jk_ws_service_t *s,
+                        status_endpoint_t *p,
+                        jk_logger_t *l)
+{
+    unsigned int i;
+    const char *worker;
+    const char *sub_worker;
+    jk_worker_t *jw = NULL;
+    lb_worker_t *lb = NULL;
+    worker_record_t *wr = NULL;
+
+    JK_TRACE_ENTER(l);
+    fetch_worker_and_sub_worker(p, "resetting", &worker, &sub_worker, l);
+    if (search_worker(s, p, &jw, worker, l) == JK_FALSE) {
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    /* XXX Until now, we only have something to reset for lb workers or their members */
+    if (check_valid_lb(s, p, jw, worker, &lb, 0, l) == JK_FALSE) {
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    if (!sub_worker || !sub_worker[0]) {
+        lb->s->max_busy = 0;
+        for (i = 0; i < lb->num_of_workers; i++) {
+            wr = &(lb->lb_workers[i]);
+            wr->s->client_errors    = 0;
+            wr->s->elected          = 0;
+            wr->s->elected_snapshot = 0;
+            wr->s->error_time       = 0;
+            wr->s->errors           = 0;
+            wr->s->lb_value         = 0;
+            wr->s->max_busy         = 0;
+            wr->s->recoveries       = 0;
+            wr->s->recovery_errors  = 0;
+            wr->s->readed           = 0;
+            wr->s->transferred      = 0;
+            wr->s->state            = JK_LB_STATE_NA;
+        }
+        JK_TRACE_EXIT(l);
+        return JK_TRUE;
+    }
+    else  {
+        if(search_sub_worker(s, p, jw, worker, &wr, sub_worker, l) == JK_FALSE) {
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+        wr->s->client_errors    = 0;
+        wr->s->elected          = 0;
+        wr->s->elected_snapshot = 0;
+        wr->s->error_time       = 0;
+        wr->s->errors           = 0;
+        wr->s->lb_value         = 0;
+        wr->s->max_busy         = 0;
+        wr->s->recoveries       = 0;
+        wr->s->recovery_errors  = 0;
+        wr->s->readed           = 0;
+        wr->s->transferred      = 0;
+        wr->s->state            = JK_LB_STATE_NA;
+        JK_TRACE_EXIT(l);
+        return JK_TRUE;
+    }
+    JK_TRACE_EXIT(l);
+    return JK_FALSE;
+}
+
+static int recover_worker(jk_ws_service_t *s,
+                          status_endpoint_t *p,
+                          jk_logger_t *l)
+{
+    const char *worker;
+    const char *sub_worker;
+    jk_worker_t *jw = NULL;
+    worker_record_t *wr = NULL;
+    status_worker_t *w = p->worker;
+
+    JK_TRACE_ENTER(l);
+    fetch_worker_and_sub_worker(p, "recovering", &worker, &sub_worker, l);
+    if (search_worker(s, p, &jw, worker, l) == JK_FALSE) {
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    if(search_sub_worker(s, p, jw, worker, &wr, sub_worker, l) == JK_FALSE) {
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    if (wr->s->state == JK_LB_STATE_ERROR) {
+        wr->s->state = JK_LB_STATE_RECOVER;
+        jk_log(l, JK_LOG_INFO,
+               "Status worker '%s' marked worker '%s' sub worker '%s' for recovery",
+               w->name, worker ? worker : "(null)", sub_worker ? sub_worker : "(null)");
+        JK_TRACE_EXIT(l);
+        return JK_TRUE;
+    }
+    jk_log(l, JK_LOG_WARNING,
+           "Status worker '%s' could not mark worker '%s' sub worker '%s' for recovery - state %s is not an error state",
+           w->name, worker ? worker : "(null)", sub_worker ? sub_worker : "(null)",
+           jk_lb_get_state(wr, l));
+    JK_TRACE_EXIT(l);
+    return JK_FALSE;
+}
+
+static int JK_METHOD service(jk_endpoint_t *e,
+                             jk_ws_service_t *s,
+                             jk_logger_t *l, int *is_error)
+{
+    int cmd;
+    int mime;
+    int refresh;
+    int read_only = 0;
+    const char *arg;
+    char *err = NULL;
+    status_endpoint_t *p;
+    status_worker_t *w;
+    int denied = 0;
+
+    JK_TRACE_ENTER(l);
+
+    if (is_error)
+        *is_error = JK_FALSE;
+    if (!e || !e->endpoint_private || !s || !is_error) {
+        JK_LOG_NULL_PARAMS(l);
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    p = e->endpoint_private;
+    w = p->worker;
+
+    if (w->num_of_users) {
+        if (s->remote_user) {
+            unsigned int i;
+            denied = 1;
+            for (i = 0; i < w->num_of_users; i++) {
+                if (w->user_case_insensitive) {
+                    if (!strcasecmp(s->remote_user, w->user_names[i])) {
+                        denied = 0;
+                        break;
+                    }
+                }
+                else {
+                    if (!strcmp(s->remote_user, w->user_names[i])) {
+                        denied = 0;
+                        break;
+                    }
+                }
+            }
+        }
+        else {
+            denied = 2;
+        }
+    }
+
+    /* Step 1: Process GET params and update configuration */
+    if (status_parse_uri(s, p, l) != JK_TRUE) {
+        err = "Error during parsing of URI";
+    }
+    status_get_string(p, JK_STATUS_ARG_CMD, NULL, &arg, l);
+    cmd = status_cmd_int(arg);
+    status_get_string(p, JK_STATUS_ARG_MIME, NULL, &arg, l);
+    mime = status_mime_int(arg);
+    refresh = status_get_int(p, JK_STATUS_ARG_REFRESH, 0, l);
+    if (w->read_only) {
+        read_only = 1;
+    }
+    else {
+        read_only = status_get_int(p, JK_STATUS_ARG_OPTIONS, 0, l) &
+                    JK_STATUS_ARG_OPTION_READ_ONLY;
+    }
+
+    if (mime == JK_STATUS_MIME_HTML) {
+        s->start_response(s, 200, "OK", headers_names, headers_vhtml, 3);
+        jk_puts(s, JK_STATUS_HEAD);
+    }
+    else if (mime == JK_STATUS_MIME_XML) {
+        s->start_response(s, 200, "OK", headers_names, headers_vxml, 3);
+        jk_puts(s, JK_STATUS_XMLH);
+        if (w->doctype) {
+            jk_putv(s, w->doctype, "\n", NULL);
+        }
+        jk_print_xml_start_elt(s, w, 0, 0, "status");
+        if (w->xmlns && strlen(w->xmlns))
+            jk_putv(s, "  ", w->xmlns, NULL);
+        jk_print_xml_stop_elt(s, 0, 0);
+    }
+    else {
+        s->start_response(s, 200, "OK", headers_names, headers_vtxt, 3);
+    }
+
+    if (denied == 0) {
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "Status worker '%s' service allowed for user '%s' [%s] from %s [%s]",
+                   w->name,
+                   s->remote_user ? s->remote_user : "(null)",
+                   s->auth_type ? s->auth_type : "(null)",
+                   s->remote_addr ? s->remote_addr : "(null)",
+                   s->remote_host ? s->remote_host : "(null)");
+    }
+    else if (denied == 1) {
+        err = "Access denied.";
+        jk_log(l, JK_LOG_WARNING,
+               "Status worker '%s' service denied for user '%s' [%s] from %s [%s]",
+               w->name,
+               s->remote_user ? s->remote_user : "(null)",
+               s->auth_type ? s->auth_type : "(null)",
+               s->remote_addr ? s->remote_addr : "(null)",
+               s->remote_host ? s->remote_host : "(null)");
+    }
+    else if (denied == 2) {
+        err = "Access denied.";
+        jk_log(l, JK_LOG_WARNING,
+               "Status worker '%s' service denied (no user) [%s] from %s [%s]",
+               w->name,
+               s->remote_user ? s->remote_user : "(null)",
+               s->auth_type ? s->auth_type : "(null)",
+               s->remote_addr ? s->remote_addr : "(null)",
+               s->remote_host ? s->remote_host : "(null)");
+    }
+    else {
+        err = "Access denied.";
+        jk_log(l, JK_LOG_WARNING,
+               "Status worker '%s' service denied (unknown reason) for user '%s' [%s] from %s [%s]",
+               w->name,
+               s->remote_user ? s->remote_user : "(null)",
+               s->auth_type ? s->auth_type : "(null)",
+               s->remote_addr ? s->remote_addr : "(null)",
+               s->remote_host ? s->remote_host : "(null)");
+    }
+
+    if (!err) {
+        if (read_only &&
+            (cmd == JK_STATUS_CMD_EDIT ||
+            cmd == JK_STATUS_CMD_UPDATE ||
+            cmd == JK_STATUS_CMD_RESET ||
+            cmd == JK_STATUS_CMD_RECOVER)) {
+            err = "This command is not allowed in read only mode.";
+        }
+    }
+
+    if (!err) {
+        if (cmd == JK_STATUS_CMD_UNKNOWN) {
+            err = "Invalid command.";
+        }
+        else if (mime == JK_STATUS_MIME_UNKNOWN) {
+            err = "Invalid mime type.";
+        }
+        else if (cmd != JK_STATUS_CMD_LIST &&
+                 cmd != JK_STATUS_CMD_VERSION &&
+                 (check_worker(s, p, l) != JK_TRUE)) {
+            err = p->msg;
+        }
+    }
+
+    if (!err) {
+        if (cmd == JK_STATUS_CMD_UPDATE) {
+            /* lock shared memory */
+            jk_shm_lock();
+            if (update_worker(s, p, l) == JK_FALSE) {
+                err = "Update failed";
+            }
+            /* unlock the shared memory */
+            jk_shm_unlock();
+            if (mime == JK_STATUS_MIME_HTML) {
+                jk_puts(s, "\n<meta http-equiv=\"Refresh\" content=\""
+                        JK_STATUS_WAIT_AFTER_UPDATE ";url=");
+                status_write_uri(s, p, NULL, JK_STATUS_CMD_UNKNOWN, JK_STATUS_MIME_UNKNOWN,
+                                 NULL, NULL, 0, 0, NULL, l);
+                jk_puts(s, "\">");
+                if (!err) {
+                    jk_putv(s, "<p><b>Result: OK - You will be redirected in "
+                            JK_STATUS_WAIT_AFTER_UPDATE " seconds.</b><p/>", NULL);
+                }
+            }
+        }
+        else if (cmd == JK_STATUS_CMD_RESET) {
+            /* lock shared memory */
+            jk_shm_lock();
+            if (reset_worker(s, p, l) == JK_FALSE) {
+                err = "Reset failed";
+            }
+            /* unlock the shared memory */
+            jk_shm_unlock();
+            if (mime == JK_STATUS_MIME_HTML) {
+                jk_puts(s, "\n<meta http-equiv=\"Refresh\" content=\""
+                        JK_STATUS_WAIT_AFTER_UPDATE ";url=");
+                status_write_uri(s, p, NULL, JK_STATUS_CMD_UNKNOWN, JK_STATUS_MIME_UNKNOWN,
+                                 NULL, NULL, 0, 0, NULL, l);
+                jk_puts(s, "\">");
+                if (!err) {
+                    jk_putv(s, "<p><b>Result: OK - You will be redirected in "
+                            JK_STATUS_WAIT_AFTER_UPDATE " seconds.</b><p/>", NULL);
+                }
+            }
+        }
+        else if (cmd == JK_STATUS_CMD_RECOVER) {
+            /* lock shared memory */
+            jk_shm_lock();
+            if (recover_worker(s, p, l) == JK_FALSE) {
+                err = "Marking worker for recovery failed";
+            }
+            /* unlock the shared memory */
+            jk_shm_unlock();
+            if (mime == JK_STATUS_MIME_HTML) {
+                jk_puts(s, "\n<meta http-equiv=\"Refresh\" content=\""
+                        JK_STATUS_WAIT_AFTER_UPDATE ";url=");
+                status_write_uri(s, p, NULL, JK_STATUS_CMD_UNKNOWN, JK_STATUS_MIME_UNKNOWN,
+                                 NULL, NULL, 0, 0, NULL, l);
+                jk_puts(s, "\">");
+                if (!err) {
+                    jk_putv(s, "<p><b>Result: OK - You will be redirected in "
+                            JK_STATUS_WAIT_AFTER_UPDATE " seconds.</b><p/>", NULL);
+                }
+            }
+        }
+        else {
+            if (mime == JK_STATUS_MIME_XML) {
+                jk_print_xml_start_elt(s, w, 0, 0, "server");
+                jk_print_xml_att_string(s, 2, "name", s->server_name);
+                jk_print_xml_att_int(s, 2, "port", s->server_port);
+                jk_print_xml_stop_elt(s, 0, 1);
+                if ((cmd == JK_STATUS_CMD_LIST) ||
+                    (cmd == JK_STATUS_CMD_SHOW) ||
+                    (cmd == JK_STATUS_CMD_VERSION)) {
+                    jk_print_xml_start_elt(s, w, 0, 0, "software");
+                    jk_print_xml_att_string(s, 2, "web_server", s->server_software);
+                    jk_print_xml_att_string(s, 2, "jk_version", JK_EXPOSED_VERSION);
+                    jk_print_xml_stop_elt(s, 0, 1);
+                }
+                if (cmd == JK_STATUS_CMD_LIST) {
+                    /* Step 2: Display configuration */
+                    if (list_workers(s, p, l) != JK_TRUE) {
+                        err = "Error in listing the workers.";
+                    }
+                }
+                else if (cmd == JK_STATUS_CMD_SHOW) {
+                    /* Step 2: Display detailed configuration */
+                    if(show_worker(s, p, l) != JK_TRUE) {
+                        err = "Error in showing this worker.";
+                    }
+                }
+            }
+            else if (mime == JK_STATUS_MIME_TXT) {
+                jk_puts(s, "Server:");
+                jk_printf(s, " name=%s", s->server_name);
+                jk_printf(s, " port=%d", s->server_port);
+                jk_puts(s, "\n");
+                if ((cmd == JK_STATUS_CMD_LIST) ||
+                    (cmd == JK_STATUS_CMD_SHOW) ||
+                    (cmd == JK_STATUS_CMD_VERSION)) {
+                    jk_puts(s, "Software:");
+                    jk_printf(s, " web_server=\"%s\"", s->server_software);
+                    jk_printf(s, " jk_version=%s", JK_EXPOSED_VERSION);
+                    jk_puts(s, "\n");
+                }
+                if (cmd == JK_STATUS_CMD_LIST) {
+                    /* Step 2: Display configuration */
+                    if (list_workers(s, p, l) != JK_TRUE) {
+                        err = "Error in listing the workers.";
+                    }
+                }
+                else if (cmd == JK_STATUS_CMD_SHOW) {
+                    /* Step 2: Display detailed configuration */
+                    if(show_worker(s, p, l) != JK_TRUE) {
+                        err = "Error in showing this worker.";
+                    }
+                }
+            }
+            else if (mime == JK_STATUS_MIME_PROP) {
+                jk_print_prop_att_string(s, w, NULL, "server_name", s->server_name);
+                jk_print_prop_att_int(s, w, NULL, "server_port", s->server_port);
+                if ((cmd == JK_STATUS_CMD_LIST) ||
+                    (cmd == JK_STATUS_CMD_SHOW) ||
+                    (cmd == JK_STATUS_CMD_VERSION)) {
+                    jk_print_prop_att_string(s, w, NULL, "web_server", s->server_software);
+                    jk_print_prop_att_string(s, w, NULL, "jk_version", JK_EXPOSED_VERSION);
+                }
+                if (cmd == JK_STATUS_CMD_LIST) {
+                    /* Step 2: Display configuration */
+                    if (list_workers(s, p, l) != JK_TRUE) {
+                        err = "Error in listing the workers.";
+                    }
+                }
+                else if (cmd == JK_STATUS_CMD_SHOW) {
+                    /* Step 2: Display detailed configuration */
+                    if(show_worker(s, p, l) != JK_TRUE) {
+                        err = "Error in showing this worker.";
+                    }
+                }
+            }
+            else if (mime == JK_STATUS_MIME_HTML) {
+                if ((cmd == JK_STATUS_CMD_LIST ||
+                    cmd == JK_STATUS_CMD_SHOW) &&
+                    refresh > 0) {
+                    jk_printf(s, "\n<meta http-equiv=\"Refresh\" content=\"%d;url=%s?%s\">",
+                          refresh, s->req_uri, s->query_string);
+                }
+                if (w->css) {
+                    jk_putv(s, "\n<link rel=\"stylesheet\" type=\"text/css\" href=\"",
+                            w->css, "\" />\n", NULL);
+                }
+                jk_puts(s, JK_STATUS_HEND);
+                jk_puts(s, "<h1>JK Status Manager for ");
+                jk_puts(s, s->server_name);
+                jk_printf(s, ":%d", s->server_port);
+                if (read_only) {
+                    jk_puts(s, " (read only)");
+                }
+                jk_puts(s, "</h1>\n\n");
+                if ((cmd == JK_STATUS_CMD_LIST) ||
+                    (cmd == JK_STATUS_CMD_SHOW) ||
+                    (cmd == JK_STATUS_CMD_VERSION)) {
+                    jk_putv(s, "<table><tr><td>Server Version:</td><td>",
+                            s->server_software, "</td></tr>\n", NULL);
+                    jk_putv(s, "<tr><td>JK Version:</td><td>",
+                            JK_EXPOSED_VERSION, "</td></tr></table>\n", NULL);
+                    jk_puts(s, "<hr/>\n");
+                }
+                if (cmd == JK_STATUS_CMD_LIST ||
+                    cmd == JK_STATUS_CMD_SHOW) {
+                    if (refresh > 0) {
+                        const char *str = s->query_string;
+                        char *buf = jk_pool_alloc(s->pool, sizeof(char *) * (strlen(str)+1));
+                        int result = 0;
+                        size_t scan = 0;
+                        size_t len = strlen(JK_STATUS_ARG_REFRESH);
+
+                        while (str[scan] != '\0') {
+                            if (strncmp(&str[scan], JK_STATUS_ARG_REFRESH, len) == 0 &&
+                                str[scan+len] == '=') {
+                                scan += len + 1;
+                                while (str[scan] != '\0' && str[scan] != '&')
+                                    scan++;
+                                if (str[scan] == '&')
+                                    scan++;
+                            }
+                            else {
+                                if (result > 0 && str[scan] != '\0' && str[scan] != '&') {
+                                    buf[result] = '&';
+                                    result++;
+                                }
+                                while (str[scan] != '\0' && str[scan] != '&') {
+                                    buf[result] = str[scan];
+                                    result++;
+                                    scan++;
+                                }
+                                if (str[scan] == '&')
+                                    scan++;
+                            }
+                        }
+                        buf[result] = '\0';
+
+                        jk_putv(s, "[<a href=\"", s->req_uri, NULL);
+                        if (buf && buf[0])
+                            jk_putv(s, "?", buf, NULL);
+                        jk_puts(s, "\">Stop auto refresh</a>]&nbsp;&nbsp;");
+                    }
+                    else {
+                        status_start_form(s, p, "GET", JK_STATUS_CMD_UNKNOWN, l);
+                        jk_puts(s, "<input type=\"submit\" value=\"Start auto refresh\"/>\n");
+                        jk_putv(s, "(every ",
+                                "<input name=\"", JK_STATUS_ARG_REFRESH,
+                                "\" type=\"text\" size=\"3\" value=\"",
+                                JK_STATUS_REFRESH_DEF "\"/> ",
+                                "seconds)", NULL);
+                        jk_puts(s, "</form>\n");
+                    }
+                }
+                if (cmd == JK_STATUS_CMD_SHOW ||
+                    cmd == JK_STATUS_CMD_EDIT ||
+                    cmd == JK_STATUS_CMD_VERSION) {
+                    int from;
+                    status_get_string(p, JK_STATUS_ARG_FROM, NULL, &arg, l);
+                    from = status_cmd_int(arg);
+                    jk_puts(s, "[");
+                    if (cmd == JK_STATUS_CMD_SHOW ||
+                        cmd == JK_STATUS_CMD_VERSION ||
+                        from == JK_STATUS_CMD_LIST) {
+                        status_write_uri(s, p, "Back to worker list", JK_STATUS_CMD_LIST, JK_STATUS_MIME_UNKNOWN,
+                                         "", "", 0, 0, "", l);
+                    }
+                    else {
+                        status_write_uri(s, p, "Back to worker view", JK_STATUS_CMD_SHOW, JK_STATUS_MIME_UNKNOWN,
+                                         NULL, NULL, 0, 0, "", l);
+                    }
+                    jk_puts(s, "]&nbsp;&nbsp;");
+                }
+                if (cmd == JK_STATUS_CMD_LIST ||
+                    cmd == JK_STATUS_CMD_SHOW ||
+                    cmd == JK_STATUS_CMD_VERSION) {
+                    jk_puts(s, "[Change&nbsp;Format: ");
+                    status_write_uri(s, p, "XML", 0, JK_STATUS_MIME_XML,
+                                     NULL, NULL, 0, 0, NULL, l);
+                    jk_puts(s, "&nbsp;|&nbsp;");
+                    status_write_uri(s, p, "Property", 0, JK_STATUS_MIME_PROP,
+                                     NULL, NULL, 0, 0, NULL, l);
+                    jk_puts(s, "&nbsp;|&nbsp;");
+                    status_write_uri(s, p, "Text", 0, JK_STATUS_MIME_TXT,
+                                     NULL, NULL, 0, 0, NULL, l);
+                    jk_puts(s, "]&nbsp;&nbsp;");
+                }
+                if (cmd == JK_STATUS_CMD_LIST || cmd == JK_STATUS_CMD_SHOW) {
+                    if (!w->read_only) {
+                        jk_puts(s, "[");
+                        if (read_only) {
+                            status_write_uri(s, p, "Read/Write", 0, JK_STATUS_MIME_UNKNOWN,
+                                             NULL, NULL, 0, JK_STATUS_ARG_OPTION_READ_ONLY, NULL, l);
+                        }
+                        else {
+                            status_write_uri(s, p, "Read Only", 0, JK_STATUS_MIME_UNKNOWN,
+                                             NULL, NULL, JK_STATUS_ARG_OPTION_READ_ONLY, 0, NULL, l);
+                        }
+                        jk_puts(s, "]&nbsp;&nbsp;\n");
+                    }
+                }
+                if (cmd == JK_STATUS_CMD_LIST || cmd == JK_STATUS_CMD_SHOW) {
+                    jk_puts(s, "[");
+                    if (cmd == JK_STATUS_CMD_LIST)
+                        jk_puts(s, "<b>S</b>=Show only this worker");
+                    if (!read_only && cmd == JK_STATUS_CMD_LIST)
+                        jk_puts(s, ", ");
+                    if (!read_only)
+                        jk_puts(s, "<b>E</b>=Edit worker, <b>R</b>=Reset worker state, <b>T</b>=Try worker recovery");
+                    jk_puts(s, "]\n");
+                }
+                if (cmd == JK_STATUS_CMD_LIST) {
+                    /* Step 2: Display configuration */
+                    if (list_workers(s, p, l) != JK_TRUE) {
+                        err = "Error in listing the workers.";
+                    }
+                }
+                else if (cmd == JK_STATUS_CMD_SHOW) {
+                    /* Step 2: Display detailed configuration */
+                    if(show_worker(s, p, l) != JK_TRUE) {
+                        err = "Error in showing this worker.";
+                    }
+                }
+                else if (cmd == JK_STATUS_CMD_EDIT) {
+                    /* Step 2: Display edit form */
+                    if(edit_worker(s, p, l) != JK_TRUE) {
+                        err = "Error in generating this worker's configuration form.";
+                    }
+                }
+            }
+        }
+    }
+    if (err) {
+        jk_log(l, JK_LOG_WARNING, "Status worker '%s': %s", w->name, err);
+        if (mime == JK_STATUS_MIME_HTML) {
+            jk_putv(s, "<p><b>Result: ERROR - ", err, "</b><br/>", NULL);
+            jk_putv(s, "<a href=\"", s->req_uri, "\">JK Status Manager Start Page</a></p>", NULL);
+        }
+        else if (mime == JK_STATUS_MIME_XML) {
+            jk_print_xml_start_elt(s, w, 2, 0, "result");
+            jk_print_xml_att_string(s, 4, "type", "ERROR");
+            jk_print_xml_att_string(s, 4, "message", err);
+            jk_print_xml_stop_elt(s, 2, 1);
+        }
+        else if (mime == JK_STATUS_MIME_TXT) {
+            jk_puts(s, "Result:");
+            jk_printf(s, " type=%s", "ERROR");
+            jk_printf(s, " message=\"%s\"", err);
+            jk_puts(s, "\n");
+        }
+        else {
+            jk_print_prop_att_string(s, w, "result", "type", "ERROR");
+            jk_print_prop_att_string(s, w, "result", "message", err);
+        }
+    }
+    else {
+        if (mime == JK_STATUS_MIME_HTML) {
+            jk_putv(s, "<p><a href=\"", s->req_uri, "\">JK Status Manager Start Page</a></p>", NULL);
+        }
+        else if (mime == JK_STATUS_MIME_XML) {
+            jk_print_xml_start_elt(s, w, 2, 0, "result");
+            jk_print_xml_att_string(s, 4, "type", "OK");
+            jk_print_xml_att_string(s, 4, "message", "Action finished");
+            jk_print_xml_stop_elt(s, 2, 1);
+        }
+        else if (mime == JK_STATUS_MIME_TXT) {
+            jk_puts(s, "Result:");
+            jk_printf(s, " type=%s", "OK");
+            jk_printf(s, " message=\"%s\"", "Action finished");
+            jk_puts(s, "\n");
+        }
+        else {
+            jk_print_prop_att_string(s, w, "result", "type", "OK");
+            jk_print_prop_att_string(s, w, "result", "message", "Action finished");
+        }
+    }
+    if (mime == JK_STATUS_MIME_HTML) {
+        if (w->css) {
+            jk_putv(s, "<hr/><div class=\"footer\">", JK_STATUS_COPYRIGHT,
+                    "</div>\n", NULL);
+        }
+        else {
+            jk_putv(s, "<hr/><p align=\"center\"><small>", JK_STATUS_COPYRIGHT,
+                    "</small></p>\n", NULL);
+        }
+        jk_puts(s, JK_STATUS_BEND);
+    }
+    else if (mime == JK_STATUS_MIME_XML) {
+        jk_print_xml_close_elt(s, w, 0, "status");
+    }
+    JK_TRACE_EXIT(l);
+    return JK_TRUE;
+}
+
+static int JK_METHOD done(jk_endpoint_t **e, jk_logger_t *l)
+{
+    JK_TRACE_ENTER(l);
+
+    if (e && *e && (*e)->endpoint_private) {
+        status_endpoint_t *p = (*e)->endpoint_private;
+
+        free(p);
+        *e = NULL;
+        JK_TRACE_EXIT(l);
+        return JK_TRUE;
+    }
+
+    JK_LOG_NULL_PARAMS(l);
+    JK_TRACE_EXIT(l);
+    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_TRACE_ENTER(l);
+
+    if (pThis && pThis->worker_private) {
+        JK_TRACE_EXIT(l);
+        return JK_TRUE;
+    }
+
+    JK_LOG_NULL_PARAMS(l);
+    JK_TRACE_EXIT(l);
+    return JK_FALSE;
+}
+
+static int JK_METHOD init(jk_worker_t *pThis,
+                          jk_map_t *props,
+                          jk_worker_env_t *we, jk_logger_t *l)
+{
+    JK_TRACE_ENTER(l);
+    if (pThis && pThis->worker_private) {
+        status_worker_t *p = pThis->worker_private;
+        char **good_rating;
+        unsigned int num_of_good;
+        char **bad_rating;
+        unsigned int num_of_bad;
+        unsigned int i;
+        p->we = we;
+        p->css = jk_get_worker_style_sheet(props, p->name, NULL);
+        p->prefix = jk_get_worker_prop_prefix(props, p->name, JK_STATUS_PREFIX_DEF);
+        p->ns = jk_get_worker_name_space(props, p->name, JK_STATUS_NS_DEF);
+        p->xmlns = jk_get_worker_xmlns(props, p->name, JK_STATUS_XMLNS_DEF);
+        p->doctype = jk_get_worker_xml_doctype(props, p->name, NULL);
+        p->read_only = jk_get_is_read_only(props, p->name);
+        p->user_case_insensitive = jk_get_worker_user_case_insensitive(props, p->name);
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "Status worker '%s' is %s and has css '%s', prefix '%s', name space '%s', xml name space '%s', document type '%s'",
+                   p->name,
+                   p->read_only ? "read-only" : "read/write",
+                   p->css ? p->css : "(null)",
+                   p->prefix ? p->prefix : "(null)",
+                   p->ns ? p->ns : "(null)",
+                   p->xmlns ? p->xmlns : "(null)",
+                   p->doctype ? p->doctype : "(null)");
+        if (jk_get_worker_user_list(props, p->name,
+                                    &(p->user_names),
+                                    &(p->num_of_users)) && p->num_of_users) {
+            for (i = 0; i < p->num_of_users; i++) {
+                if (JK_IS_DEBUG_LEVEL(l))
+                    jk_log(l, JK_LOG_DEBUG,
+                            "Status worker '%s' restricting access to user '%s' case %s",
+                            p->name, p->user_names[i],
+                            p->user_case_insensitive ? "insensitive" : "sensitive");
+            }
+        }
+        if (jk_get_worker_good_rating(props, p->name,
+                                      &good_rating,
+                                      &num_of_good) && num_of_good) {
+            p->good_mask = 0;
+            for (i = 0; i < num_of_good; i++) {
+                if (JK_IS_DEBUG_LEVEL(l))
+                    jk_log(l, JK_LOG_DEBUG,
+                            "Status worker '%s' rating as good: '%s'",
+                            p->name, good_rating[i]);
+                p->good_mask |= status_get_rating(good_rating[i], l);
+            }
+        }
+        else {
+            p->good_mask = JK_STATUS_MASK_GOOD_DEF;
+        }
+        if (jk_get_worker_bad_rating(props, p->name,
+                                      &bad_rating,
+                                      &num_of_bad) && num_of_bad) {
+            p->bad_mask = 0;
+            for (i = 0; i < num_of_bad; i++) {
+                if (JK_IS_DEBUG_LEVEL(l))
+                    jk_log(l, JK_LOG_DEBUG,
+                            "Status worker '%s' rating as bad: '%s'",
+                            p->name, bad_rating[i]);
+                p->bad_mask |= status_get_rating(bad_rating[i], l);
+            }
+        }
+        else {
+            p->bad_mask = JK_STATUS_MASK_BAD_DEF;
+        }
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "Status worker '%s' has good rating for '%08" JK_UINT32_T_HEX_FMT
+                   "' and bad rating for '%08" JK_UINT32_T_HEX_FMT "'",
+                   p->name,
+                   p->good_mask,
+                   p->bad_mask);
+        if (p->good_mask & p->bad_mask)
+            jk_log(l, JK_LOG_WARNING,
+                   "Status worker '%s' has non empty intersection '%08" JK_UINT32_T_HEX_FMT
+                   "' between good rating for '%08" JK_UINT32_T_HEX_FMT
+                   "' and bad rating for '%08" JK_UINT32_T_HEX_FMT "'",
+                   p->name,
+                   p->good_mask & p->bad_mask,
+                   p->good_mask,
+                   p->bad_mask);
+    }
+    JK_TRACE_EXIT(l);
+    return JK_TRUE;
+}
+
+static int JK_METHOD get_endpoint(jk_worker_t *pThis,
+                                  jk_endpoint_t **pend, jk_logger_t *l)
+{
+    JK_TRACE_ENTER(l);
+
+    if (pThis && pThis->worker_private && pend) {
+        status_endpoint_t *p = (status_endpoint_t *) malloc(sizeof(status_endpoint_t));
+        p->worker = pThis->worker_private;
+        p->endpoint.endpoint_private = p;
+        p->endpoint.service = service;
+        p->endpoint.done = done;
+        p->req_params = NULL;
+        p->msg = NULL;
+        *pend = &p->endpoint;
+
+        JK_TRACE_EXIT(l);
+        return JK_TRUE;
+    }
+    else {
+        JK_LOG_NULL_PARAMS(l);
+    }
+
+    JK_TRACE_EXIT(l);
+    return JK_FALSE;
+}
+
+static int JK_METHOD destroy(jk_worker_t **pThis, jk_logger_t *l)
+{
+    JK_TRACE_ENTER(l);
+
+    if (pThis && *pThis && (*pThis)->worker_private) {
+        status_worker_t *private_data = (*pThis)->worker_private;
+
+        jk_close_pool(&private_data->p);
+        free(private_data);
+
+        JK_TRACE_EXIT(l);
+        return JK_TRUE;
+    }
+
+    JK_LOG_NULL_PARAMS(l);
+    JK_TRACE_EXIT(l);
+    return JK_FALSE;
+}
+
+int JK_METHOD status_worker_factory(jk_worker_t **w,
+                                    const char *name, jk_logger_t *l)
+{
+    JK_TRACE_ENTER(l);
+
+    if (NULL != name && NULL != w) {
+        status_worker_t *private_data =
+            (status_worker_t *) calloc(1, sizeof(status_worker_t));
+
+        jk_open_pool(&private_data->p,
+                        private_data->buf,
+                        sizeof(jk_pool_atom_t) * TINY_POOL_SIZE);
+
+        private_data->name = name;
+
+        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;
+        private_data->worker.retries = 1;
+
+        *w = &private_data->worker;
+        JK_TRACE_EXIT(l);
+        return JK_STATUS_WORKER_TYPE;
+    }
+    else {
+        JK_LOG_NULL_PARAMS(l);
+    }
+
+    JK_TRACE_EXIT(l);
+    return 0;
+}
diff --git a/connectors/jk/native/common/jk_status.h b/connectors/jk/native/common/jk_status.h
new file mode 100644
index 0000000..10c2167
--- /dev/null
+++ b/connectors/jk/native/common/jk_status.h
@@ -0,0 +1,44 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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: Status worker header file                                  *
+ * Author:      Mladen Turk <mturk@jboss.com>                              * 
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#ifndef JK_STATUS_H
+#define JK_STATUS_H
+
+#include "jk_logger.h"
+#include "jk_service.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif                          /* __cplusplus */
+
+#define JK_STATUS_WORKER_NAME     ("status")
+#define JK_STATUS_WORKER_TYPE     (6)
+
+int JK_METHOD status_worker_factory(jk_worker_t **w,
+                                    const char *name, jk_logger_t *l);
+
+#ifdef __cplusplus
+}
+#endif                          /* __cplusplus */
+#endif                          /* JK_STATUS_H */
diff --git a/connectors/jk/native/common/jk_types.h.in b/connectors/jk/native/common/jk_types.h.in
new file mode 100644
index 0000000..ea67e3a
--- /dev/null
+++ b/connectors/jk/native/common/jk_types.h.in
@@ -0,0 +1,60 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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: Platform specific, auto-detected types.                    *
+ * Author:      Rainer Jung <rjung@apache.org>                             *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#ifndef JK_TYPES_H
+#define JK_TYPES_H
+
+/* GENERATED FILE WARNING!  DO NOT EDIT jk_types.h
+ *
+ * You must modify jk_types.h.in instead.
+ *
+ */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif                          /* __cplusplus */
+
+/* jk_uint32_t defines a four byte word */
+typedef unsigned @int32_value@ jk_uint32_t;
+
+/* And JK_UINT32_T_FMT */
+@uint32_t_fmt@
+
+/* And JK_UINT32_T_HEX_FMT */
+@uint32_t_hex_fmt@
+
+/* jk_uint64_t defines a eight byte word */
+typedef unsigned @int64_value@ jk_uint64_t;
+
+/* And JK_UINT64_T_FMT */
+@uint64_t_fmt@
+
+/* And JK_UINT64_T_HEX_FMT */
+@uint64_t_hex_fmt@
+
+#ifdef __cplusplus
+}
+#endif                          /* __cplusplus */
+
+#endif                          /* JK_TYPES_H */
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..7f3feab
--- /dev/null
+++ b/connectors/jk/native/common/jk_uri_worker_map.c
@@ -0,0 +1,872 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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.                                  *
+ *                                                                         *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Author:      Mladen Turk <mturk@apache.org>                             *
+ * Version:     $Revision$                                          *
+ ***************************************************************************/
+
+#include "jk_pool.h"
+#include "jk_util.h"
+#include "jk_map.h"
+#include "jk_mt.h"
+#include "jk_uri_worker_map.h"
+
+#ifdef WIN32
+#define JK_STRCMP   strcasecmp
+#define JK_STRNCMP  strnicmp
+#else
+#define JK_STRCMP   strcmp
+#define JK_STRNCMP  strncmp
+#endif
+#define BAD_REQUEST     -1
+#define BAD_PATH        -2
+
+
+static const char *uri_worker_map_source_type[] = {
+    "unknown",
+    SOURCE_TYPE_TEXT_WORKERDEF,
+    SOURCE_TYPE_TEXT_JKMOUNT,
+    SOURCE_TYPE_TEXT_URIMAP,
+    SOURCE_TYPE_TEXT_DISCOVER,
+    NULL
+};
+
+#define JK_ISXDIGIT(x) isxdigit((int)(unsigned char)((x)))
+
+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 (!JK_ISXDIGIT(url[y + 1]) || !JK_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 normalize_url(char *name)
+{
+    int l, w;
+
+    /* Four paseses, as per RFC 1808 */
+    /* 1. 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++];
+    }
+
+    /* 2. remove trailing . path, segment */
+    if (w == 1 && name[0] == '.')
+        w--;
+    else if (w > 1 && name[w - 1] == '.' && name[w - 2] == '/')
+        w--;
+    name[w] = '\0';
+
+    /* 3. 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]) != '\0') {
+                n++;
+                m++;
+            }
+        }
+        else
+            ++l;
+    }
+
+    /* 4. 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';
+    }
+}
+
+
+/* Return the string representation of the uwr source */
+const char *uri_worker_map_get_source(uri_worker_record_t *uwr, jk_logger_t *l)
+{
+    return uri_worker_map_source_type[uwr->source_type];
+}
+
+/* Return the string representation of the uwr match type */
+char *uri_worker_map_get_match(uri_worker_record_t *uwr, char *buf, jk_logger_t *l)
+{
+    unsigned int match;
+
+    buf[0] = '\0';
+    match = uwr->match_type;
+
+    if (match & MATCH_TYPE_DISABLED)
+        strcat(buf, "Disabled ");
+/* deprecated
+    if (match & MATCH_TYPE_STOPPED)
+        strcat(buf, "Stopped ");
+ */
+    if (match & MATCH_TYPE_NO_MATCH)
+        strcat(buf, "Unmount ");
+    if (match & MATCH_TYPE_EXACT)
+        strcat(buf, "Exact");
+    else if (match & MATCH_TYPE_WILDCHAR_PATH)
+        strcat(buf, "Wildchar");
+/* deprecated
+    else if (match & MATCH_TYPE_CONTEXT)
+        strcat(buf, "Context");
+    else if (match & MATCH_TYPE_CONTEXT_PATH)
+        strcat(buf, "Context Path");
+    else if (match & MATCH_TYPE_SUFFIX)
+        strcat(buf, "Suffix");
+    else if (match & MATCH_TYPE_GENERAL_SUFFIX)
+        return "General Suffix";
+ */
+    else
+        strcat(buf, "Unknown");
+    return buf;
+}
+
+/*
+ * Given context uri, count the number of path tokens.
+ *
+ * Servlet specification 2.4, SRV.11.1 says
+
+ *   The container will recursively try tomatch the longest
+ *   path-prefix. This is done by stepping down the path tree a
+ *   directory at a time, using the / character as a path
+ *   separator. The longest match determines the servlet selected.
+ *
+ * The implication seems to be `most uri path elements is most exact'.
+ * This is a little helper function to count uri tokens, so we can
+ * keep the worker map sorted with most specific first.
+ */
+static int worker_count_context_uri_tokens(const char * context)
+{
+    const char * c = context;
+    int count = 0;
+    while (c && *c) {
+        if ('/' == *c++)
+            count++;
+    }
+    return count;
+}
+
+static int worker_compare(const void *elem1, const void *elem2)
+{
+    uri_worker_record_t *e1 = *(uri_worker_record_t **)elem1;
+    uri_worker_record_t *e2 = *(uri_worker_record_t **)elem2;
+    int e1_tokens = 0;
+    int e2_tokens = 0;
+
+    e1_tokens = worker_count_context_uri_tokens(e1->context);
+    e2_tokens = worker_count_context_uri_tokens(e2->context);
+
+    if (e1_tokens != e2_tokens) {
+        return (e2_tokens - e1_tokens);
+    }
+    /* given the same number of URI tokens, use character
+     * length as a tie breaker
+     */
+    if(e2->context_len != e1->context_len)
+        return ((int)e2->context_len - (int)e1->context_len);
+
+    return ((int)e2->source_type - (int)e1->source_type);
+}
+
+static void worker_qsort(jk_uri_worker_map_t *uw_map)
+{
+
+   /* Sort remaining args using Quicksort algorithm: */
+   qsort((void *)uw_map->maps, uw_map->size,
+         sizeof(uri_worker_record_t *), worker_compare );
+
+}
+
+/* Match = 0, NoMatch = 1, Abort = -1
+ * Based loosely on sections of wildmat.c by Rich Salz
+ */
+static int wildchar_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 = wildchar_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');
+}
+
+int uri_worker_map_alloc(jk_uri_worker_map_t **uw_map,
+                         jk_map_t *init_data, jk_logger_t *l)
+{
+    JK_TRACE_ENTER(l);
+
+    if (uw_map) {
+        int rc;
+        *uw_map = (jk_uri_worker_map_t *)calloc(1, sizeof(jk_uri_worker_map_t));
+
+        JK_INIT_CS(&((*uw_map)->cs), rc);
+        if (rc == JK_FALSE) {
+            jk_log(l, JK_LOG_ERROR,
+                   "creating thread lock (errno=%d)",
+                   errno);
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+        if (init_data)
+            rc = uri_worker_map_open(*uw_map, init_data, l);
+        JK_TRACE_EXIT(l);
+        return rc;
+    }
+
+    JK_LOG_NULL_PARAMS(l);
+    JK_TRACE_EXIT(l);
+
+    return JK_FALSE;
+}
+
+static int uri_worker_map_close(jk_uri_worker_map_t *uw_map, jk_logger_t *l)
+{
+    JK_TRACE_ENTER(l);
+
+    if (uw_map) {
+        int i;
+        JK_DELETE_CS(&(uw_map->cs), i);
+        jk_close_pool(&uw_map->p);
+        JK_TRACE_EXIT(l);
+        return JK_TRUE;
+    }
+
+    JK_LOG_NULL_PARAMS(l);
+    JK_TRACE_EXIT(l);
+    return JK_FALSE;
+}
+
+int uri_worker_map_free(jk_uri_worker_map_t **uw_map, jk_logger_t *l)
+{
+    JK_TRACE_ENTER(l);
+
+    if (uw_map && *uw_map) {
+        uri_worker_map_close(*uw_map, l);
+        free(*uw_map);
+        *uw_map = NULL;
+        JK_TRACE_EXIT(l);
+        return JK_TRUE;
+    }
+    else
+        JK_LOG_NULL_PARAMS(l);
+
+    JK_TRACE_EXIT(l);
+    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;
+}
+
+
+/*
+ * Delete all entries of a given source type
+ */
+
+int uri_worker_map_clear(jk_uri_worker_map_t *uw_map,
+                         unsigned int source_type, jk_logger_t *l)
+{
+    uri_worker_record_t *uwr = NULL;
+    unsigned int i;
+    unsigned int j;
+
+    JK_TRACE_ENTER(l);
+
+    for (i = 0; i < uw_map->size; i++) {
+        uwr = uw_map->maps[i];
+        if (uwr->source_type == source_type) {
+            jk_log(l, JK_LOG_DEBUG,
+                   "deleting map rule '%s=%s' source '%s'",
+                   uwr->context, uwr->worker_name, uri_worker_map_get_source(uwr, l));
+            for (j = i; j < uw_map->size-1; j++)
+                uw_map->maps[j] = uw_map->maps[j+1];
+            uw_map->size--;
+            i--;
+        }
+    }
+
+    JK_TRACE_EXIT(l);
+    return JK_TRUE;
+}
+
+
+
+int uri_worker_map_add(jk_uri_worker_map_t *uw_map,
+                       const char *puri, const char *worker,
+                       unsigned int source_type, jk_logger_t *l)
+{
+    uri_worker_record_t *uwr = NULL;
+    char *uri;
+    unsigned int match_type = 0;
+
+    JK_TRACE_ENTER(l);
+
+    if (*puri == '-') {
+        /* Disable urimap.
+         * This way you can disable already mounted
+         * context.
+         */
+        match_type = MATCH_TYPE_DISABLED;
+        puri++;
+    }
+    if (*puri == '!') {
+        match_type |= MATCH_TYPE_NO_MATCH;
+        puri++;
+    }
+
+    if (uri_worker_map_realloc(uw_map) == JK_FALSE) {
+        JK_TRACE_EXIT(l);
+        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,
+               "can't alloc map entry");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    uri = jk_pool_strdup(&uw_map->p, puri);
+    if (!uri || !worker) {
+        jk_log(l, JK_LOG_ERROR,
+               "can't alloc uri/worker strings");
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    if (*uri == '/') {
+        uwr->uri = uri;
+        uwr->context = uri;
+        uwr->worker_name = jk_pool_strdup(&uw_map->p, worker);
+        uwr->context_len = strlen(uwr->context);
+        uwr->source_type = source_type;
+        if (strchr(uri, '*') ||
+            strchr(uri, '?')) {
+            /* Something like
+             * /context/ * /user/ *
+             * /context/ *.suffix
+             */
+            match_type |= MATCH_TYPE_WILDCHAR_PATH;
+            jk_log(l, JK_LOG_DEBUG,
+                   "wildchar rule '%s=%s' source '%s' was added",
+                   uwr->context, uwr->worker_name, uri_worker_map_get_source(uwr, l));
+
+        }
+        else {
+            /* Something like:  JkMount /login/j_security_check ajp13 */
+            match_type |= MATCH_TYPE_EXACT;
+            jk_log(l, JK_LOG_DEBUG,
+                   "exact rule '%s=%s' source '%s' was added",
+                   uwr->context, uwr->worker_name, uri_worker_map_get_source(uwr, l));
+        }
+    }
+    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,
+               "invalid context '%s': does not begin with '/'",
+               uri);
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    uwr->match_type = match_type;
+    uw_map->maps[uw_map->size] = uwr;
+    uw_map->size++;
+    if (match_type & MATCH_TYPE_NO_MATCH) {
+        /* If we split the mappings this one will be calculated */
+        uw_map->nosize++;
+    }
+    worker_qsort(uw_map);
+    JK_TRACE_EXIT(l);
+    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_TRACE_ENTER(l);
+
+    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);
+        uw_map->size = 0;
+        uw_map->maps = NULL;
+
+        sz = jk_map_size(init_data);
+
+        jk_log(l, JK_LOG_DEBUG,
+               "rule map size is %d",
+               sz);
+
+        if (sz > 0) {
+            int i;
+            for (i = 0; i < sz; i++) {
+                const char *u = jk_map_name_at(init_data, i);
+                const char *w = jk_map_value_at(init_data, i);
+                /* Multiple mappings like :
+                 * /servlets-examples|/ *
+                 * will create two mappings:
+                 * /servlets-examples
+                 * and:
+                 * /servlets-examples/ *
+                 */
+                if (strchr(u, '|')) {
+                    char *s, *r = strdup(u);
+                    s = strchr(r, '|');
+                    *(s++) = '\0';
+                    /* Add first mapping */
+                    if (!uri_worker_map_add(uw_map, r, w, SOURCE_TYPE_JKMOUNT, l)) {
+                        jk_log(l, JK_LOG_ERROR,
+                        "invalid mapping rule %s->%s", r, w);
+                        rc = JK_FALSE;
+                    }
+                    for (; *s; s++)
+                        *(s - 1) = *s;
+                    *(s - 1) = '\0';
+                    /* add second mapping */
+                    if (!uri_worker_map_add(uw_map, r, w, SOURCE_TYPE_JKMOUNT, l)) {
+                        jk_log(l, JK_LOG_ERROR,
+                               "invalid mapping rule %s->%s", r, w);
+                        rc = JK_FALSE;
+                    }
+                    free(r);
+                }
+                else if (!uri_worker_map_add(uw_map, u, w, SOURCE_TYPE_JKMOUNT, l)) {
+                    jk_log(l, JK_LOG_ERROR,
+                           "invalid mapping rule %s->%s",
+                           u, w);
+                    rc = JK_FALSE;
+                    break;
+                }
+                if (rc == JK_FALSE)
+                    break;
+            }
+        }
+
+        if (rc == JK_FALSE) {
+            jk_log(l, JK_LOG_ERROR,
+                   "there was an error, freing buf");
+            jk_close_pool(&uw_map->p);
+        }
+    }
+
+    JK_TRACE_EXIT(l);
+    return rc;
+}
+
+static int is_nomap_match(jk_uri_worker_map_t *uw_map,
+                          const char *uri, const char* worker,
+                          jk_logger_t *l)
+{
+    unsigned int i;
+
+    JK_TRACE_ENTER(l);
+
+    for (i = 0; i < uw_map->size; i++) {
+        uri_worker_record_t *uwr = uw_map->maps[i];
+
+        /* Check only nomatch mappings */
+        if (!(uwr->match_type & MATCH_TYPE_NO_MATCH) ||
+            (uwr->match_type & MATCH_TYPE_DISABLED))
+            continue;
+        /* Check only matching workers */
+        if (strcmp(uwr->worker_name, worker))
+            continue;
+        if (uwr->match_type & MATCH_TYPE_WILDCHAR_PATH) {
+            /* Map is already sorted by context_len */
+            if (wildchar_match(uri, uwr->context,
+#ifdef WIN32
+                               0
+#else
+                               0
+#endif
+                               ) == 0) {
+                    jk_log(l, JK_LOG_DEBUG,
+                           "Found a wildchar no match '%s=%s' source '%s'",
+                           uwr->context, uwr->worker_name, uri_worker_map_get_source(uwr, l));
+                    JK_TRACE_EXIT(l);
+                    return JK_TRUE;
+             }
+        }
+        else if (JK_STRNCMP(uwr->context, uri, uwr->context_len) == 0) {
+            if (strlen(uri) == uwr->context_len) {
+                if (JK_IS_DEBUG_LEVEL(l))
+                    jk_log(l, JK_LOG_DEBUG,
+                           "Found an exact no match '%s=%s' source '%s'",
+                           uwr->context, uwr->worker_name, uri_worker_map_get_source(uwr, l));
+                JK_TRACE_EXIT(l);
+                return JK_TRUE;
+            }
+        }
+    }
+
+    JK_TRACE_EXIT(l);
+    return JK_FALSE;
+}
+
+
+const char *map_uri_to_worker(jk_uri_worker_map_t *uw_map,
+                              const char *uri, jk_logger_t *l)
+{
+    unsigned int i;
+    int rc;
+    const char *rv = NULL;
+    char  url[JK_MAX_URI_LEN+1];
+
+    JK_TRACE_ENTER(l);
+
+    if (!uw_map || !uri) {
+        JK_LOG_NULL_PARAMS(l);
+        JK_TRACE_EXIT(l);
+        return NULL;
+    }
+    if (*uri != '/') {
+        jk_log(l, JK_LOG_WARNING,
+                "Uri %s is invalid. Uri must start with /", uri);
+        JK_TRACE_EXIT(l);
+        return NULL;
+    }
+    if (uw_map->fname) {
+        uri_worker_map_update(uw_map, l);
+        if (!uw_map->size) {
+            jk_log(l, JK_LOG_INFO,
+                   "No worker maps defined for %s.",
+                   uw_map->fname);
+            JK_TRACE_EXIT(l);
+            return NULL;
+        }
+    }
+    /* Make the copy of the provided uri and strip
+     * everything after the first ';' char.
+     */
+    for (i = 0; i < strlen(uri); i++) {
+        if (i == JK_MAX_URI_LEN) {
+            jk_log(l, JK_LOG_WARNING,
+                   "Uri %s is invalid. Uri must be smaller then %d chars",
+                   uri, JK_MAX_URI_LEN);
+            JK_TRACE_EXIT(l);
+            return NULL;
+        }
+        if (uri[i] == ';')
+            break;
+        else
+            url[i] = uri[i];
+    }
+    url[i] = '\0';
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG, "Attempting to map original URI '%s' from %d maps",
+               url, uw_map->size);
+    rc = unescape_url(url);
+    if (rc == BAD_REQUEST) {
+        jk_log(l, JK_LOG_INFO, "Invalid request while unescaping original URI '%s'", url);
+        return NULL;
+    }
+    else if (rc == BAD_PATH) {
+        jk_log(l, JK_LOG_INFO, "Invalid path while unescaping URI '%s'", url);
+        return NULL;
+    }
+    normalize_url(url);
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG, "Attempting to map normalized URI '%s' from %d maps",
+               url, uw_map->size);
+
+    if (JK_IS_DEBUG_LEVEL(l)) {
+        char *url_rewrite = strstr(uri, JK_PATH_SESSION_IDENTIFIER);
+        if (url_rewrite)
+            jk_log(l, JK_LOG_DEBUG, "separating session identifier '%s' from url '%s'",
+                   url_rewrite, uri);
+    }
+    for (i = 0; i < uw_map->size; i++) {
+        uri_worker_record_t *uwr = uw_map->maps[i];
+
+        /* Check for match types */
+        if ((uwr->match_type & MATCH_TYPE_DISABLED) ||
+            (uwr->match_type & MATCH_TYPE_NO_MATCH))
+            continue;
+
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG, "Attempting to map context URI '%s=%s' source '%s'",
+                   uwr->context, uwr->worker_name, uri_worker_map_get_source(uwr, l));
+
+        if (uwr->match_type & MATCH_TYPE_WILDCHAR_PATH) {
+            const char *wname;
+            /* Map is already sorted by context_len */
+            if (wildchar_match(url, uwr->context,
+#ifdef WIN32
+                               0
+#else
+                               0
+#endif
+                               ) == 0) {
+                    wname = uwr->worker_name;
+                    if (JK_IS_DEBUG_LEVEL(l))
+                        jk_log(l, JK_LOG_DEBUG,
+                               "Found a wildchar match '%s=%s'",
+                               uwr->context, uwr->worker_name);
+                    JK_TRACE_EXIT(l);
+                    rv = wname;
+                    goto cleanup;
+             }
+        }
+        else if (JK_STRNCMP(uwr->context, url, uwr->context_len) == 0) {
+            if (strlen(url) == uwr->context_len) {
+                if (JK_IS_DEBUG_LEVEL(l))
+                    jk_log(l, JK_LOG_DEBUG,
+                           "Found an exact match '%s=%s'",
+                           uwr->context, uwr->worker_name);
+                JK_TRACE_EXIT(l);
+                rv = uwr->worker_name;
+                goto cleanup;
+            }
+        }
+    }
+    /* No matches found */
+    JK_TRACE_EXIT(l);
+
+cleanup:
+    if (rv && uw_map->nosize) {
+        if (is_nomap_match(uw_map, url, rv, l)) {
+            if (JK_IS_DEBUG_LEVEL(l))
+                jk_log(l, JK_LOG_DEBUG,
+                       "Denying matching for worker %s by nomatch rule",
+                       rv);
+            rv = NULL;
+        }
+    }
+    return rv;
+}
+
+int uri_worker_map_load(jk_uri_worker_map_t *uw_map,
+                        jk_logger_t *l)
+{
+    int rc = JK_FALSE;
+    jk_map_t *map;
+
+    jk_map_alloc(&map);
+    if (jk_map_read_properties(map, uw_map->fname,
+                               &uw_map->modified, 0, l)) {
+        int i;
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "Loading urimaps from %s with reload check interval %d seconds",
+                   uw_map->fname, uw_map->reload);
+        uri_worker_map_clear(uw_map, SOURCE_TYPE_URIMAP, l);
+        for (i = 0; i < jk_map_size(map); i++) {
+            const char *u = jk_map_name_at(map, i);
+            const char *w = jk_map_value_at(map, i);
+            /* Multiple mappings like :
+             * /servlets-examples|/ *
+             * will create two mappings:
+             * /servlets-examples
+             * and:
+             * /servlets-examples/ *
+             */
+            if (strchr(u, '|')) {
+                char *s, *r = strdup(u);
+                s = strchr(r, '|');
+                *(s++) = '\0';
+                /* Add first mapping */
+                if (!uri_worker_map_add(uw_map, r, w, SOURCE_TYPE_URIMAP, l)) {
+                    jk_log(l, JK_LOG_ERROR,
+                    "invalid mapping rule %s->%s", r, w);
+                }
+                for (; *s; s++)
+                   *(s - 1) = *s;
+                *(s - 1) = '\0';
+                /* add second mapping */
+                if (!uri_worker_map_add(uw_map, r, w, SOURCE_TYPE_URIMAP, l)) {
+                    jk_log(l, JK_LOG_ERROR,
+                    "invalid mapping rule %s->%s", r, w);
+                }
+                free(r);
+            }
+            else if (!uri_worker_map_add(uw_map, u, w, SOURCE_TYPE_URIMAP, l)) {
+                jk_log(l, JK_LOG_ERROR,
+                       "invalid mapping rule %s->%s",
+                       u, w);
+            }
+        }
+        uw_map->checked = time(NULL);
+        rc = JK_TRUE;
+    }
+    jk_map_free(&map);
+    return rc;
+}
+
+int uri_worker_map_update(jk_uri_worker_map_t *uw_map,
+                          jk_logger_t *l)
+{
+    int rc = JK_TRUE;
+    time_t now = time(NULL);
+
+    if (uw_map->reload > 0 && difftime(now, uw_map->checked) > uw_map->reload) {
+        struct stat statbuf;
+        uw_map->checked = now;
+        if ((rc = jk_stat(uw_map->fname, &statbuf)) == -1) {
+            jk_log(l, JK_LOG_ERROR,
+                   "Unable to stat the %s (errno=%d)",
+                   uw_map->fname, errno);
+            return JK_FALSE;
+        }
+        if (statbuf.st_mtime == uw_map->modified) {
+            if (JK_IS_DEBUG_LEVEL(l))
+                jk_log(l, JK_LOG_DEBUG,
+                       "File %s  is not modified",
+                       uw_map->fname);
+            return JK_TRUE;
+        }
+        JK_ENTER_CS(&(uw_map->cs), rc);
+        /* Check if some other thread updated status */
+        if (statbuf.st_mtime == uw_map->modified) {
+            JK_LEAVE_CS(&(uw_map->cs), rc);
+            if (JK_IS_DEBUG_LEVEL(l))
+                jk_log(l, JK_LOG_DEBUG,
+                       "File %s  is not modified",
+                       uw_map->fname);
+            return JK_TRUE;
+        }
+        rc = uri_worker_map_load(uw_map, l);
+        JK_LEAVE_CS(&(uw_map->cs), rc);
+        jk_log(l, JK_LOG_INFO,
+               "Reloaded urimaps from %s", uw_map->fname);
+    }
+    return JK_TRUE;
+}
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..53e1422
--- /dev/null
+++ b/connectors/jk/native/common/jk_uri_worker_map.h
@@ -0,0 +1,153 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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"
+#include "jk_mt.h"
+
+#define MATCH_TYPE_EXACT            0x0001
+/* deprecated
+#define MATCH_TYPE_CONTEXT          0x0002
+ */
+/* match all context path URIs with a path component suffix */
+/* deprecated
+#define MATCH_TYPE_CONTEXT_PATH     0x0004
+ */
+/* deprecated
+#define MATCH_TYPE_SUFFIX           0x0010
+ */
+/* match all URIs of the form *ext */
+/* deprecated
+#define MATCH_TYPE_GENERAL_SUFFIX   0x0020
+ */
+/* match multiple wild characters (*) and (?) */
+#define MATCH_TYPE_WILDCHAR_PATH    0x0040
+#define MATCH_TYPE_NO_MATCH         0x1000
+#define MATCH_TYPE_DISABLED         0x2000
+/* deprecated
+#define MATCH_TYPE_STOPPED          0x4000
+ */
+
+#define SOURCE_TYPE_WORKERDEF       0x0001
+#define SOURCE_TYPE_JKMOUNT         0x0002
+#define SOURCE_TYPE_URIMAP          0x0003
+#define SOURCE_TYPE_DISCOVER        0x0004
+#define SOURCE_TYPE_TEXT_WORKERDEF  ("worker definition")
+#define SOURCE_TYPE_TEXT_JKMOUNT    ("JkMount")
+#define SOURCE_TYPE_TEXT_URIMAP     ("uriworkermap")
+#define SOURCE_TYPE_TEXT_DISCOVER   ("ajp14")
+
+#define JK_MAX_URI_LEN              4095
+struct uri_worker_record
+{
+    /* Original uri for logging */
+    char *uri;
+
+    /* Name of worker mapped */
+    const char *worker_name;
+
+    /* Base context */
+    const char *context;
+
+    /* Match type */
+    unsigned int match_type;
+
+    /* Definition source type */
+    unsigned int source_type;
+
+    /* char length of the context */
+    size_t context_len;
+};
+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[BIG_POOL_SIZE];
+
+    /* map URI->WORKER */
+    uri_worker_record_t **maps;
+    
+    /* Map Number */
+    unsigned int size;
+
+    /* Map Capacity */
+    unsigned int capacity;
+
+    /* NoMap Number */
+    unsigned int nosize;
+
+    /* Dynamic config support */
+
+    JK_CRIT_SEC cs;
+    /* uriworkermap filename */
+    const char *fname;    
+    /* uriworkermap reload check interval */
+    int reload;    
+    /* Last modified time */
+    time_t  modified;
+    /* Last checked time */
+    time_t  checked;
+};
+typedef struct jk_uri_worker_map jk_uri_worker_map_t;
+
+const char *uri_worker_map_get_source(uri_worker_record_t *uwr, jk_logger_t *l);
+
+char *uri_worker_map_get_match(uri_worker_record_t *uwr, char *buf, jk_logger_t *l);
+
+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,
+                       const char *puri, const char *worker,
+                       unsigned int source_type, jk_logger_t *l);
+
+const char *map_uri_to_worker(jk_uri_worker_map_t *uw_map,
+                              const char *uri, jk_logger_t *l);
+
+int uri_worker_map_load(jk_uri_worker_map_t *uw_map,
+                        jk_logger_t *l);
+
+int uri_worker_map_update(jk_uri_worker_map_t *uw_map,
+                          jk_logger_t *l);
+
+#ifdef __cplusplus
+}
+#endif    /* __cplusplus */
+#endif    /* JK_URI_WORKER_MAP_H */
diff --git a/connectors/jk/native/common/jk_url.c b/connectors/jk/native/common/jk_url.c
new file mode 100644
index 0000000..95d590b
--- /dev/null
+++ b/connectors/jk/native/common/jk_url.c
@@ -0,0 +1,218 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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: URL manupilation subroutines. (ported from mod_proxy).     *
+ * Version:     $Revision: 531816 $                                        *
+ ***************************************************************************/
+
+#include "jk_global.h"
+#include "jk_url.h"
+
+#ifdef HAVE_APR
+#define JK_ISXDIGIT(x) apr_isxdigit((x))
+#define JK_ISDIGIT(x)  apr_isdigit((x))
+#define JK_ISUPPER(x)  apr_isupper((x))
+#define JK_ISALNUM(x)  apr_isalnum((x))
+#else
+#define JK_ISXDIGIT(x) isxdigit((int)(unsigned char)((x)))
+#define JK_ISDIGIT(x)  isdigit((int)(unsigned char)((x)))
+#define JK_ISUPPER(x)  isupper((int)(unsigned char)((x)))
+#define JK_ISALNUM(x)  isalnum((int)(unsigned char)((x)))
+#endif
+
+/* already called in the knowledge that the characters are hex digits */
+static  int jk_hex2c(const char *x)
+{
+    int i, ch;
+
+#if !CHARSET_EBCDIC
+    ch = x[0];
+    if (JK_ISDIGIT(ch)) {
+        i = ch - '0';
+    }
+    else if (JK_ISUPPER(ch)) {
+        i = ch - ('A' - 10);
+    }
+    else {
+        i = ch - ('a' - 10);
+    }
+    i <<= 4;
+
+    ch = x[1];
+    if (JK_ISDIGIT(ch)) {
+        i += ch - '0';
+    }
+    else if (JK_ISUPPER(ch)) {
+        i += ch - ('A' - 10);
+    }
+    else {
+        i += ch - ('a' - 10);
+    }
+    return i;
+#else /*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;
+        jk_xlate_from_ascii(buf, 1);
+        return buf[0];
+    }
+    else {
+        return 0;
+    }
+#endif /*CHARSET_EBCDIC*/
+}
+
+static void jk_c2hex(int ch, char *x)
+{
+#if !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 /*CHARSET_EBCDIC*/
+    static const char ntoa[] = { "0123456789ABCDEF" };
+    char buf[1];
+
+    ch &= 0xFF;
+
+    buf[0] = ch;
+    jk_xlate_to_ascii(buf, 1);
+
+    x[0] = '%';
+    x[1] = ntoa[(buf[0] >> 4) & 0x0F];
+    x[2] = ntoa[buf[0] & 0x0F];
+    x[3] = '\0';
+#endif /*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.
+ */
+char * jk_canonenc(char *y, const char *x, int len,
+                                       enum enctype t, int forcedec,
+                                       int proxyreq)
+{
+    int i, j, ch;
+    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. do not decode reverse proxied URLs
+ * unless specifically forced
+ */
+        if ((forcedec || (proxyreq && proxyreq != JK_PROXYREQ_REVERSE)) && ch == '%') {
+            if (!JK_ISXDIGIT(x[i + 1]) || !JK_ISXDIGIT(x[i + 2])) {
+                return NULL;
+            }
+            ch = jk_hex2c(&x[i + 1]);
+            i += 2;
+            if (ch != 0 && strchr(reserved, ch)) {  /* keep it encoded */
+                jk_c2hex(ch, &y[j]);
+                j += 2;
+                continue;
+            }
+        }
+/* recode it, if necessary */
+        if (!JK_ISALNUM(ch) && !strchr(allowed, ch)) {
+            jk_c2hex(ch, &y[j]);
+            j += 2;
+        }
+        else {
+            y[j] = ch;
+        }
+    }
+    y[j] = '\0';
+    return y;
+}
diff --git a/connectors/jk/native/common/jk_url.h b/connectors/jk/native/common/jk_url.h
new file mode 100644
index 0000000..2311b20
--- /dev/null
+++ b/connectors/jk/native/common/jk_url.h
@@ -0,0 +1,54 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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: URL manupilation subroutines header file. (mod_proxy)      *
+ * Version:     $Revision: 500534 $                                        *
+ ***************************************************************************/
+#ifndef _JK_URL_H
+#define _JK_URL_H
+
+#include "jk_global.h"
+#include "jk_pool.h"
+#include "jk_util.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif                          /* __cplusplus */
+
+/* for proxy_canonenc() */
+enum enctype {
+    enc_path, enc_search, enc_user, enc_fpath, enc_parm
+};
+
+#define JK_PROXYREQ_NONE 0         /**< No proxy */
+#define JK_PROXYREQ_PROXY 1        /**< Standard proxy */
+#define JK_PROXYREQ_REVERSE 2      /**< Reverse proxy */
+#define JK_PROXYREQ_RESPONSE 3     /**< Origin response */
+
+/*
+ * Do a canonical encoding of the x url y contains the result
+ * and should have a size of at least 3 * len + 1 bytes.
+ */
+char * jk_canonenc(char *y, const char *x, int len,
+                   enum enctype t, int forcedec, int proxyreq);
+
+#ifdef __cplusplus
+}
+#endif  /* __cplusplus */
+#endif  /* _JK_URL_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..3de6c3e
--- /dev/null
+++ b/connectors/jk/native/common/jk_util.c
@@ -0,0 +1,1798 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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:      Rainer Jung <rjung@apache.org>                             *
+ * Version:     $Revision$                                          *
+ ***************************************************************************/
+
+
+#include "jk_util.h"
+#include "jk_ajp12_worker.h"
+#include "jk_ajp13_worker.h"
+#include "jk_ajp14_worker.h"
+#include "jk_lb_worker.h"
+#include "jk_mt.h"
+
+#define SYSPROPS_OF_WORKER          ("sysprops")
+#define STDERR_OF_WORKER            ("stderr")
+#define STDOUT_OF_WORKER            ("stdout")
+#define SECRET_OF_WORKER            ("secret")
+#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 HOST_OF_WORKER              ("host")
+#define PORT_OF_WORKER              ("port")
+#define TYPE_OF_WORKER              ("type")
+#define CACHE_OF_WORKER_DEPRECATED  ("cachesize")
+#define CACHE_OF_WORKER             ("connection_pool_size")
+#define CACHE_OF_WORKER_MIN         ("connection_pool_minsize")
+#define CACHE_TIMEOUT_DEPRECATED    ("cache_timeout")
+#define CACHE_TIMEOUT_OF_WORKER     ("connection_pool_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_BUFFER_OF_WORKER     ("socket_buffer")
+#define SOCKET_KEEPALIVE_OF_WORKER  ("socket_keepalive")
+#define RECYCLE_TIMEOUT_DEPRECATED  ("recycle_timeout")
+#define LOAD_FACTOR_OF_WORKER       ("lbfactor")
+#define DISTANCE_OF_WORKER          ("distance")
+#define BALANCED_WORKERS_DEPRECATED ("balanced_workers")
+#define BALANCE_WORKERS             ("balance_workers")
+#define STICKY_SESSION              ("sticky_session")
+#define STICKY_SESSION_FORCE        ("sticky_session_force")
+#define LOCAL_WORKER_DEPRECATED     ("local_worker")
+#define LOCAL_WORKER_ONLY_DEPRECATED ("local_worker_only")
+#define JVM_ROUTE_OF_WORKER_DEPRECATED ("jvm_route")
+#define ROUTE_OF_WORKER             ("route")
+#define DOMAIN_OF_WORKER            ("domain")
+#define REDIRECT_OF_WORKER          ("redirect")
+#define MOUNT_OF_WORKER             ("mount")
+#define METHOD_OF_WORKER            ("method")
+#define LOCK_OF_WORKER              ("lock")
+#define IS_WORKER_DISABLED_DEPRECATED ("disabled")
+#define IS_WORKER_STOPPED_DEPRECATED  ("stopped")
+#define ACTIVATION_OF_WORKER        ("activation")
+#define WORKER_RECOVER_TIME         ("recover_time")
+#define WORKER_MAX_PACKET_SIZE      ("max_packet_size")
+#define STYLE_SHEET_OF_WORKER       ("css")
+#define NAMESPACE_OF_WORKER         ("ns")
+#define XML_NAMESPACE_OF_WORKER     ("xmlns")
+#define XML_DOCTYPE_OF_WORKER       ("doctype")
+#define PROP_PREFIX_OF_WORKER       ("prefix")
+
+#define READ_ONLY_OF_WORKER         ("read_only")
+#define USER_OF_WORKER              ("user")
+#define USER_CASE_OF_WORKER         ("user_case_insensitive")
+#define GOOD_RATING_OF_WORKER       ("good")
+#define BAD_RATING_OF_WORKER        ("bad")
+
+#define DEFAULT_WORKER_TYPE         JK_AJP13_WORKER_NAME
+#define SECRET_KEY_OF_WORKER        ("secretkey")
+#define RETRIES_OF_WORKER           ("retries")
+#define STATUS_FAIL_OF_WORKER       ("fail_on_status")
+
+#define DEFAULT_WORKER              JK_AJP13_WORKER_NAME
+#define WORKER_LIST_PROPERTY_NAME     ("worker.list")
+#define LIST_PROPERTY_NAME            ("list")
+#define WORKER_MAINTAIN_PROPERTY_NAME ("worker.maintain")
+#define MAINTAIN_PROPERTY_NAME        ("maintain")
+#define DEFAULT_MAINTAIN_TIME       (60)
+#define DEFAULT_LB_FACTOR           (1)
+#define DEFAULT_DISTANCE            (0)
+
+#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 MAKE_WORKER_PARAM(P)     \
+        strcpy(buf, "worker.");  \
+        strcat(buf, wname);      \
+        strcat(buf, ".");        \
+        strcat(buf, P)
+
+/*
+ * 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
+
+/* Visual C++ Toolkit 2003 support */
+#if defined (_MSC_VER) && (_MSC_VER == 1310)
+    extern long _ftol(double); /* defined by VC6 C libs */
+    extern long _ftol2(double dblSource) { return _ftol(dblSource); }
+#endif
+
+static const char *list_properties[] = {
+    BALANCE_WORKERS,
+    MOUNT_OF_WORKER,
+    USER_OF_WORKER,
+    GOOD_RATING_OF_WORKER,
+    BAD_RATING_OF_WORKER,
+    STATUS_FAIL_OF_WORKER,
+    "list",
+    NULL
+};
+
+static const char *unique_properties[] = {
+    SECRET_OF_WORKER,
+    HOST_OF_WORKER,
+    PORT_OF_WORKER,
+    TYPE_OF_WORKER,
+    CACHE_OF_WORKER_DEPRECATED,
+    CACHE_OF_WORKER,
+    CACHE_OF_WORKER_MIN,
+    CACHE_TIMEOUT_DEPRECATED,
+    CACHE_TIMEOUT_OF_WORKER,
+    RECOVERY_OPTS_OF_WORKER,
+    CONNECT_TIMEOUT_OF_WORKER,
+    PREPOST_TIMEOUT_OF_WORKER,
+    REPLY_TIMEOUT_OF_WORKER,
+    SOCKET_TIMEOUT_OF_WORKER,
+    SOCKET_BUFFER_OF_WORKER,
+    SOCKET_KEEPALIVE_OF_WORKER,
+    RECYCLE_TIMEOUT_DEPRECATED,
+    LOAD_FACTOR_OF_WORKER,
+    STICKY_SESSION,
+    STICKY_SESSION_FORCE,
+    LOCAL_WORKER_DEPRECATED,
+    LOCAL_WORKER_ONLY_DEPRECATED,
+    JVM_ROUTE_OF_WORKER_DEPRECATED,
+    ROUTE_OF_WORKER,
+    DOMAIN_OF_WORKER,
+    REDIRECT_OF_WORKER,
+    METHOD_OF_WORKER,
+    LOCK_OF_WORKER,
+    IS_WORKER_DISABLED_DEPRECATED,
+    IS_WORKER_STOPPED_DEPRECATED,
+    ACTIVATION_OF_WORKER,
+    WORKER_RECOVER_TIME,
+    WORKER_MAX_PACKET_SIZE,
+    STYLE_SHEET_OF_WORKER,
+    READ_ONLY_OF_WORKER,
+    RETRIES_OF_WORKER,
+    WORKER_MAINTAIN_PROPERTY_NAME,
+    NAMESPACE_OF_WORKER,
+    XML_NAMESPACE_OF_WORKER,
+    XML_DOCTYPE_OF_WORKER,
+    PROP_PREFIX_OF_WORKER,
+    USER_CASE_OF_WORKER,
+    NULL
+};
+
+static const char *deprecated_properties[] = {
+    SYSPROPS_OF_WORKER,
+    STDERR_OF_WORKER,
+    STDOUT_OF_WORKER,
+    MX_OF_WORKER,
+    MS_OF_WORKER,
+    CP_OF_WORKER,
+    BRIDGE_OF_WORKER,
+    JVM_OF_WORKER,
+    LIBPATH_OF_WORKER,
+    CMD_LINE_OF_WORKER,
+    NATIVE_LIB_OF_WORKER,
+    CACHE_OF_WORKER_DEPRECATED,
+    CACHE_TIMEOUT_DEPRECATED,
+    RECYCLE_TIMEOUT_DEPRECATED,
+    BALANCED_WORKERS_DEPRECATED,
+    JVM_ROUTE_OF_WORKER_DEPRECATED,
+    LOCAL_WORKER_DEPRECATED,
+    LOCAL_WORKER_ONLY_DEPRECATED,
+    IS_WORKER_DISABLED_DEPRECATED,
+    IS_WORKER_STOPPED_DEPRECATED,
+    NULL
+};
+
+static const char *supported_properties[] = {
+    SYSPROPS_OF_WORKER,
+    STDERR_OF_WORKER,
+    STDOUT_OF_WORKER,
+    SECRET_OF_WORKER,
+    MX_OF_WORKER,
+    MS_OF_WORKER,
+    CP_OF_WORKER,
+    BRIDGE_OF_WORKER,
+    JVM_OF_WORKER,
+    LIBPATH_OF_WORKER,
+    CMD_LINE_OF_WORKER,
+    NATIVE_LIB_OF_WORKER,
+    HOST_OF_WORKER,
+    PORT_OF_WORKER,
+    TYPE_OF_WORKER,
+    CACHE_OF_WORKER_DEPRECATED,
+    CACHE_OF_WORKER,
+    CACHE_OF_WORKER_MIN,
+    CACHE_TIMEOUT_DEPRECATED,
+    CACHE_TIMEOUT_OF_WORKER,
+    RECOVERY_OPTS_OF_WORKER,
+    CONNECT_TIMEOUT_OF_WORKER,
+    PREPOST_TIMEOUT_OF_WORKER,
+    REPLY_TIMEOUT_OF_WORKER,
+    SOCKET_TIMEOUT_OF_WORKER,
+    SOCKET_BUFFER_OF_WORKER,
+    SOCKET_KEEPALIVE_OF_WORKER,
+    RECYCLE_TIMEOUT_DEPRECATED,
+    LOAD_FACTOR_OF_WORKER,
+    DISTANCE_OF_WORKER,
+    BALANCED_WORKERS_DEPRECATED,
+    BALANCE_WORKERS,
+    STICKY_SESSION,
+    STICKY_SESSION_FORCE,
+    LOCAL_WORKER_DEPRECATED,
+    LOCAL_WORKER_ONLY_DEPRECATED,
+    JVM_ROUTE_OF_WORKER_DEPRECATED,
+    ROUTE_OF_WORKER,
+    DOMAIN_OF_WORKER,
+    REDIRECT_OF_WORKER,
+    MOUNT_OF_WORKER,
+    METHOD_OF_WORKER,
+    LOCK_OF_WORKER,
+    IS_WORKER_DISABLED_DEPRECATED,
+    IS_WORKER_STOPPED_DEPRECATED,
+    ACTIVATION_OF_WORKER,
+    WORKER_RECOVER_TIME,
+    WORKER_MAX_PACKET_SIZE,
+    STYLE_SHEET_OF_WORKER,
+    NAMESPACE_OF_WORKER,
+    XML_NAMESPACE_OF_WORKER,
+    XML_DOCTYPE_OF_WORKER,
+    PROP_PREFIX_OF_WORKER,
+    READ_ONLY_OF_WORKER,
+    USER_OF_WORKER,
+    USER_CASE_OF_WORKER,
+    GOOD_RATING_OF_WORKER,
+    BAD_RATING_OF_WORKER,
+    SECRET_KEY_OF_WORKER,
+    RETRIES_OF_WORKER,
+    STATUS_FAIL_OF_WORKER,
+    LIST_PROPERTY_NAME,
+    MAINTAIN_PROPERTY_NAME
+};
+
+/* All entries need to have fixed length 8 chars! */
+static const char *jk_level_verbs[] = {
+    "[" JK_LOG_TRACE_VERB "] ",
+    "[" JK_LOG_DEBUG_VERB "] ",
+    "[" JK_LOG_INFO_VERB "]  ",
+    "[" JK_LOG_WARN_VERB "]  ",
+    "[" JK_LOG_ERROR_VERB "] ",
+    "[" JK_LOG_EMERG_VERB "] ",
+    NULL
+};
+
+const char *jk_get_bool(int v)
+{
+    if (v == 0)
+        return "False";
+    else
+        return "True";
+}
+
+int jk_get_bool_code(const char *v, int def)
+{
+    if (!v) {
+        return def;
+    }
+    else if (!strcasecmp(v, "off") ||
+             *v == 'F' || *v == 'f' ||
+             *v == 'N' || *v == 'n' ||
+             *v == '0') {
+        return 0;
+    }
+    else if (!strcasecmp(v, "on") ||
+             *v == 'T' || *v == 't' ||
+             *v == 'Y' || *v == 'y' ||
+             *v == '1') {
+        return 1;
+    }
+    return def;
+}
+
+/* Sleep for 100ms */
+void jk_sleep(int ms)
+{
+#ifdef OS2
+    DosSleep(ms);
+#elif defined(BEOS)
+    snooze(ms * 1000);
+#elif defined(NETWARE)
+    delay(ms);
+#elif defined(WIN32)
+    Sleep(ms);
+#else
+    struct timeval tv;
+    tv.tv_usec = ms * 1000;
+    tv.tv_sec = 0;
+    select(0, NULL, NULL, NULL, &tv);
+#endif
+}
+
+static int set_time_str(char *str, int len, const char *jk_log_fmt)
+{
+    time_t t = time(NULL);
+    struct tm *tms;
+
+    tms = localtime(&t);
+    if (jk_log_fmt)
+        return (int)strftime(str, len, jk_log_fmt, tms);
+    return (int)strftime(str, len, JK_TIME_FORMAT, tms);
+}
+
+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) {
+        jk_file_logger_t *p = l->logger_private;
+        if (p->logfile) {
+            fputs(what, p->logfile);
+            fputc('\n', 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_TRACE_VERB)) {
+        return JK_LOG_TRACE_LEVEL;
+    }
+
+    if (0 == strcasecmp(level, JK_LOG_DEBUG_VERB)) {
+        return JK_LOG_DEBUG_LEVEL;
+    }
+
+    if (0 == strcasecmp(level, JK_LOG_INFO_VERB)) {
+        return JK_LOG_INFO_LEVEL;
+    }
+
+    if (0 == strcasecmp(level, JK_LOG_WARN_VERB)) {
+        return JK_LOG_WARNING_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_DEF_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));
+        jk_file_logger_t *p = (jk_file_logger_t *) malloc(sizeof(jk_file_logger_t));
+        if (rc && p) {
+            rc->log = log_to_file;
+            rc->level = level;
+            rc->log_fmt = NULL;
+            rc->logger_private = p;
+#if defined(AS400) && !defined(AS400_UTF8)
+            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) {
+        jk_file_logger_t *p = (*l)->logger_private;
+        if (p) {
+            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, const char *funcname, int level,
+           const char *fmt, ...)
+{
+    int rc = 0;
+    /* Need to reserve space for terminating zero byte. */
+    static int usable_size = HUGE_BUFFER_SIZE - 1;
+    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 NETWARE
+        buf = (char *)malloc(HUGE_BUFFER_SIZE);
+        if (NULL == buf)
+            return -1;
+#endif
+        used = set_time_str(buf, usable_size, l->log_fmt);
+
+        if (line) {
+            /* Log [pid:threadid] for all levels except REQUEST. */
+            /* This information helps to correlate lines from different logs. */
+            /* Performance is no issue, because with production log levels */
+            /* we only call it often, if we have a lot of errors */
+            rc = snprintf(&buf[used], usable_size - used,
+                             "[%04d:%04d] ", getpid(), jk_gettid());
+            used += rc;
+            if (rc < 0 || usable_size - used < 8) {
+                return 0;
+            }
+            strcat(buf, jk_level_verbs[level]);
+            used += 8;
+
+            if (funcname) {
+                rc = (int)strlen(funcname) + 2;
+                if (usable_size - used >= rc) {
+                    strcat(buf, funcname);
+                    strcat(buf, "::");
+                    used += rc;
+                }
+            }
+
+            rc = snprintf(&buf[used], usable_size - used,
+                             "%s (%d): ", f, line);
+            used += rc;
+            if (rc < 0 || usable_size - used < 0) {
+                return 0;           /* [V] not sure what to return... */
+            }
+        }
+
+        va_start(args, fmt);
+        rc = vsnprintf(buf + used, usable_size - used, fmt, args);
+        va_end(args);
+        if ( rc <= usable_size - used ) {
+            used += rc;
+        } else {
+            used = usable_size;
+        }
+        buf[used] = 0;
+        l->log(l, level, buf);
+
+#ifdef NETWARE
+        free(buf);
+#endif
+    }
+
+    return rc;
+}
+
+const char *jk_get_worker_type(jk_map_t *m, const char *wname)
+{
+    char buf[1024];
+
+    if (!m || !wname) {
+        return NULL;
+    }
+    MAKE_WORKER_PARAM(TYPE_OF_WORKER);
+    return jk_map_get_string(m, buf, DEFAULT_WORKER_TYPE);
+}
+
+const char *jk_get_worker_route(jk_map_t *m, const char *wname, const char *def)
+{
+    char buf[1024];
+    const char *v;
+    if (!m || !wname) {
+        return NULL;
+    }
+    MAKE_WORKER_PARAM(ROUTE_OF_WORKER);
+    v = jk_map_get_string(m, buf, NULL);
+    if (v) {
+        return v;
+    }
+    /* Try old jvm_route directive */
+    MAKE_WORKER_PARAM(JVM_ROUTE_OF_WORKER_DEPRECATED);
+    return jk_map_get_string(m, buf, def);
+}
+
+const char *jk_get_worker_domain(jk_map_t *m, const char *wname, const char *def)
+{
+    char buf[1024];
+    if (!m || !wname) {
+        return NULL;
+    }
+    MAKE_WORKER_PARAM(DOMAIN_OF_WORKER);
+    return jk_map_get_string(m, buf, def);
+}
+
+const char *jk_get_worker_redirect(jk_map_t *m, const char *wname, const char *def)
+{
+    char buf[1024];
+    if (!m || !wname) {
+        return NULL;
+    }
+   MAKE_WORKER_PARAM(REDIRECT_OF_WORKER);
+    return jk_map_get_string(m, buf, def);
+}
+
+const char *jk_get_worker_secret(jk_map_t *m, const char *wname)
+{
+    char buf[1024];
+
+    if (!m || !wname) {
+        return NULL;
+    }
+
+    MAKE_WORKER_PARAM(SECRET_OF_WORKER);
+
+    return jk_map_get_string(m, buf, NULL);
+}
+
+/* [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, const char **prop)
+{
+    char buf[1024];
+
+    if (m && prop && wname && pname) {
+        MAKE_WORKER_PARAM(pname);
+        *prop = jk_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;
+        MAKE_WORKER_PARAM(pname);
+        i = jk_map_get_int(m, buf, -1);
+        if (-1 != i) {
+            *prop = i;
+            return JK_TRUE;
+        }
+    }
+    return JK_FALSE;
+}
+
+const char *jk_get_worker_host(jk_map_t *m, const char *wname, const char *def)
+{
+    char buf[1024];
+
+    if (!m || !wname) {
+        return NULL;
+    }
+
+    MAKE_WORKER_PARAM(HOST_OF_WORKER);
+
+    return jk_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;
+    }
+
+    MAKE_WORKER_PARAM(PORT_OF_WORKER);
+
+    return jk_map_get_int(m, buf, def);
+}
+
+static int def_cache_size = -1;
+int jk_get_worker_def_cache_size(int protocol)
+{
+    if (def_cache_size < 1) {
+        if (protocol == AJP14_PROTO)
+            def_cache_size = AJP14_DEF_CACHE_SZ;
+        else
+            def_cache_size = AJP13_DEF_CACHE_SZ;
+    }
+    return def_cache_size;
+}
+
+void jk_set_worker_def_cache_size(int sz)
+{
+    def_cache_size = sz;
+}
+
+int jk_get_worker_cache_size(jk_map_t *m, const char *wname, int def)
+{
+    char buf[1024];
+    int rv;
+
+    if (!m || !wname) {
+        return -1;
+    }
+
+    MAKE_WORKER_PARAM(CACHE_OF_WORKER);
+    if ((rv = jk_map_get_int(m, buf, -1)) >= 0)
+        return rv;
+    MAKE_WORKER_PARAM(CACHE_OF_WORKER_DEPRECATED);
+    return jk_map_get_int(m, buf, def);
+}
+
+int jk_get_worker_cache_size_min(jk_map_t *m, const char *wname, int def)
+{
+    char buf[1024];
+
+    if (!m || !wname) {
+        return -1;
+    }
+
+    MAKE_WORKER_PARAM(CACHE_OF_WORKER_MIN);
+    return jk_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;
+    }
+
+    MAKE_WORKER_PARAM(SOCKET_TIMEOUT_OF_WORKER);
+
+    return jk_map_get_int(m, buf, def);
+}
+
+int jk_get_worker_recover_timeout(jk_map_t *m, const char *wname, int def)
+{
+    char buf[1024];
+
+    if (!m || !wname) {
+        return -1;
+    }
+
+    MAKE_WORKER_PARAM(WORKER_RECOVER_TIME);
+
+    return jk_map_get_int(m, buf, def);
+}
+
+int jk_get_worker_socket_buffer(jk_map_t *m, const char *wname, int def)
+{
+    char buf[1024];
+    int i;
+    if (!m || !wname) {
+        return -1;
+    }
+
+    MAKE_WORKER_PARAM(SOCKET_BUFFER_OF_WORKER);
+
+    i = jk_map_get_int(m, buf, 0);
+    if (i > 0 && i < def)
+        i = def;
+    return i;
+}
+
+int jk_get_worker_socket_keepalive(jk_map_t *m, const char *wname, int def)
+{
+    char buf[1024];
+
+    if (!m || !wname) {
+        return -1;
+    }
+
+    MAKE_WORKER_PARAM(SOCKET_KEEPALIVE_OF_WORKER);
+
+    return jk_map_get_bool(m, buf, def);
+}
+
+int jk_get_worker_cache_timeout(jk_map_t *m, const char *wname, int def)
+{
+    char buf[1024];
+    int rv;
+
+    if (!m || !wname) {
+        return -1;
+    }
+
+    MAKE_WORKER_PARAM(CACHE_TIMEOUT_OF_WORKER);
+    if ((rv = jk_map_get_int(m, buf, -1)) >= 0)
+        return rv;
+    MAKE_WORKER_PARAM(CACHE_TIMEOUT_DEPRECATED);
+
+    return jk_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;
+    }
+
+    MAKE_WORKER_PARAM(CONNECT_TIMEOUT_OF_WORKER);
+
+    return jk_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;
+    }
+
+    MAKE_WORKER_PARAM(PREPOST_TIMEOUT_OF_WORKER);
+
+    return jk_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;
+    }
+
+    MAKE_WORKER_PARAM(REPLY_TIMEOUT_OF_WORKER);
+
+    return jk_map_get_int(m, buf, def);
+}
+
+int jk_get_worker_recycle_timeout(jk_map_t *m, const char *wname, int def)
+{
+    return def;
+}
+
+int jk_get_worker_retries(jk_map_t *m, const char *wname, int def)
+{
+    char buf[1024];
+    int rv;
+    if (!m || !wname) {
+        return -1;
+    }
+
+    MAKE_WORKER_PARAM(RETRIES_OF_WORKER);
+
+    rv = jk_map_get_int(m, buf, def);
+    if (rv < 1)
+        rv = 1;
+
+    return rv;
+}
+
+int jk_get_worker_recovery_opts(jk_map_t *m, const char *wname, int def)
+{
+    char buf[1024];
+
+    if (!m || !wname) {
+        return -1;
+    }
+
+    MAKE_WORKER_PARAM(RECOVERY_OPTS_OF_WORKER);
+
+    return jk_map_get_int(m, buf, def);
+}
+
+const char *jk_get_worker_secret_key(jk_map_t *m, const char *wname)
+{
+    char buf[1024];
+
+    if (!m || !wname) {
+        return NULL;
+    }
+
+    MAKE_WORKER_PARAM(SECRET_KEY_OF_WORKER);
+    return jk_map_get_string(m, buf, NULL);
+}
+
+int jk_get_worker_list(jk_map_t *m, char ***list, unsigned *num_of_workers)
+{
+    if (m && list && num_of_workers) {
+        char **ar = jk_map_get_string_list(m,
+                                        WORKER_LIST_PROPERTY_NAME,
+                                        num_of_workers,
+                                        DEFAULT_WORKER);
+        if (ar) {
+            *list = ar;
+            return JK_TRUE;
+        }
+        *list = NULL;
+        *num_of_workers = 0;
+    }
+
+    return JK_FALSE;
+}
+
+int jk_get_is_worker_disabled(jk_map_t *m, const char *wname)
+{
+    int rc = JK_TRUE;
+    char buf[1024];
+    if (m && wname) {
+        int value;
+        MAKE_WORKER_PARAM(IS_WORKER_DISABLED_DEPRECATED);
+        value = jk_map_get_bool(m, buf, 0);
+        if (!value)
+            rc = JK_FALSE;
+    }
+    return rc;
+}
+
+int jk_get_is_worker_stopped(jk_map_t *m, const char *wname)
+{
+    int rc = JK_TRUE;
+    char buf[1024];
+    if (m && wname) {
+        int value;
+        MAKE_WORKER_PARAM(IS_WORKER_STOPPED_DEPRECATED);
+        value = jk_map_get_bool(m, buf, 0);
+        if (!value)
+            rc = JK_FALSE;
+    }
+    return rc;
+}
+
+int jk_get_worker_activation(jk_map_t *m, const char *wname)
+{
+    char buf[1024];
+    const char *v;
+    if (!m || !wname) {
+        return JK_LB_ACTIVATION_ACTIVE;
+    }
+
+    MAKE_WORKER_PARAM(ACTIVATION_OF_WORKER);
+    v = jk_map_get_string(m, buf, NULL);
+    if (v) {
+        return jk_lb_get_activation_code(v);
+    }
+    else if (jk_get_is_worker_stopped(m, wname))
+        return JK_LB_ACTIVATION_STOPPED;
+    else if (jk_get_is_worker_disabled(m, wname))
+        return JK_LB_ACTIVATION_DISABLED;
+    else
+        return JK_LB_ACTIVATION_DEF;
+}
+
+int jk_get_lb_factor(jk_map_t *m, const char *wname)
+{
+    char buf[1024];
+
+    if (!m || !wname) {
+        return DEFAULT_LB_FACTOR;
+    }
+
+    MAKE_WORKER_PARAM(LOAD_FACTOR_OF_WORKER);
+
+    return jk_map_get_int(m, buf, DEFAULT_LB_FACTOR);
+}
+
+int jk_get_distance(jk_map_t *m, const char *wname)
+{
+    char buf[1024];
+
+    if (!m || !wname) {
+        return DEFAULT_DISTANCE;
+    }
+
+    MAKE_WORKER_PARAM(DISTANCE_OF_WORKER);
+
+    return jk_map_get_int(m, buf, DEFAULT_DISTANCE);
+}
+
+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;
+        MAKE_WORKER_PARAM(STICKY_SESSION);
+        value = jk_map_get_bool(m, buf, 1);
+        if (!value)
+            rc = JK_FALSE;
+    }
+    return rc;
+}
+
+int jk_get_is_sticky_session_force(jk_map_t *m, const char *wname)
+{
+    int rc = JK_FALSE;
+    char buf[1024];
+    if (m && wname) {
+        int value;
+        MAKE_WORKER_PARAM(STICKY_SESSION_FORCE);
+        value = jk_map_get_bool(m, buf, 0);
+        if (value)
+            rc = JK_TRUE;
+    }
+    return rc;
+}
+
+int jk_get_lb_method(jk_map_t *m, const char *wname)
+{
+    char buf[1024];
+    const char *v;
+    if (!m || !wname) {
+        return JK_LB_METHOD_DEF;
+    }
+
+    MAKE_WORKER_PARAM(METHOD_OF_WORKER);
+    v = jk_map_get_string(m, buf, NULL);
+    return jk_lb_get_method_code(v);
+}
+
+int jk_get_lb_lock(jk_map_t *m, const char *wname)
+{
+    char buf[1024];
+    const char *v;
+    if (!m || !wname) {
+        return JK_LB_LOCK_DEF;
+    }
+
+    MAKE_WORKER_PARAM(LOCK_OF_WORKER);
+    v = jk_map_get_string(m, buf, NULL);
+    return jk_lb_get_lock_code(v);
+}
+
+int jk_get_max_packet_size(jk_map_t *m, const char *wname)
+{
+    char buf[1024];
+    int sz;
+
+    if (!m || !wname) {
+        return DEF_BUFFER_SZ;
+    }
+
+    MAKE_WORKER_PARAM(WORKER_MAX_PACKET_SIZE);
+    sz = jk_map_get_int(m, buf, DEF_BUFFER_SZ);
+    sz = JK_ALIGN(sz, 1024);
+    if (sz < DEF_BUFFER_SZ)
+        sz = DEF_BUFFER_SZ;
+    else if (sz > 64*1024)
+        sz = 64*1024;
+
+    return sz;
+}
+
+int jk_get_worker_fail_on_status(jk_map_t *m, const char *wname,
+                                 int *list, unsigned int list_size)
+{
+    char buf[1024];
+    if (!m || !wname || !list) {
+        return 0;
+    }
+    MAKE_WORKER_PARAM(STATUS_FAIL_OF_WORKER);
+    if (list_size) {
+        return jk_map_get_int_list(m, buf,
+                                   list, list_size,
+                                   NULL);
+    }
+
+    return 0;
+}
+
+int jk_get_worker_user_case_insensitive(jk_map_t *m, const char *wname)
+{
+    int rc = JK_FALSE;
+    char buf[1024];
+    if (m && wname) {
+        int value;
+        MAKE_WORKER_PARAM(USER_CASE_OF_WORKER);
+        value = jk_map_get_bool(m, buf, 0);
+        if (value)
+            rc = JK_TRUE;
+    }
+    return rc;
+
+}
+
+const char *jk_get_worker_style_sheet(jk_map_t *m, const char *wname, const char *def)
+{
+    char buf[1024];
+
+    if (!m || !wname) {
+        return NULL;
+    }
+
+    MAKE_WORKER_PARAM(STYLE_SHEET_OF_WORKER);
+
+    return jk_map_get_string(m, buf, def);
+}
+
+const char *jk_get_worker_name_space(jk_map_t *m, const char *wname, const char *def)
+{
+    const char *rc;
+    char buf[1024];
+    if (!m || !wname) {
+        return NULL;
+    }
+    MAKE_WORKER_PARAM(NAMESPACE_OF_WORKER);
+    rc = jk_map_get_string(m, buf, def);
+    if (*rc == '-')
+        return "";
+    else
+        return rc;
+}
+
+const char *jk_get_worker_xmlns(jk_map_t *m, const char *wname, const char *def)
+{
+    const char *rc;
+    char buf[1024];
+    if (!m || !wname) {
+        return NULL;
+    }
+    MAKE_WORKER_PARAM(XML_NAMESPACE_OF_WORKER);
+    rc = jk_map_get_string(m, buf, def);
+    if (*rc == '-')
+        return "";
+    else
+        return rc;
+}
+
+const char *jk_get_worker_xml_doctype(jk_map_t *m, const char *wname, const char *def)
+{
+    char buf[1024];
+    if (!m || !wname) {
+        return NULL;
+    }
+    MAKE_WORKER_PARAM(XML_DOCTYPE_OF_WORKER);
+    return jk_map_get_string(m, buf, def);
+}
+
+const char *jk_get_worker_prop_prefix(jk_map_t *m, const char *wname, const char *def)
+{
+    char buf[1024];
+    if (!m || !wname) {
+        return NULL;
+    }
+    MAKE_WORKER_PARAM(PROP_PREFIX_OF_WORKER);
+    return jk_map_get_string(m, buf, def);
+}
+
+int jk_get_is_read_only(jk_map_t *m, const char *wname)
+{
+    int rc = JK_FALSE;
+    char buf[1024];
+    if (m && wname) {
+        int value;
+        MAKE_WORKER_PARAM(READ_ONLY_OF_WORKER);
+        value = jk_map_get_bool(m, buf, 0);
+        if (value)
+            rc = JK_TRUE;
+    }
+    return rc;
+}
+
+int jk_get_worker_user_list(jk_map_t *m,
+                            const char *wname,
+                            char ***list, unsigned int *num)
+{
+    char buf[1024];
+
+    if (m && list && num && wname) {
+        char **ar = NULL;
+
+        MAKE_WORKER_PARAM(USER_OF_WORKER);
+        ar = jk_map_get_string_list(m, buf, num, NULL);
+        if (ar) {
+            *list = ar;
+            return JK_TRUE;
+        }
+        *list = NULL;
+        *num = 0;
+    }
+
+    return JK_FALSE;
+}
+
+int jk_get_worker_good_rating(jk_map_t *m,
+                              const char *wname,
+                              char ***list, unsigned int *num)
+{
+    char buf[1024];
+
+    if (m && list && num && wname) {
+        char **ar = NULL;
+
+        MAKE_WORKER_PARAM(GOOD_RATING_OF_WORKER);
+        ar = jk_map_get_string_list(m, buf, num, NULL);
+        if (ar) {
+            *list = ar;
+            return JK_TRUE;
+        }
+        *list = NULL;
+        *num = 0;
+    }
+
+    return JK_FALSE;
+}
+
+int jk_get_worker_bad_rating(jk_map_t *m,
+                             const char *wname,
+                             char ***list, unsigned int *num)
+{
+    char buf[1024];
+
+    if (m && list && num && wname) {
+        char **ar = NULL;
+
+        MAKE_WORKER_PARAM(BAD_RATING_OF_WORKER);
+        ar = jk_map_get_string_list(m, buf, num, NULL);
+        if (ar) {
+            *list = ar;
+            return JK_TRUE;
+        }
+        *list = NULL;
+        *num = 0;
+    }
+
+    return JK_FALSE;
+}
+
+int jk_get_lb_worker_list(jk_map_t *m,
+                          const char *wname,
+                          char ***list, unsigned int *num_of_workers)
+{
+    char buf[1024];
+
+    if (m && list && num_of_workers && wname) {
+        char **ar = NULL;
+
+        MAKE_WORKER_PARAM(BALANCE_WORKERS);
+        ar = jk_map_get_string_list(m, buf, num_of_workers, NULL);
+        if (ar) {
+            *list = ar;
+            return JK_TRUE;
+        }
+        /* Try old balanced_workers directive */
+        MAKE_WORKER_PARAM(BALANCED_WORKERS_DEPRECATED);
+        ar = jk_map_get_string_list(m, buf, num_of_workers, NULL);
+        if (ar) {
+            *list = ar;
+            return JK_TRUE;
+        }
+        *list = NULL;
+        *num_of_workers = 0;
+    }
+
+    return JK_FALSE;
+}
+
+int jk_get_worker_mount_list(jk_map_t *m,
+                             const char *wname,
+                             char ***list, unsigned int *num_of_maps)
+{
+    char buf[1024];
+
+    if (m && list && num_of_maps && wname) {
+        char **ar = NULL;
+
+        MAKE_WORKER_PARAM(MOUNT_OF_WORKER);
+        ar = jk_map_get_string_list(m, buf, num_of_maps, NULL);
+        if (ar) {
+            *list = ar;
+            return JK_TRUE;
+        }
+        *list = NULL;
+        *num_of_maps = 0;
+    }
+
+    return JK_FALSE;
+}
+
+int jk_get_worker_maintain_time(jk_map_t *m)
+{
+    return jk_map_get_int(m, WORKER_MAINTAIN_PROPERTY_NAME,
+                          DEFAULT_MAINTAIN_TIME);
+}
+
+int jk_get_worker_mx(jk_map_t *m, const char *wname, unsigned *mx)
+{
+    char buf[1024];
+
+    if (m && mx && wname) {
+        int i;
+        MAKE_WORKER_PARAM(MX_OF_WORKER);
+
+        i = jk_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;
+        MAKE_WORKER_PARAM(MS_OF_WORKER);
+
+        i = jk_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, const char **cp)
+{
+    char buf[1024];
+
+    if (m && cp && wname) {
+        MAKE_WORKER_PARAM(CP_OF_WORKER);
+
+        *cp = jk_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];
+    const char *type;
+
+    if (m && bt && wname) {
+        MAKE_WORKER_PARAM(BRIDGE_OF_WORKER);
+
+        type = jk_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, const char **vm_path)
+{
+    char buf[1024];
+
+    if (m && vm_path && wname) {
+        MAKE_WORKER_PARAM(JVM_OF_WORKER);
+
+        *vm_path = jk_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, const char **cb_path)
+{
+    char buf[1024];
+
+    if (m && cb_path && wname) {
+        MAKE_WORKER_PARAM(NATIVE_LIB_OF_WORKER);
+
+        *cb_path = jk_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, const char **cmd_line)
+{
+    char buf[1024];
+
+    if (m && cmd_line && wname) {
+        MAKE_WORKER_PARAM(CMD_LINE_OF_WORKER);
+
+        *cmd_line = jk_map_get_string(m, buf, NULL);
+        if (*cmd_line) {
+            return JK_TRUE;
+        }
+    }
+
+    return JK_FALSE;
+}
+
+
+int jk_stat(const char *f, struct stat * statbuf)
+{
+  int rc;
+/**
+ * i5/OS V5R4 expect filename in ASCII for fopen but required them in EBCDIC for stat()
+ */
+#ifdef AS400_UTF8
+  char *ptr;
+
+  ptr = (char *)malloc(strlen(f) + 1);
+  jk_ascii2ebcdic((char *)f, ptr);
+  rc = stat(ptr, statbuf);
+  free(ptr);
+#else
+  rc = stat(f, statbuf);
+#endif
+
+  return (rc);
+}
+
+
+int jk_file_exists(const char *f)
+{
+    if (f) {
+        struct stat st;
+
+        if ((0 == jk_stat(f, &st)) && (st.st_mode & S_IFREG))
+      return JK_TRUE;
+    }
+
+    return JK_FALSE;
+}
+
+static int jk_is_some_property(const char *prp_name, const char *suffix, const char *sep)
+{
+    char buf[1024];
+
+    if (prp_name && suffix) {
+        size_t prp_name_len;
+        size_t suffix_len;
+        strcpy(buf, sep);
+        strcat(buf, suffix);
+        prp_name_len = strlen(prp_name);
+        suffix_len = strlen(buf);
+        if (prp_name_len >= suffix_len) {
+            const char *prp_suffix = prp_name + prp_name_len - suffix_len;
+            if (0 == strcmp(buf, prp_suffix)) {
+                return JK_TRUE;
+            }
+        }
+    }
+
+    return JK_FALSE;
+}
+
+int jk_is_path_property(const char *prp_name)
+{
+    return jk_is_some_property(prp_name, "path", "_");
+}
+
+int jk_is_cmd_line_property(const char *prp_name)
+{
+    return jk_is_some_property(prp_name, CMD_LINE_OF_WORKER, ".");
+}
+
+int jk_is_list_property(const char *prp_name)
+{
+    const char **props = &list_properties[0];
+    while (*props) {
+        if (jk_is_some_property(prp_name, *props, "."))
+            return JK_TRUE;
+        props++;
+    }
+    return JK_FALSE;
+}
+
+int jk_is_unique_property(const char *prp_name)
+{
+    const char **props = &unique_properties[0];
+    while (*props) {
+        if (jk_is_some_property(prp_name, *props, "."))
+            return JK_TRUE;
+        props++;
+    }
+    return JK_FALSE;
+}
+
+int jk_is_deprecated_property(const char *prp_name)
+{
+    const char **props = &deprecated_properties[0];
+    while (*props) {
+        if (jk_is_some_property(prp_name, *props, "."))
+            return JK_TRUE;
+        props++;
+    }
+    return JK_FALSE;
+}
+/*
+ * Check that property is a valid one (to prevent user typos).
+ * Only property starting with worker.
+ */
+int jk_is_valid_property(const char *prp_name)
+{
+    const char **props;
+    if (memcmp(prp_name, "worker.", 7))
+        return JK_TRUE;
+
+    props = &supported_properties[0];
+    while (*props) {
+        if (jk_is_some_property(prp_name, *props, "."))
+            return JK_TRUE;
+        props++;
+    }
+    return JK_FALSE;
+}
+
+int jk_get_worker_stdout(jk_map_t *m, const char *wname, const char **stdout_name)
+{
+    char buf[1024];
+
+    if (m && stdout_name && wname) {
+        MAKE_WORKER_PARAM(STDOUT_OF_WORKER);
+
+        *stdout_name = jk_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, const char **stderr_name)
+{
+    char buf[1024];
+
+    if (m && stderr_name && wname) {
+        MAKE_WORKER_PARAM(STDERR_OF_WORKER);
+
+        *stderr_name = jk_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, const char **sysprops)
+{
+    char buf[1024];
+
+    if (m && sysprops && wname) {
+        MAKE_WORKER_PARAM(SYSPROPS_OF_WORKER);
+
+        *sysprops = jk_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, const char **libpath)
+{
+    char buf[1024];
+
+    if (m && libpath && wname) {
+        MAKE_WORKER_PARAM(LIBPATH_OF_WORKER);
+
+        *libpath = jk_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;
+#ifdef _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;
+#ifdef _REENTRANT
+                char *tmp = strtok_r(prps, "*", &lasts);
+#else
+                char *tmp = strtok(prps, "*");
+#endif
+
+                while (tmp && i < num_of_prps) {
+                    rc[i] = tmp;
+#ifdef _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->route = NULL;
+    s->retries = JK_RETRIES;
+    s->add_log_items = NULL;
+}
+
+#ifdef _MT_CODE_PTHREAD
+int jk_gettid()
+{
+    pthread_t t = pthread_self();
+#ifdef AS400
+    /* OS400 use 64 bits ThreadId, get only low 32 bits for now */
+    pthread_id_np_t       tid;
+    pthread_getunique_np(&t, &tid);
+    return ((int)(tid.intId.lo & 0xFFFFFFFF));
+#else
+    int tid = ((int)t) & 0xFFFF;
+    return tid;
+#endif /* AS400 */
+}
+#endif
+
+/***
+ * ASCII <-> EBCDIC conversions
+ *
+ * For now usefull only in i5/OS V5R4 where UTF and EBCDIC mode are mixed
+ */
+
+#ifdef AS400_UTF8
+
+/* EBCDIC to ASCII translation table */
+static u_char ebcdic_to_ascii[256] =
+{
+  0x00,0x01,0x02,0x03,0x20,0x09,0x20,0x7f, /* 00-07 */
+  0x20,0x20,0x20,0x0b,0x0c,0x0d,0x0e,0x0f, /* 08-0f */
+  0x10,0x11,0x12,0x13,0x20,0x0a,0x08,0x20, /* 10-17 */
+  0x18,0x19,0x20,0x20,0x20,0x1d,0x1e,0x1f, /* 18-1f */
+  0x20,0x20,0x1c,0x20,0x20,0x0a,0x17,0x1b, /* 20-27 */
+  0x20,0x20,0x20,0x20,0x20,0x05,0x06,0x07, /* 28-2f */
+  0x20,0x20,0x16,0x20,0x20,0x20,0x20,0x04, /* 30-37 */
+  0x20,0x20,0x20,0x20,0x14,0x15,0x20,0x1a, /* 38-3f */
+  0x20,0x20,0x83,0x84,0x85,0xa0,0xc6,0x86, /* 40-47 */
+  0x87,0xa4,0xbd,0x2e,0x3c,0x28,0x2b,0x7c, /* 48-4f */
+  0x26,0x82,0x88,0x89,0x8a,0xa1,0x8c,0x8b, /* 50-57 */
+  0x8d,0xe1,0x21,0x24,0x2a,0x29,0x3b,0xaa, /* 58-5f */
+  0x2d,0x2f,0xb6,0x8e,0xb7,0xb5,0xc7,0x8f, /* 60-67 */
+  0x80,0xa5,0xdd,0x2c,0x25,0x5f,0x3e,0x3f, /* 68-6f */
+  0x9b,0x90,0xd2,0xd3,0xd4,0xd6,0xd7,0xd8, /* 70-77 */
+  0xde,0x60,0x3a,0x23,0x40,0x27,0x3d,0x22, /* 78-7f */
+  0x9d,0x61,0x62,0x63,0x64,0x65,0x66,0x67, /* 80-87 */
+  0x68,0x69,0xae,0xaf,0xd0,0xec,0xe7,0xf1, /* 88-8f */
+  0xf8,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,0x70, /* 90-97 */
+  0x71,0x72,0xa6,0xa7,0x91,0xf7,0x92,0xcf, /* 98-9f */
+  0xe6,0x7e,0x73,0x74,0x75,0x76,0x77,0x78, /* a8-a7 */
+  0x79,0x7a,0xad,0xa8,0xd1,0xed,0xe8,0xa9, /* a8-af */
+  0x5e,0x9c,0xbe,0xfa,0xb8,0x15,0x14,0xac, /* b0-b7 */
+  0xab,0xf3,0x5b,0x5d,0xee,0xf9,0xef,0x9e, /* b8-bf */
+  0x7b,0x41,0x42,0x43,0x44,0x45,0x46,0x47, /* c0-c7 */
+  0x48,0x49,0xf0,0x93,0x94,0x95,0xa2,0xe4, /* c8-cf */
+  0x7d,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50, /* d0-d7 */
+  0x51,0x52,0xfb,0x96,0x81,0x97,0xa3,0x98, /* d8-df */
+  0x5c,0xf6,0x53,0x54,0x55,0x56,0x57,0x58, /* e0-e7 */
+  0x59,0x5a,0xfc,0xe2,0x99,0xe3,0xe0,0xe5, /* e8-ef */
+  0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, /* f0-f7 */
+  0x38,0x39,0xfd,0xea,0x9a,0xeb,0xe9,0xff  /* f8-ff */
+};
+
+/* ASCII to EBCDIC translation table */
+static u_char ascii_to_ebcdic[256] =
+{
+  0x00,0x01,0x02,0x03,0x37,0x2d,0x2e,0x2f, /* 00-07 */
+  0x16,0x05,0x25,0x0b,0x0c,0x0d,0x0e,0x0f, /* 08-0f */
+  0x10,0x11,0x12,0x13,0x3c,0x3d,0x32,0x26, /* 10-17 */
+  0x18,0x19,0x3f,0x27,0x22,0x1d,0x1e,0x1f, /* 18-1f */
+  0x40,0x5a,0x7f,0x7b,0x5b,0x6c,0x50,0x7d, /* 20-27 */
+  0x4d,0x5d,0x5c,0x4e,0x6b,0x60,0x4b,0x61, /* 28-2f */
+  0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7, /* 30-37 */
+  0xf8,0xf9,0x7a,0x5e,0x4c,0x7e,0x6e,0x6f, /* 38-3f */
+  0x7c,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7, /* 40-47 */
+  0xc8,0xc9,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6, /* 48-4f */
+  0xd7,0xd8,0xd9,0xe2,0xe3,0xe4,0xe5,0xe6, /* 50-57 */
+  0xe7,0xe8,0xe9,0xba,0xe0,0xbb,0xb0,0x6d, /* 58-5f */
+  0x79,0x81,0x82,0x83,0x84,0x85,0x86,0x87, /* 60-67 */
+  0x88,0x89,0x91,0x92,0x93,0x94,0x95,0x96, /* 68-6f */
+  0x97,0x98,0x99,0xa2,0xa3,0xa4,0xa5,0xa6, /* 70-77 */
+  0xa7,0xa8,0xa9,0xc0,0x4f,0xd0,0xa1,0x07, /* 78-7f */
+  0x68,0xdc,0x51,0x42,0x43,0x44,0x47,0x48, /* 80-87 */
+  0x52,0x53,0x54,0x57,0x56,0x58,0x63,0x67, /* 88-8f */
+  0x71,0x9c,0x9e,0xcb,0xcc,0xcd,0xdb,0xdd, /* 90-97 */
+  0xdf,0xec,0xfc,0x70,0xb1,0x80,0xbf,0x40, /* 98-9f */
+  0x45,0x55,0xee,0xde,0x49,0x69,0x9a,0x9b, /* a8-a7 */
+  0xab,0xaf,0x5f,0xb8,0xb7,0xaa,0x8a,0x8b, /* a8-af */
+  0x40,0x40,0x40,0x40,0x40,0x65,0x62,0x64, /* b0-b7 */
+  0xb4,0x40,0x40,0x40,0x40,0x4a,0xb2,0x40, /* b8-bf */
+  0x40,0x40,0x40,0x40,0x40,0x40,0x46,0x66, /* c0-c7 */
+  0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x9f, /* c8-cf */
+  0x8c,0xac,0x72,0x73,0x74,0x89,0x75,0x76, /* d0-d7 */
+  0x77,0x40,0x40,0x40,0x40,0x6a,0x78,0x40, /* d8-df */
+  0xee,0x59,0xeb,0xed,0xcf,0xef,0xa0,0x8e, /* e0-e7 */
+  0xae,0xfe,0xfb,0xfd,0x8d,0xad,0xbc,0xbe, /* e8-ef */
+  0xca,0x8f,0x40,0xb9,0xb6,0xb5,0xe1,0x9d, /* f0-f7 */
+  0x90,0xbd,0xb3,0xda,0xea,0xfa,0x40,0x40  /* f8-ff */
+};
+
+void jk_ascii2ebcdic(char *src, char *dst) {
+    char c;
+
+    while ((c = *src++) != 0) {
+        *dst++ = ascii_to_ebcdic[(unsigned int)c];
+    }
+
+    *dst = 0;
+}
+
+void jk_ebcdic2ascii(char *src, char *dst) {
+    char c;
+
+    while ((c = *src++) != 0) {
+        *dst++ = ebcdic_to_ascii[(unsigned int)c];
+    }
+
+    *dst = 0;
+}
+
+#endif
+
diff --git a/connectors/jk/native/common/jk_util.h b/connectors/jk/native/common/jk_util.h
new file mode 100644
index 0000000..0c44299
--- /dev/null
+++ b/connectors/jk/native/common/jk_util.h
@@ -0,0 +1,241 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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>                            *
+ * Author:      Rainer Jung <rjung@apache.org>                             *
+ * Version:     $Revision$                                          *
+ ***************************************************************************/
+#ifndef _JK_UTIL_H
+#define _JK_UTIL_H
+
+#include "jk_global.h"
+#include "jk_logger.h"
+#include "jk_map.h"
+#include "jk_pool.h"
+#include "jk_service.h"
+
+#define JK_SLEEP_DEF     (100)
+
+const char *jk_get_bool(int v);
+
+int jk_get_bool_code(const char *v, int def);
+
+void jk_sleep(int ms);
+
+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, const char *funcname, 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, const char **prop);
+
+int jk_get_worker_int_prop(jk_map_t *m,
+                           const char *wname, const char *pname, int *prop);
+
+const char *jk_get_worker_host(jk_map_t *m, const char *wname, const char *def);
+
+const 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_cache_size_min(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_buffer(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);
+
+int jk_get_worker_recycle_timeout(jk_map_t *m, const char *wname, int def);
+
+int jk_get_worker_recover_timeout(jk_map_t *m, const char *wname, int def);
+
+const char *jk_get_worker_route(jk_map_t *m, const char *wname, const char *def);
+
+const char *jk_get_worker_domain(jk_map_t *m, const char *wname, const char *def);
+
+const char *jk_get_worker_redirect(jk_map_t *m, const char *wname, const char *def);
+
+const char *jk_get_worker_secret_key(jk_map_t *m, const char *wname);
+
+int jk_get_worker_retries(jk_map_t *m, const char *wname, int def);
+
+int jk_get_is_worker_disabled(jk_map_t *m, const char *wname);
+
+int jk_get_is_worker_stopped(jk_map_t *m, const char *wname);
+
+int jk_get_worker_activation(jk_map_t *m, const char *wname);
+
+int jk_get_worker_list(jk_map_t *m, char ***list, unsigned *num_of_workers);
+
+int jk_get_lb_factor(jk_map_t *m, const char *wname);
+
+int jk_get_distance(jk_map_t *m, const char *wname);
+
+int jk_get_is_sticky_session(jk_map_t *m, const char *wname);
+
+int jk_get_is_sticky_session_force(jk_map_t *m, const char *wname);
+
+int jk_get_lb_method(jk_map_t *m, const char *wname);
+
+int jk_get_lb_lock(jk_map_t *m, const char *wname);
+
+int jk_get_lb_worker_list(jk_map_t *m,
+                          const char *lb_wname,
+                          char ***list, unsigned int *num_of_workers);
+int jk_get_worker_mount_list(jk_map_t *m,
+                             const char *wname,
+                             char ***list, unsigned int *num_of_maps);
+const 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, const 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, const char **vm_path);
+
+int jk_get_worker_callback_dll(jk_map_t *m,
+                               const char *wname, const char **cb_path);
+
+int jk_get_worker_cmd_line(jk_map_t *m, const char *wname, const char **cmd_line);
+
+int jk_file_exists(const char *f);
+
+int jk_is_list_property(const char *prp_name);
+
+int jk_is_path_property(const char *prp_name);
+
+int jk_is_cmd_line_property(const char *prp_name);
+
+int jk_is_unique_property(const char *prp_name);
+
+int jk_is_deprecated_property(const char *prp_name);
+
+int jk_is_valid_property(const char *prp_name);
+
+int jk_get_worker_stdout(jk_map_t *m, const char *wname, const char **stdout_name);
+
+int jk_get_worker_stderr(jk_map_t *m, const char *wname, const char **stderr_name);
+
+int jk_get_worker_sysprops(jk_map_t *m, const char *wname, const char **sysprops);
+
+int jk_get_worker_libpath(jk_map_t *m, const char *wname, const 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_set_worker_def_cache_size(int sz);
+
+int jk_get_worker_def_cache_size(int protocol);
+
+int jk_get_worker_maintain_time(jk_map_t *m);
+
+int jk_get_max_packet_size(jk_map_t *m, const char *wname);
+
+const char *jk_get_worker_style_sheet(jk_map_t *m, const char *wname, const char *def);
+
+int jk_get_is_read_only(jk_map_t *m, const char *wname);
+
+int jk_get_worker_user_list(jk_map_t *m,
+                            const char *wname,
+                            char ***list, unsigned int *num);
+
+int jk_get_worker_good_rating(jk_map_t *m,
+                              const char *wname,
+                              char ***list, unsigned int *num);
+
+int jk_get_worker_bad_rating(jk_map_t *m,
+                             const char *wname,
+                             char ***list, unsigned int *num);
+
+const char *jk_get_worker_name_space(jk_map_t *m, const char *wname, const char *def);
+
+const char *jk_get_worker_xmlns(jk_map_t *m, const char *wname, const char *def);
+
+const char *jk_get_worker_xml_doctype(jk_map_t *m, const char *wname, const char *def);
+
+const char *jk_get_worker_prop_prefix(jk_map_t *m, const char *wname, const char *def);
+
+int jk_get_worker_fail_on_status(jk_map_t *m, const char *wname,
+                                 int *list, unsigned int list_size);
+
+
+int jk_get_worker_user_case_insensitive(jk_map_t *m, const char *wname);
+
+#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 AS400
+
+#define S_IFREG _S_IFREG
+
+#ifdef AS400_UTF8
+
+void jk_ascii2ebcdic(char *src, char *dst);
+void jk_ebcdic2ascii(char *src, char *dst);
+
+#endif /* AS400_UTF8 */
+
+#endif
+
+/* i5/OS V5R4 need ASCII-EBCDIC conversion before stat() call */
+/* added a stat() mapper function, jk_map, for such purpose */
+
+int jk_stat(const char *f, struct stat * statbuf);
+
+#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..6f4e848
--- /dev/null
+++ b/connectors/jk/native/common/jk_version.h
@@ -0,0 +1,72 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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       24
+#define JK_VERSTRING    "1.2.24"
+
+/* Beta number */
+#define JK_VERBETA      0
+#define JK_BETASTRING   "0"
+/* set JK_VERISRELEASE to 1 when release (do not forget to commit!) */
+#define JK_VERISRELEASE 0
+#define JK_VERRC        0
+#define JK_RCSTRING     "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
+
+#if (JK_VERRC != 0)
+#undef JK_EXPOSED_VERSION
+#define JK_EXPOSED_VERSION JK_RELEASE_STR "-rc-" JK_RCSTRING
+#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..3a1b42c
--- /dev/null
+++ b/connectors/jk/native/common/jk_worker.c
@@ -0,0 +1,333 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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"
+#include "jk_mt.h"
+
+static void close_workers(jk_logger_t *l);
+
+static worker_factory get_factory_for(const 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);
+
+/* Global worker list */
+static jk_map_t *worker_map;
+#if _MT_CODE
+static JK_CRIT_SEC worker_lock;
+#endif
+static int worker_maintain_time = 0;
+
+int wc_open(jk_map_t *init_data, jk_worker_env_t *we, jk_logger_t *l)
+{
+    int rc;
+    JK_TRACE_ENTER(l);
+
+    if (!jk_map_alloc(&worker_map)) {
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+    JK_INIT_CS(&worker_lock, rc);
+    if (rc == JK_FALSE) {
+        jk_log(l, JK_LOG_ERROR,
+                "creating thread lock (errno=%d)",
+                errno);
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    if (!jk_get_worker_list(init_data, &(we->worker_list),
+                            &we->num_of_workers)) {
+        JK_TRACE_EXIT(l);
+        we->num_of_workers = 0;
+        we->worker_list = NULL;
+        return JK_FALSE;
+    }
+
+    worker_maintain_time = jk_get_worker_maintain_time(init_data);
+    if(worker_maintain_time < 0)
+        worker_maintain_time = 0;
+
+    if (!build_worker_map(init_data, we->worker_list,
+                          we->num_of_workers, we, l)) {
+        close_workers(l);
+        we->num_of_workers = 0;
+        we->worker_list = NULL;
+        JK_TRACE_EXIT(l);
+        return JK_FALSE;
+    }
+
+    JK_TRACE_EXIT(l);
+    return JK_TRUE;
+}
+
+
+void wc_close(jk_logger_t *l)
+{
+    int rc;
+    JK_TRACE_ENTER(l);
+    JK_DELETE_CS(&worker_lock, rc);
+    close_workers(l);
+    JK_TRACE_EXIT(l);
+}
+
+jk_worker_t *wc_get_worker_for_name(const char *name, jk_logger_t *l)
+{
+    jk_worker_t *rc;
+
+    JK_TRACE_ENTER(l);
+    if (!name) {
+        JK_LOG_NULL_PARAMS(l);
+        JK_TRACE_EXIT(l);
+        return NULL;
+    }
+
+    rc = jk_map_get(worker_map, name, NULL);
+
+    if (JK_IS_DEBUG_LEVEL(l))
+        jk_log(l, JK_LOG_DEBUG, "%s a worker %s",
+               rc ? "found" : "did not find", name);
+    JK_TRACE_EXIT(l);
+    return rc;
+}
+
+int wc_create_worker(const char *name, int use_map,
+                     jk_map_t *init_data,
+                     jk_worker_t **rc, jk_worker_env_t *we, jk_logger_t *l)
+{
+    JK_TRACE_ENTER(l);
+
+    if (rc) {
+        const char *type = jk_get_worker_type(init_data, name);
+        worker_factory fac = get_factory_for(type);
+        jk_worker_t *w = NULL;
+        unsigned int i, num_of_maps;
+        char **map_names;
+        int wtype;
+
+        *rc = NULL;
+
+        if (!fac) {
+            jk_log(l, JK_LOG_ERROR, "Unknown worker type %s for worker %s",
+                   type, name);
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "about to create instance %s of %s", name,
+                   type);
+
+        if (((wtype = fac(&w, name, l)) == 0) || !w) {
+            jk_log(l, JK_LOG_ERROR,
+                   "factory for %s failed for %s", type,
+                   name);
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "about to validate and init %s", name);
+        if (!w->validate(w, init_data, we, l)) {
+            w->destroy(&w, l);
+            jk_log(l, JK_LOG_ERROR,
+                   "validate failed for %s", name);
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+
+        if (!w->init(w, init_data, we, l)) {
+            w->destroy(&w, l);
+            jk_log(l, JK_LOG_ERROR, "init failed for %s",
+                   name);
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+        if (use_map &&
+            jk_get_worker_mount_list(init_data, name,
+                                     &map_names,
+                                     &num_of_maps) && num_of_maps) {
+            for (i = 0; i < num_of_maps; i++) {
+                if (JK_IS_DEBUG_LEVEL(l))
+                    jk_log(l, JK_LOG_DEBUG,
+                            "mounting %s to worker %s",
+                            map_names[i], name);
+                if (uri_worker_map_add(we->uri_to_worker, map_names[i],
+                                       name, SOURCE_TYPE_WORKERDEF, l) == JK_FALSE) {
+                    w->destroy(&w, l);
+                    jk_log(l, JK_LOG_ERROR,
+                           "validate failed for %s", name);
+                    JK_TRACE_EXIT(l);
+                    return JK_FALSE;
+                }
+            }
+        }
+        w->type = wtype;
+        *rc = w;
+        JK_TRACE_EXIT(l);
+        return JK_TRUE;
+    }
+
+    JK_LOG_NULL_PARAMS(l);
+    return JK_FALSE;
+}
+
+static void close_workers(jk_logger_t *l)
+{
+    int sz = jk_map_size(worker_map);
+
+    JK_TRACE_ENTER(l);
+
+    if (sz > 0) {
+        int i;
+        for (i = 0; i < sz; i++) {
+            jk_worker_t *w = jk_map_value_at(worker_map, i);
+            if (w) {
+                if (JK_IS_DEBUG_LEVEL(l))
+                    jk_log(l, JK_LOG_DEBUG,
+                           "close_workers will destroy worker %s",
+                           jk_map_name_at(worker_map, i));
+                w->destroy(&w, l);
+            }
+        }
+    }
+    jk_map_free(&worker_map);
+    JK_TRACE_EXIT(l);
+}
+
+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_TRACE_ENTER(l);
+
+    for (i = 0; i < num_of_workers; i++) {
+        jk_worker_t *w = NULL;
+
+        if (JK_IS_DEBUG_LEVEL(l))
+            jk_log(l, JK_LOG_DEBUG,
+                   "creating worker %s", worker_list[i]);
+
+        if (wc_create_worker(worker_list[i], 1, init_data, &w, we, l)) {
+            jk_worker_t *oldw = NULL;
+            if (!jk_map_put(worker_map, worker_list[i], w, (void *)&oldw)) {
+                w->destroy(&w, l);
+                JK_TRACE_EXIT(l);
+                return JK_FALSE;
+            }
+
+            if (oldw) {
+                if (JK_IS_DEBUG_LEVEL(l))
+                    jk_log(l, JK_LOG_DEBUG,
+                           "removing old %s worker",
+                           worker_list[i]);
+                oldw->destroy(&oldw, l);
+            }
+        }
+        else {
+            jk_log(l, JK_LOG_ERROR,
+                   "failed to create worker %s",
+                   worker_list[i]);
+            JK_TRACE_EXIT(l);
+            return JK_FALSE;
+        }
+    }
+
+    JK_TRACE_EXIT(l);
+    return JK_TRUE;
+}
+
+static worker_factory get_factory_for(const 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;
+}
+
+const char *wc_get_name_for_type(int type, jk_logger_t *l)
+{
+    worker_factory_record_t *factory = &worker_factories[0];
+    while (factory->name) {
+        if (type == factory->type) {
+            jk_log(l, JK_LOG_DEBUG,
+                   "Found worker type '%s'",
+                   factory->name);
+            return factory->name;
+        }
+
+        factory++;
+    }
+
+    return NULL;
+}
+
+void wc_maintain(jk_logger_t *l)
+{
+    static time_t last_maintain = 0;
+    int sz = jk_map_size(worker_map);
+
+    JK_TRACE_ENTER(l);
+
+    if (sz > 0 && worker_maintain_time > 0) {
+        int i;
+        time_t now;
+        JK_ENTER_CS(&worker_lock, i);
+        now = time(NULL);
+        if (difftime(now, last_maintain) >= worker_maintain_time) {
+            last_maintain = now;
+            JK_LEAVE_CS(&worker_lock, i);
+            for (i = 0; i < sz; i++) {
+                jk_worker_t *w = jk_map_value_at(worker_map, i);
+                if (w && w->maintain) {
+                    if (JK_IS_DEBUG_LEVEL(l))
+                        jk_log(l, JK_LOG_DEBUG,
+                               "Maintaining worker %s",
+                               jk_map_name_at(worker_map, i));
+                    w->maintain(w, now, l);
+                }
+            }
+        }
+        else {
+            JK_LEAVE_CS(&worker_lock, i);
+        }
+    }
+    JK_TRACE_EXIT(l);
+}
diff --git a/connectors/jk/native/common/jk_worker.h b/connectors/jk/native/common/jk_worker.h
new file mode 100644
index 0000000..61e7fa9
--- /dev/null
+++ b/connectors/jk/native/common/jk_worker.h
@@ -0,0 +1,55 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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);
+
+const char *wc_get_name_for_type(int type, jk_logger_t *l);
+
+int wc_create_worker(const char *name, int use_map,
+                     jk_map_t *init_data,
+                     jk_worker_t **rc,
+                     jk_worker_env_t *we, jk_logger_t *l);
+
+void wc_maintain(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..14355e1
--- /dev/null
+++ b/connectors/jk/native/common/jk_worker_list.h
@@ -0,0 +1,98 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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"
+#include "jk_status.h"
+
+struct worker_factory_record
+{
+    const char *name;
+    int        type;
+    worker_factory fac;
+};
+typedef struct worker_factory_record worker_factory_record_t;
+
+static worker_factory_record_t worker_factories[] = {
+    /*
+     * AJPv12 worker, this is the stable worker.
+     */
+    {JK_AJP12_WORKER_NAME, JK_AJP12_WORKER_TYPE, ajp12_worker_factory},
+    /*
+     * AJPv13 worker, fast bi-directional worker.
+     */
+    {JK_AJP13_WORKER_NAME, JK_AJP13_WORKER_TYPE, ajp13_worker_factory},
+    /*
+     * AJPv14 worker, next generation fast bi-directional worker.
+     */
+    {JK_AJP14_WORKER_NAME, JK_AJP14_WORKER_TYPE, 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, JK_JNI_WORKER_TYPE, jni_worker_factory},
+#endif
+    /*
+     * Load balancing worker. Performs round robin with sticky 
+     * session load balancing.
+     */
+    {JK_LB_WORKER_NAME, JK_LB_WORKER_TYPE, lb_worker_factory},
+
+    /*
+     * Status worker. Performs display display and
+     * worker management.
+     */
+    {JK_STATUS_WORKER_NAME, JK_STATUS_WORKER_TYPE, status_worker_factory},
+
+    /*
+     * Marks the end of the worker factory list.
+     */
+    {NULL, 0, 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..3fa5334
--- /dev/null
+++ b/connectors/jk/native/common/list.mk.in
@@ -0,0 +1,12 @@
+## 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}jk_shm${OEXT} @JK_JNI_WORKER@ \
+                ${JK}jk_ajp_common${OEXT} ${JK}jk_context${OEXT} \
+                ${JK}jk_url${OEXT} \
+                ${JK}jk_status${OEXT}
diff --git a/connectors/jk/native/common/portable.h b/connectors/jk/native/common/portable.h
new file mode 100644
index 0000000..1743164
--- /dev/null
+++ b/connectors/jk/native/common/portable.h
@@ -0,0 +1,2 @@
+/* On most platform this file is overwritten when doing configure */
+/* DON'T COMMIT THE FILE IT BREAKS windoze and Netware, commit the sample file */
diff --git a/connectors/jk/native/common/portable.h.sample b/connectors/jk/native/common/portable.h.sample
new file mode 100644
index 0000000..e6c8840
--- /dev/null
+++ b/connectors/jk/native/common/portable.h.sample
@@ -0,0 +1,96 @@
+/* On most platform this file is overwritten when doing configure */
+/* common/portable.h.  Generated by configure.  */
+/* common/portable.h.in.  Generated from configure.in by autoheader.  */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Have flock() */
+#define HAVE_FLOCK 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Have snprintf() */
+#define HAVE_SNPRINTF 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/filio.h> header file. */
+#define HAVE_SYS_FILIO_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Have vsnprintf() */
+#define HAVE_VSNPRINTF 1
+
+/* Name of package */
+#define PACKAGE "mod_jk"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME ""
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING ""
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION ""
+
+/* The size of `char', as computed by sizeof. */
+#define SIZEOF_CHAR 1
+
+/* The size of `int', as computed by sizeof. */
+#define SIZEOF_INT 4
+
+/* The size of `long', as computed by sizeof. */
+#define SIZEOF_LONG 4
+
+/* The size of `longlong', as computed by sizeof. */
+#define SIZEOF_LONGLONG 0
+
+/* The size of `long double', as computed by sizeof. */
+#define SIZEOF_LONG_DOUBLE 16
+
+/* The size of `long long', as computed by sizeof. */
+#define SIZEOF_LONG_LONG 8
+
+/* The size of `short', as computed by sizeof. */
+#define SIZEOF_SHORT 2
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define to use SO_RCVTIMEO with setsockopt() */
+#define USE_SO_RCVTIMEO 1
+
+/* Define to use SO_SNDTIMEO with setsockopt() */
+#define USE_SO_SNDTIMEO 1
+
+/* Version number of package */
+#define VERSION "1.2.24"
diff --git a/connectors/jk/native/configure.in b/connectors/jk/native/configure.in
new file mode 100755
index 0000000..3900cf9
--- /dev/null
+++ b/connectors/jk/native/configure.in
@@ -0,0 +1,686 @@
+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.24
+
+AM_INIT_AUTOMAKE(${PACKAGE}, ${VERSION})
+
+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)
+
+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"
+		APXSCC="`$APXS -q CC`"
+		APXSCFLAGS="`$APXS -q CFLAGS` -DJK_PREFORK"
+		APXS_CPPFLAGS=""
+            else
+                WEBSERVER="apache-2.0"
+              	APRINCLUDEDIR=""
+              	INCTEMP="`$APXS -q APR_INCLUDEDIR` `$APXS -q APU_INCLUDEDIR`"
+              	for INC in ${INCTEMP}; do
+              	    APRINCLUDEDIR="${APRINCLUDEDIR} -I${INC}"
+              	done
+                AC_MSG_RESULT([APRINCLUDEDIR is $APRINCLUDEDIR])
+		APXSCC="`$APXS -q CC`"
+              	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\"])
+            if ${TEST} -z "${CC}" ; then
+                CC="${APXSCC}"
+            else
+                if ${TEST} "${CC}" != "$APXSCC" ; then
+                    WARN_CC=1
+                fi
+            fi
+
+            AC_SUBST(APXS)
+        fi
+    fi
+],
+[
+	AC_MSG_RESULT(no apxs given)
+])
+
+AC_SUBST(APACHE_CONFIG_VARS)
+
+AC_PROG_CC
+AC_PROG_LD
+
+SAVE_LIBTOOL="$LIBTOOL"
+
+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
+
+if ${TEST} -n "${SAVE_LIBTOOL}" ; then
+    LIBTOOL="$SAVE_LIBTOOL"
+fi
+AC_MSG_RESULT([LIBTOOL="$LIBTOOL"])
+
+AC_SUBST(LIBTOOL)
+
+dnl ----------------------------- Checks for standard typedefs
+
+dnl Checks for integer size
+AC_CHECK_SIZEOF(char, 1)
+AC_CHECK_SIZEOF(int, 4)
+AC_CHECK_SIZEOF(long, 4)
+AC_CHECK_SIZEOF(short, 2)
+AC_CHECK_SIZEOF(long double, 12)
+AC_CHECK_SIZEOF(long long, 8)
+AC_CHECK_SIZEOF(longlong, 8)
+
+# Now we need to find what jk_uint32_t (sizeof == 4) will be.
+# The first match is our preference.
+if test "$ac_cv_sizeof_int" = "4"; then
+    int32_t_fmt='#define JK_INT32_T_FMT "d"'
+    uint32_t_fmt='#define JK_UINT32_T_FMT "u"'
+    uint32_t_hex_fmt='#define JK_UINT32_T_HEX_FMT "x"'
+    int32_value="int"
+elif test "$ac_cv_sizeof_long" = "4"; then
+    int32_t_fmt='#define JK_INT32_T_FMT "ld"'
+    uint32_t_fmt='#define JK_UINT32_T_FMT "lu"'
+    uint32_t_hex_fmt='#define JK_UINT32_T_HEX_FMT "lx"'
+    int32_value="long"
+else
+    int32_t_fmt='#error could not detect a 32-bit integer type'
+    uint32_t_fmt='#error could not detect a 32-bit integer type'
+    uint32_t_hex_fmt='#error could not detect a 32-bit integer type'
+    AC_ERROR([could not detect a 32-bit integer type])
+fi
+
+# Now we need to find what jk_uint64_t (sizeof == 8) will be.
+# The first match is our preference.
+if test "$ac_cv_sizeof_int" = "8"; then
+    int64_t_fmt='#define JK_INT64_T_FMT "d"'
+    uint64_t_fmt='#define JK_UINT64_T_FMT "u"'
+    uint64_t_hex_fmt='#define JK_UINT64_T_HEX_FMT "x"'
+    int64_value="int"
+elif test "$ac_cv_sizeof_long" = "8"; then
+    int64_t_fmt='#define JK_INT64_T_FMT "ld"'
+    uint64_t_fmt='#define JK_UINT64_T_FMT "lu"'
+    uint64_t_hex_fmt='#define JK_UINT64_T_HEX_FMT "lx"'
+    int64_value="long"
+elif test "$ac_cv_sizeof_long_long" = "8"; then
+    # Linux, Solaris, FreeBSD all support ll with printf.
+    # BSD 4.4 originated 'q'.  Solaris is more popular and 
+    # doesn't support 'q'.  Solaris wins.  Exceptions can
+    # go to the OS-dependent section.
+    int64_t_fmt='#define JK_INT64_T_FMT "lld"'
+    uint64_t_fmt='#define JK_UINT64_T_FMT "llu"'
+    uint64_t_hex_fmt='#define JK_UINT64_T_HEX_FMT "llx"'
+    int64_value="long long"
+elif test "$ac_cv_sizeof_long_double" = "8"; then
+    int64_t_fmt='#define JK_INT64_T_FMT "Ld"'
+    uint64_t_fmt='#define JK_UINT64_T_FMT "Lu"'
+    uint64_t_hex_fmt='#define JK_UINT64_T_HEX_FMT "Lx"'
+    int64_value="long double"
+elif test "$ac_cv_sizeof_longlong" = "8"; then
+    int64_t_fmt='#define JK_INT64_T_FMT "qd"'
+    uint64_t_fmt='#define JK_UINT64_T_FMT "qu"'
+    uint64_t_hex_fmt='#define JK_UINT64_T_HEX_FMT "qx"'
+    int64_value="__int64"
+else
+    int64_t_fmt='#error could not detect a 64-bit integer type'
+    uint64_t_fmt='#error could not detect a 64-bit integer type'
+    uint64_t_hex_fmt='#error could not detect a 64-bit integer type'
+    AC_ERROR([could not detect a 64-bit integer type])
+fi
+
+AC_SUBST(int32_value)
+AC_SUBST(int32_t_fmt) 
+AC_SUBST(uint32_t_fmt) 
+AC_SUBST(uint32_t_hex_fmt) 
+AC_SUBST(int64_value)
+AC_SUBST(int64_t_fmt) 
+AC_SUBST(uint64_t_fmt) 
+AC_SUBST(uint64_t_hex_fmt) 
+
+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()]))
+dnl check for flock function.
+AC_CHECK_FUNC(flock, AC_DEFINE(HAVE_FLOCK,1,[Have flock()]))
+
+dnl check for -lsocket library
+AC_CHECK_LIB(socket, setsockopt, [LIBS="$LIBS -lsocket"])
+
+dnl check for filio.h used on Solaris to define FIONREAD ioctl.
+AC_CHECK_HEADERS(sys/filio.h)
+
+AC_DEFUN([JK_CHECK_SETSOCKOPT], [
+AC_MSG_CHECKING(whether to use $1 with setsockopt())
+AC_TRY_RUN([
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+int main(void)
+{
+    int s;
+    struct timeval tv;
+    tv.tv_sec  = 3;
+    tv.tv_usec = 0;
+
+#ifndef $1
+    exit(3);
+#else
+    if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1)
+        exit(2);
+
+    /* fails on Solaris 2.6,8,9,10 and some Linuxes because
+       SO_RCVTIMEO|SO_SNDTIMEO are defined but not implemented */
+    if (setsockopt(s, SOL_SOCKET, $1, (const void *)&tv, sizeof(tv)) == -1)
+        exit(1);
+
+    exit(0);
+#endif
+}
+]
+, [ AC_MSG_RESULT([yes]) AC_DEFINE(USE_$1, 1, [Define to use $1 with setsockopt()]) ]
+, [ AC_MSG_RESULT([no]) ]
+)
+])dnl
+
+dnl check for SO_RCVTIMEO and SO_SNDTIMEO
+JK_CHECK_SETSOCKOPT(SO_RCVTIMEO)
+JK_CHECK_SETSOCKOPT(SO_SNDTIMEO)
+
+dnl Apache-2.0 needs the os subdirectory to include os.h
+dnl this include is copy from os/config.m4
+sinclude(../support/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} together, 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
+               CFLAGS="${CFLAGS} -DJK_PREFORK"
+               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)
+
+AC_ARG_ENABLE(netscape,
+[  --enable-netscape=DIR  Build Netscape/iPlanet/SunONE nsapi redirector plugin.],
+[
+    if ${TEST} ! -z "$WEBSERVER" ; then
+        AC_MSG_ERROR([Sorry cannot use --with-apxs=${APXS} or --with-apache=${withval} with --with-netscape, please choose one or the other.])
+    fi
+
+    WEBSERVER="netscape"
+
+    AC_MSG_RESULT([building connector for \"$WEBSERVER\"])
+],
+[
+	AC_MSG_RESULT(no netscape given)
+])
+
+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 CFLAGS for maintainer mode
+dnl it also allows the CFLAGS environment variable.
+CFLAGS="${CFLAGS}"
+AC_ARG_ENABLE(
+maintainer-mode,
+[  --enable-maintainer-mode   Turn on debugging and compile time warnings],
+[
+case "${enableval}" in
+    y | Y | YES | yes | TRUE | true )
+        CFLAGS="${CFLAGS} -DDEBUG -Wall"
+        AC_MSG_RESULT([...Enabling Maintainer mode...])
+        ;;
+esac
+])
+AC_SUBST(CFLAGS)
+
+dnl CFLAGS for preforks mode
+dnl it also allows the CFLAGS environment variable.
+CFLAGS="${CFLAGS}"
+AC_ARG_ENABLE(
+prefork,
+[  --enable-prefork   Turn on prefork web server mode],
+[
+case "${enableval}" in
+    y | Y | YES | yes | TRUE | true )
+        CFLAGS="${CFLAGS} -DJK_PREFORK"
+        AC_MSG_RESULT([...Enabling Prefork mode...])
+        ;;
+esac
+])
+AC_SUBST(CFLAGS)
+
+dnl CFLAGS for shared memory lock mode
+dnl it also allows the CFLAGS environment variable.
+CFLAGS="${CFLAGS}"
+AC_ARG_ENABLE(
+flock,
+[  --enable-flock   Turn on flock for shared locking if present],
+[
+case "${enableval}" in
+    y | Y | YES | yes | TRUE | true )
+        CFLAGS="${CFLAGS} -DJK_USE_FLOCK"
+        AC_MSG_RESULT([...Enabling flock() shared memory locking...])
+        ;;
+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
+	common/jk_types.h
+	jni/Makefile
+	])
+
+if ${TEST} -n "${WARN_CC}" ; then
+    AC_MSG_WARN([Using CC from environment:])
+    AC_MSG_WARN([    CC="$CC"])
+    AC_MSG_WARN([instead of CC from apxs:])
+    AC_MSG_WARN([    CC="$APXSCC"])
+    AC_MSG_WARN([If "make" throws an error of the form])
+    AC_MSG_WARN([    "libtool: compile: unable to infer tagged configuration"])
+    AC_MSG_WARN([    "libtool: compile: specify a tag with `--tag'"])
+    AC_MSG_WARN([try running configure without setting CC,])
+    AC_MSG_WARN([or at least CC should start with "$APXSCC"])
+fi
diff --git a/connectors/jk/native/docs/api/README.txt b/connectors/jk/native/docs/api/README.txt
new file mode 100644
index 0000000..d4a05c3
--- /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. (tomcat-connectors/jk/native)
diff --git a/connectors/jk/native/iis/Makefile.amd64 b/connectors/jk/native/iis/Makefile.amd64
new file mode 100644
index 0000000..91eb8e0
--- /dev/null
+++ b/connectors/jk/native/iis/Makefile.amd64
@@ -0,0 +1,284 @@
+# Microsoft Developer Studio Generated NMAKE File, Based on isapi.dsp
+# Use Platform SDK:
+# SetEnv.cmd /X64 /RETAIL
+# nmake -f Makefile.amd64
+#
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+OUTDIR=.\Release_amd64
+INTDIR=.\Release_amd64
+# Begin Custom Macros
+OutDir=.\Release_amd64
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0" 
+
+ALL : "$(OUTDIR)\isapi_redirect.dll"
+
+!ELSE 
+
+ALL : "pcre_amd64" "$(OUTDIR)\isapi_redirect.dll"
+
+!ENDIF 
+
+!IF "$(RECURSE)" == "1" 
+CLEAN :"pcre_amd64CLEAN" 
+!ELSE 
+CLEAN :
+!ENDIF 
+	-@erase "$(INTDIR)\isapi_redirect.res"
+	-@erase "$(INTDIR)\isapi_redirector_src.idb"
+	-@erase "$(INTDIR)\isapi_redirector_src.pdb"
+	-@erase "$(INTDIR)\jk_ajp12_worker.obj"
+	-@erase "$(INTDIR)\jk_ajp13.obj"
+	-@erase "$(INTDIR)\jk_ajp13_worker.obj"
+	-@erase "$(INTDIR)\jk_ajp14.obj"
+	-@erase "$(INTDIR)\jk_ajp14_worker.obj"
+	-@erase "$(INTDIR)\jk_ajp_common.obj"
+	-@erase "$(INTDIR)\jk_connect.obj"
+	-@erase "$(INTDIR)\jk_context.obj"
+	-@erase "$(INTDIR)\jk_isapi_plugin.obj"
+	-@erase "$(INTDIR)\jk_jni_worker.obj"
+	-@erase "$(INTDIR)\jk_lb_worker.obj"
+	-@erase "$(INTDIR)\jk_map.obj"
+	-@erase "$(INTDIR)\jk_md5.obj"
+	-@erase "$(INTDIR)\jk_msg_buff.obj"
+	-@erase "$(INTDIR)\jk_nwmain.obj"
+	-@erase "$(INTDIR)\jk_pool.obj"
+	-@erase "$(INTDIR)\jk_shm.obj"
+	-@erase "$(INTDIR)\jk_sockbuf.obj"
+	-@erase "$(INTDIR)\jk_status.obj"
+	-@erase "$(INTDIR)\jk_uri_worker_map.obj"
+	-@erase "$(INTDIR)\jk_util.obj"
+	-@erase "$(INTDIR)\jk_worker.obj"
+	-@erase "$(OUTDIR)\isapi_redirect.dll"
+	-@erase "$(OUTDIR)\isapi_redirect.exp"
+	-@erase "$(OUTDIR)\isapi_redirect.lib"
+	-@erase "$(OUTDIR)\isapi_redirect.pdb"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\isapi.bsc" 
+BSC32_SBRS= \
+	
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib user32.lib advapi32.lib ws2_32.lib mswsock.lib strsafe.lib bufferoverflowu.lib /nologo /dll /incremental:no /pdb:"$(OUTDIR)\isapi_redirect.pdb" /debug /machine:AMD64 /def:".\isapi.def" /out:"$(OUTDIR)\isapi_redirect.dll" /implib:"$(OUTDIR)\isapi_redirect.lib" 
+DEF_FILE= \
+	".\isapi.def"
+LINK32_OBJS= \
+	"$(INTDIR)\jk_ajp12_worker.obj" \
+	"$(INTDIR)\jk_ajp13.obj" \
+	"$(INTDIR)\jk_ajp13_worker.obj" \
+	"$(INTDIR)\jk_ajp14.obj" \
+	"$(INTDIR)\jk_ajp14_worker.obj" \
+	"$(INTDIR)\jk_ajp_common.obj" \
+	"$(INTDIR)\jk_connect.obj" \
+	"$(INTDIR)\jk_context.obj" \
+	"$(INTDIR)\jk_isapi_plugin.obj" \
+	"$(INTDIR)\jk_jni_worker.obj" \
+	"$(INTDIR)\jk_lb_worker.obj" \
+	"$(INTDIR)\jk_map.obj" \
+	"$(INTDIR)\jk_md5.obj" \
+	"$(INTDIR)\jk_msg_buff.obj" \
+	"$(INTDIR)\jk_nwmain.obj" \
+	"$(INTDIR)\jk_pool.obj" \
+	"$(INTDIR)\jk_shm.obj" \
+	"$(INTDIR)\jk_sockbuf.obj" \
+	"$(INTDIR)\jk_status.obj" \
+	"$(INTDIR)\jk_uri_worker_map.obj" \
+	"$(INTDIR)\jk_util.obj" \
+	"$(INTDIR)\jk_worker.obj" \
+	"$(INTDIR)\isapi_redirect.res" \
+	".\pcre\Release_amd64\pcre.lib"
+
+"$(OUTDIR)\isapi_redirect.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+	IF EXIST $(OUTDIR)\isapi_redirect.manifest \
+		mt -nologo -manifest $(OUTDIR)\isapi_redirect.manifest -outputresource:$(OUTDIR)\isapi_redirect.dll;2
+
+CPP_PROJ=/nologo /MD /W3 /Zi /O2 /I "..\common" /I "pcre" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_AMD64_=1" -DWIN64 /D "_WIN64" /Wp64 /FIPRE64PRA.H /D "ISAPI_EXPORTS" /D "HAS_PCRE" /D "PCRE_STATIC" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\isapi_redirector_src" /FD /c 
+
+.c{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cpp{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cxx{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.c{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cpp{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cxx{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 
+RSC_PROJ=/l 0x809 /fo"$(INTDIR)\isapi_redirect.res" /d "NDEBUG" 
+
+SOURCE=.\isapi_redirect.rc
+
+"$(INTDIR)\isapi_redirect.res" : $(SOURCE) "$(INTDIR)"
+	$(RSC) $(RSC_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_ajp12_worker.c
+
+"$(INTDIR)\jk_ajp12_worker.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_ajp13.c
+
+"$(INTDIR)\jk_ajp13.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_ajp13_worker.c
+
+"$(INTDIR)\jk_ajp13_worker.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_ajp14.c
+
+"$(INTDIR)\jk_ajp14.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_ajp14_worker.c
+
+"$(INTDIR)\jk_ajp14_worker.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_ajp_common.c
+
+"$(INTDIR)\jk_ajp_common.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_connect.c
+
+"$(INTDIR)\jk_connect.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_context.c
+
+"$(INTDIR)\jk_context.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\jk_isapi_plugin.c
+
+"$(INTDIR)\jk_isapi_plugin.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=..\common\jk_jni_worker.c
+
+"$(INTDIR)\jk_jni_worker.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_lb_worker.c
+
+"$(INTDIR)\jk_lb_worker.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_map.c
+
+"$(INTDIR)\jk_map.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_md5.c
+
+"$(INTDIR)\jk_md5.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_msg_buff.c
+
+"$(INTDIR)\jk_msg_buff.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_nwmain.c
+
+"$(INTDIR)\jk_nwmain.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_pool.c
+
+"$(INTDIR)\jk_pool.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_shm.c
+
+"$(INTDIR)\jk_shm.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_sockbuf.c
+
+"$(INTDIR)\jk_sockbuf.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_status.c
+
+"$(INTDIR)\jk_status.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_uri_worker_map.c
+
+"$(INTDIR)\jk_uri_worker_map.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_util.c
+
+"$(INTDIR)\jk_util.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_worker.c
+
+"$(INTDIR)\jk_worker.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+"pcre_amd64" : 
+   cd ".\pcre"
+   $(MAKE) /$(MAKEFLAGS) /F ".\pcre.amd64"
+   cd ".."
+
+"pcre_amd64CLEAN" : 
+   cd ".\pcre"
+   $(MAKE) /$(MAKEFLAGS) /F ".\pcre.amd64" RECURSE=1 CLEAN 
+   cd ".."
diff --git a/connectors/jk/native/iis/Makefile.ia64 b/connectors/jk/native/iis/Makefile.ia64
new file mode 100644
index 0000000..7a77494
--- /dev/null
+++ b/connectors/jk/native/iis/Makefile.ia64
@@ -0,0 +1,284 @@
+# Microsoft Developer Studio Generated NMAKE File, Based on isapi.dsp
+# Use Platform SDK:
+# SetEnv.cmd /SRV64 /RETAIL
+# nmake -f Makefile.ia64
+#
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+OUTDIR=.\Release_ia64
+INTDIR=.\Release_ia64
+# Begin Custom Macros
+OutDir=.\Release_ia64
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0" 
+
+ALL : "$(OUTDIR)\isapi_redirect.dll"
+
+!ELSE 
+
+ALL : "pcre_ia64" "$(OUTDIR)\isapi_redirect.dll"
+
+!ENDIF 
+
+!IF "$(RECURSE)" == "1" 
+CLEAN :"pcre_ia64CLEAN" 
+!ELSE 
+CLEAN :
+!ENDIF 
+	-@erase "$(INTDIR)\isapi_redirect.res"
+	-@erase "$(INTDIR)\isapi_redirector_src.idb"
+	-@erase "$(INTDIR)\isapi_redirector_src.pdb"
+	-@erase "$(INTDIR)\jk_ajp12_worker.obj"
+	-@erase "$(INTDIR)\jk_ajp13.obj"
+	-@erase "$(INTDIR)\jk_ajp13_worker.obj"
+	-@erase "$(INTDIR)\jk_ajp14.obj"
+	-@erase "$(INTDIR)\jk_ajp14_worker.obj"
+	-@erase "$(INTDIR)\jk_ajp_common.obj"
+	-@erase "$(INTDIR)\jk_connect.obj"
+	-@erase "$(INTDIR)\jk_context.obj"
+	-@erase "$(INTDIR)\jk_isapi_plugin.obj"
+	-@erase "$(INTDIR)\jk_jni_worker.obj"
+	-@erase "$(INTDIR)\jk_lb_worker.obj"
+	-@erase "$(INTDIR)\jk_map.obj"
+	-@erase "$(INTDIR)\jk_md5.obj"
+	-@erase "$(INTDIR)\jk_msg_buff.obj"
+	-@erase "$(INTDIR)\jk_nwmain.obj"
+	-@erase "$(INTDIR)\jk_pool.obj"
+	-@erase "$(INTDIR)\jk_shm.obj"
+	-@erase "$(INTDIR)\jk_sockbuf.obj"
+	-@erase "$(INTDIR)\jk_status.obj"
+	-@erase "$(INTDIR)\jk_uri_worker_map.obj"
+	-@erase "$(INTDIR)\jk_util.obj"
+	-@erase "$(INTDIR)\jk_worker.obj"
+	-@erase "$(OUTDIR)\isapi_redirect.dll"
+	-@erase "$(OUTDIR)\isapi_redirect.exp"
+	-@erase "$(OUTDIR)\isapi_redirect.lib"
+	-@erase "$(OUTDIR)\isapi_redirect.pdb"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\isapi.bsc" 
+BSC32_SBRS= \
+	
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib user32.lib advapi32.lib ws2_32.lib mswsock.lib strsafe.lib bufferoverflowu.lib /nologo /dll /incremental:no /pdb:"$(OUTDIR)\isapi_redirect.pdb" /debug /machine:IA64 /def:".\isapi.def" /out:"$(OUTDIR)\isapi_redirect.dll" /implib:"$(OUTDIR)\isapi_redirect.lib" 
+DEF_FILE= \
+	".\isapi.def"
+LINK32_OBJS= \
+	"$(INTDIR)\jk_ajp12_worker.obj" \
+	"$(INTDIR)\jk_ajp13.obj" \
+	"$(INTDIR)\jk_ajp13_worker.obj" \
+	"$(INTDIR)\jk_ajp14.obj" \
+	"$(INTDIR)\jk_ajp14_worker.obj" \
+	"$(INTDIR)\jk_ajp_common.obj" \
+	"$(INTDIR)\jk_connect.obj" \
+	"$(INTDIR)\jk_context.obj" \
+	"$(INTDIR)\jk_isapi_plugin.obj" \
+	"$(INTDIR)\jk_jni_worker.obj" \
+	"$(INTDIR)\jk_lb_worker.obj" \
+	"$(INTDIR)\jk_map.obj" \
+	"$(INTDIR)\jk_md5.obj" \
+	"$(INTDIR)\jk_msg_buff.obj" \
+	"$(INTDIR)\jk_nwmain.obj" \
+	"$(INTDIR)\jk_pool.obj" \
+	"$(INTDIR)\jk_shm.obj" \
+	"$(INTDIR)\jk_sockbuf.obj" \
+	"$(INTDIR)\jk_status.obj" \
+	"$(INTDIR)\jk_uri_worker_map.obj" \
+	"$(INTDIR)\jk_util.obj" \
+	"$(INTDIR)\jk_worker.obj" \
+	"$(INTDIR)\isapi_redirect.res" \
+	".\pcre\Release_ia64\pcre.lib"
+
+"$(OUTDIR)\isapi_redirect.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+	IF EXIST $(OUTDIR)\isapi_redirect.manifest \
+		mt -nologo -manifest $(OUTDIR)\isapi_redirect.manifest -outputresource:$(OUTDIR)\isapi_redirect.dll;2
+
+CPP_PROJ=/nologo /MD /W3 /Zi /O2 /I "..\common" /I "pcre" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_IA64_=1" -DWIN64 /D "_WIN64" /Wp64 /FIPRE64PRA.H /D "ISAPI_EXPORTS" /D "HAS_PCRE" /D "PCRE_STATIC" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\isapi_redirector_src" /FD /c 
+
+.c{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cpp{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cxx{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.c{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cpp{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cxx{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 
+RSC_PROJ=/l 0x809 /fo"$(INTDIR)\isapi_redirect.res" /d "NDEBUG" 
+
+SOURCE=.\isapi_redirect.rc
+
+"$(INTDIR)\isapi_redirect.res" : $(SOURCE) "$(INTDIR)"
+	$(RSC) $(RSC_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_ajp12_worker.c
+
+"$(INTDIR)\jk_ajp12_worker.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_ajp13.c
+
+"$(INTDIR)\jk_ajp13.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_ajp13_worker.c
+
+"$(INTDIR)\jk_ajp13_worker.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_ajp14.c
+
+"$(INTDIR)\jk_ajp14.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_ajp14_worker.c
+
+"$(INTDIR)\jk_ajp14_worker.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_ajp_common.c
+
+"$(INTDIR)\jk_ajp_common.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_connect.c
+
+"$(INTDIR)\jk_connect.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_context.c
+
+"$(INTDIR)\jk_context.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\jk_isapi_plugin.c
+
+"$(INTDIR)\jk_isapi_plugin.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=..\common\jk_jni_worker.c
+
+"$(INTDIR)\jk_jni_worker.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_lb_worker.c
+
+"$(INTDIR)\jk_lb_worker.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_map.c
+
+"$(INTDIR)\jk_map.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_md5.c
+
+"$(INTDIR)\jk_md5.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_msg_buff.c
+
+"$(INTDIR)\jk_msg_buff.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_nwmain.c
+
+"$(INTDIR)\jk_nwmain.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_pool.c
+
+"$(INTDIR)\jk_pool.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_shm.c
+
+"$(INTDIR)\jk_shm.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_sockbuf.c
+
+"$(INTDIR)\jk_sockbuf.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_status.c
+
+"$(INTDIR)\jk_status.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_uri_worker_map.c
+
+"$(INTDIR)\jk_uri_worker_map.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_util.c
+
+"$(INTDIR)\jk_util.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_worker.c
+
+"$(INTDIR)\jk_worker.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+"pcre_ia64" : 
+   cd ".\pcre"
+   $(MAKE) /$(MAKEFLAGS) /F ".\pcre.ia64"
+   cd ".."
+
+"pcre_ia64CLEAN" : 
+   cd ".\pcre"
+   $(MAKE) /$(MAKEFLAGS) /F ".\pcre.ia64" RECURSE=1 CLEAN 
+   cd ".."
diff --git a/connectors/jk/native/iis/Makefile.x86 b/connectors/jk/native/iis/Makefile.x86
new file mode 100644
index 0000000..9171c48
--- /dev/null
+++ b/connectors/jk/native/iis/Makefile.x86
@@ -0,0 +1,277 @@
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+OUTDIR=.\Release_x86
+INTDIR=.\Release_x86
+# Begin Custom Macros
+OutDir=.\Release_x86
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0" 
+
+ALL : "$(OUTDIR)\isapi_redirect.dll"
+
+!ELSE 
+
+ALL : "pcre_x86" "$(OUTDIR)\isapi_redirect.dll"
+
+!ENDIF 
+
+!IF "$(RECURSE)" == "1" 
+CLEAN :"pcre_x86CLEAN" 
+!ELSE 
+CLEAN :
+!ENDIF 
+	-@erase "$(INTDIR)\isapi_redirect.res"
+	-@erase "$(INTDIR)\isapi_redirector_src.idb"
+	-@erase "$(INTDIR)\isapi_redirector_src.pdb"
+	-@erase "$(INTDIR)\jk_ajp12_worker.obj"
+	-@erase "$(INTDIR)\jk_ajp13.obj"
+	-@erase "$(INTDIR)\jk_ajp13_worker.obj"
+	-@erase "$(INTDIR)\jk_ajp14.obj"
+	-@erase "$(INTDIR)\jk_ajp14_worker.obj"
+	-@erase "$(INTDIR)\jk_ajp_common.obj"
+	-@erase "$(INTDIR)\jk_connect.obj"
+	-@erase "$(INTDIR)\jk_context.obj"
+	-@erase "$(INTDIR)\jk_isapi_plugin.obj"
+	-@erase "$(INTDIR)\jk_jni_worker.obj"
+	-@erase "$(INTDIR)\jk_lb_worker.obj"
+	-@erase "$(INTDIR)\jk_map.obj"
+	-@erase "$(INTDIR)\jk_md5.obj"
+	-@erase "$(INTDIR)\jk_msg_buff.obj"
+	-@erase "$(INTDIR)\jk_nwmain.obj"
+	-@erase "$(INTDIR)\jk_pool.obj"
+	-@erase "$(INTDIR)\jk_shm.obj"
+	-@erase "$(INTDIR)\jk_sockbuf.obj"
+	-@erase "$(INTDIR)\jk_status.obj"
+	-@erase "$(INTDIR)\jk_uri_worker_map.obj"
+	-@erase "$(INTDIR)\jk_util.obj"
+	-@erase "$(INTDIR)\jk_worker.obj"
+	-@erase "$(OUTDIR)\isapi_redirect.dll"
+	-@erase "$(OUTDIR)\isapi_redirect.exp"
+	-@erase "$(OUTDIR)\isapi_redirect.lib"
+	-@erase "$(OUTDIR)\isapi_redirect.pdb"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\isapi.bsc" 
+BSC32_SBRS= \
+	
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib user32.lib advapi32.lib ws2_32.lib mswsock.lib strsafe.lib /nologo /base:"0x6A6B0000" /dll /incremental:no /pdb:"$(OUTDIR)\isapi_redirect.pdb" /debug /machine:I386 /def:".\isapi.def" /out:"$(OUTDIR)\isapi_redirect.dll" /implib:"$(OUTDIR)\isapi_redirect.lib" 
+DEF_FILE= \
+	".\isapi.def"
+LINK32_OBJS= \
+	"$(INTDIR)\jk_ajp12_worker.obj" \
+	"$(INTDIR)\jk_ajp13.obj" \
+	"$(INTDIR)\jk_ajp13_worker.obj" \
+	"$(INTDIR)\jk_ajp14.obj" \
+	"$(INTDIR)\jk_ajp14_worker.obj" \
+	"$(INTDIR)\jk_ajp_common.obj" \
+	"$(INTDIR)\jk_connect.obj" \
+	"$(INTDIR)\jk_context.obj" \
+	"$(INTDIR)\jk_isapi_plugin.obj" \
+	"$(INTDIR)\jk_jni_worker.obj" \
+	"$(INTDIR)\jk_lb_worker.obj" \
+	"$(INTDIR)\jk_map.obj" \
+	"$(INTDIR)\jk_md5.obj" \
+	"$(INTDIR)\jk_msg_buff.obj" \
+	"$(INTDIR)\jk_nwmain.obj" \
+	"$(INTDIR)\jk_pool.obj" \
+	"$(INTDIR)\jk_shm.obj" \
+	"$(INTDIR)\jk_sockbuf.obj" \
+	"$(INTDIR)\jk_status.obj" \
+	"$(INTDIR)\jk_uri_worker_map.obj" \
+	"$(INTDIR)\jk_util.obj" \
+	"$(INTDIR)\jk_worker.obj" \
+	"$(INTDIR)\isapi_redirect.res" \
+	".\pcre\Release_x86\pcre.lib"
+
+"$(OUTDIR)\isapi_redirect.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+CPP_PROJ=/nologo /MD /W3 /Zi /O2 /I "..\common" /I "pcre" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "ISAPI_EXPORTS" /D "HAS_PCRE" /D "PCRE_STATIC" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\isapi_redirector_src" /FD /c 
+
+.c{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cpp{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cxx{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.c{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cpp{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cxx{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 
+RSC_PROJ=/l 0x809 /fo"$(INTDIR)\isapi_redirect.res" /d "NDEBUG" 
+
+SOURCE=.\isapi_redirect.rc
+
+"$(INTDIR)\isapi_redirect.res" : $(SOURCE) "$(INTDIR)"
+	$(RSC) $(RSC_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_ajp12_worker.c
+
+"$(INTDIR)\jk_ajp12_worker.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_ajp13.c
+
+"$(INTDIR)\jk_ajp13.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_ajp13_worker.c
+
+"$(INTDIR)\jk_ajp13_worker.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_ajp14.c
+
+"$(INTDIR)\jk_ajp14.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_ajp14_worker.c
+
+"$(INTDIR)\jk_ajp14_worker.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_ajp_common.c
+
+"$(INTDIR)\jk_ajp_common.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_connect.c
+
+"$(INTDIR)\jk_connect.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_context.c
+
+"$(INTDIR)\jk_context.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=.\jk_isapi_plugin.c
+
+"$(INTDIR)\jk_isapi_plugin.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=..\common\jk_jni_worker.c
+
+"$(INTDIR)\jk_jni_worker.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_lb_worker.c
+
+"$(INTDIR)\jk_lb_worker.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_map.c
+
+"$(INTDIR)\jk_map.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_md5.c
+
+"$(INTDIR)\jk_md5.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_msg_buff.c
+
+"$(INTDIR)\jk_msg_buff.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_nwmain.c
+
+"$(INTDIR)\jk_nwmain.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_pool.c
+
+"$(INTDIR)\jk_pool.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_shm.c
+
+"$(INTDIR)\jk_shm.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_sockbuf.c
+
+"$(INTDIR)\jk_sockbuf.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_status.c
+
+"$(INTDIR)\jk_status.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_uri_worker_map.c
+
+"$(INTDIR)\jk_uri_worker_map.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_util.c
+
+"$(INTDIR)\jk_util.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+SOURCE=..\common\jk_worker.c
+
+"$(INTDIR)\jk_worker.obj" : $(SOURCE) "$(INTDIR)"
+	$(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+"pcre_x86" : 
+   cd ".\pcre"
+   $(MAKE) /$(MAKEFLAGS) /F ".\pcre.x86" CFG="pcre_x86" 
+   cd ".."
+
+"pcre_x86CLEAN" : 
+   cd ".\pcre"
+   $(MAKE) /$(MAKEFLAGS) /F ".\pcre.x86" CFG="pcre_x86" RECURSE=1 CLEAN 
+   cd ".."
diff --git a/connectors/jk/native/iis/README b/connectors/jk/native/iis/README
new file mode 100644
index 0000000..fcb6364
--- /dev/null
+++ b/connectors/jk/native/iis/README
@@ -0,0 +1,45 @@
+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:
+      nmake -f Makefile.x86
+
+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/installer/LICENSE.TXT b/connectors/jk/native/iis/installer/LICENSE.TXT
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/connectors/jk/native/iis/installer/LICENSE.TXT
@@ -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/connectors/jk/native/iis/installer/License.rtf b/connectors/jk/native/iis/installer/License.rtf
new file mode 100644
index 0000000..e174f06
--- /dev/null
+++ b/connectors/jk/native/iis/installer/License.rtf
Binary files differ
diff --git a/connectors/jk/native/iis/installer/bin/README b/connectors/jk/native/iis/installer/bin/README
new file mode 100644
index 0000000..0539cf2
--- /dev/null
+++ b/connectors/jk/native/iis/installer/bin/README
@@ -0,0 +1 @@
+Jakarta Isapi Redirector
\ No newline at end of file
diff --git a/connectors/jk/native/iis/installer/conf/rewrite.properties b/connectors/jk/native/iis/installer/conf/rewrite.properties
new file mode 100644
index 0000000..0c66612
--- /dev/null
+++ b/connectors/jk/native/iis/installer/conf/rewrite.properties
@@ -0,0 +1,13 @@
+# rewrite.properties - IIS
+#
+# Form of the file
+# requested=replacement
+#
+# Note: Requested must be present in the
+#       uriworkermap.properies file and mapped to
+#       the desired worker.
+#
+# Next will send /examples/servlets/ to the container
+# for the /servlets-examples request.
+#
+# /servlets-examples/=/examples/servlets/
diff --git a/connectors/jk/native/iis/installer/conf/uriworkermap.properties b/connectors/jk/native/iis/installer/conf/uriworkermap.properties
new file mode 100644
index 0000000..5214cd4
--- /dev/null
+++ b/connectors/jk/native/iis/installer/conf/uriworkermap.properties
@@ -0,0 +1,24 @@
+# uriworkermap.properties - IIS
+#
+# This file provides sample mappings for example wlb
+# worker defined in workermap.properties.minimal
+# The general syntax for this file is:
+# [URL]=[Worker name]
+
+/admin/*=wlb
+/manager/*=wlb
+/jsp-examples/*=wlb
+/servlets-examples/*=wlb
+/examples/*=wlb
+
+# Optionally filter out all .jpeg files inside that context
+# For no mapping the url has to start with exclamation (!)
+
+!/servlets-examples/*.jpeg=wlb
+
+#
+# Mount jkstatus to /jkmanager
+# For production servers you will need to
+# secure the access to the /jkmanager url
+#
+/jkmanager=jkstatus
diff --git a/connectors/jk/native/iis/installer/conf/workers.properties.minimal b/connectors/jk/native/iis/installer/conf/workers.properties.minimal
new file mode 100644
index 0000000..14dbb6e
--- /dev/null
+++ b/connectors/jk/native/iis/installer/conf/workers.properties.minimal
@@ -0,0 +1,30 @@
+# workers.properties.minimal -
+#
+# This file provides minimal jk configuration properties needed to
+# connect to Tomcat.
+#
+# The workers that jk should create and work with
+#
+
+worker.list=wlb,jkstatus
+
+#
+# Defining a worker named ajp13w and of type ajp13
+# Note that the name and the type do not have to match.
+#
+worker.ajp13w.type=ajp13
+worker.ajp13w.host=localhost
+worker.ajp13w.port=8009
+
+#
+# Defining a load balancer
+# 
+
+worker.wlb.type=lb
+worker.wlb.balance_workers=ajp13w
+
+#
+# Define status worker
+#
+
+worker.jkstatus.type=status
diff --git a/connectors/jk/native/iis/installer/iisfilter.vbs b/connectors/jk/native/iis/installer/iisfilter.vbs
new file mode 100644
index 0000000..228beff
--- /dev/null
+++ b/connectors/jk/native/iis/installer/iisfilter.vbs
@@ -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.
+'
+
+' =========================================================================
+' Description: Install script for Tomcat ISAPI redirector                              
+' Author:      Mladen Turk <mturk@apache.org>                           
+' Version:     $Revision$                                           
+' =========================================================================
+
+'
+' Get a handle to the filters for the server - we process all errors
+'
+On Error Resume Next
+
+filterName = "jakarta"
+filterLib = "bin\isapi_redirect.dll"
+  
+Function IISInstallFilter(filterDir, filterObject)
+
+    Dim filters
+    Set filters = GetObject(filterObject)
+    If err Then err.clear
+    info "Got Filters " + filters.FilterLoadOrder
+    
+    '
+    ' 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
+    	    info "Error Deleting Filter"
+    	    IISInstallFilter = 0
+    	    Exit Function
+    	End If
+    	Set filter = filters.Create( "IISFilter", filterName )
+    	If Err Then
+    	    info "Error Creating Filter"
+    	    IISInstallFilter = 0
+    	    Exit Function
+    	End If
+    End If
+    
+    '
+    ' Set the filter info and save it
+    '
+    filter.FilterPath = filterDir + filterLib
+'    filter.FilterEnabled = true
+    filter.FilterDescription = "Jakarta Isapi Redirector"
+    filter.NotifyOrderHigh = true
+    filter.SetInfo
+    info "Created Filter " + filterDir + filterLib
+    
+    '
+    ' 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 "Added Filter " + filterName 
+    Else
+    	info "Filter already exists in load order - no update required."
+    End If
+    IISInstallFilter = 1
+End Function
+
+' 
+' Helper function for snafus
+'
+Function fail(message)
+'	MsgBox " " + message
+	WScript.Quit(1)
+End function
+
+'
+' Helper function for info
+'
+Function info(message)
+'	MsgBox " " + message
+End Function 
+
+info "Installing IIS Filter " + Session.Property("INSTALLDIR")
+Dim rv
+rv = 0
+rv = IISInstallFilter(Session.Property("INSTALLDIR"), "IIS://LocalHost/W3SVC/1/Filters")
+If rv = 0 Then    
+    rv = IISInstallFilter(Session.Property("INSTALLDIR"), "/LM/W3SVC/Filters")
+End If
+If rv = 0 Then
+    rv = IISInstallFilter(Session.Property("INSTALLDIR"), "/LM/W3SVC/1/Filters")
+End If
diff --git a/connectors/jk/native/iis/installer/isapi-redirector-win32-msi.ism b/connectors/jk/native/iis/installer/isapi-redirector-win32-msi.ism
new file mode 100644
index 0000000..992b2ef
--- /dev/null
+++ b/connectors/jk/native/iis/installer/isapi-redirector-win32-msi.ism
@@ -0,0 +1,4757 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<?xml-stylesheet type="text/xsl" href="is.xsl" ?>
+<!DOCTYPE msi [
+   <!ELEMENT msi   (summary,table*)>
+   <!ATTLIST msi version    CDATA #REQUIRED>
+   <!ATTLIST msi xmlns:dt   CDATA #IMPLIED
+                 codepage   CDATA #IMPLIED
+                 compression (MSZIP|LZX|none) "LZX">
+   
+   <!ELEMENT summary       (codepage?,title?,subject?,author?,keywords?,comments?,
+                            template,lastauthor?,revnumber,lastprinted?,
+                            createdtm?,lastsavedtm?,pagecount,wordcount,
+                            charcount?,appname?,security?)>
+                            
+   <!ELEMENT codepage      (#PCDATA)>
+   <!ELEMENT title         (#PCDATA)>
+   <!ELEMENT subject       (#PCDATA)>
+   <!ELEMENT author        (#PCDATA)>
+   <!ELEMENT keywords      (#PCDATA)>
+   <!ELEMENT comments      (#PCDATA)>
+   <!ELEMENT template      (#PCDATA)>
+   <!ELEMENT lastauthor    (#PCDATA)>
+   <!ELEMENT revnumber     (#PCDATA)>
+   <!ELEMENT lastprinted   (#PCDATA)>
+   <!ELEMENT createdtm     (#PCDATA)>
+   <!ELEMENT lastsavedtm   (#PCDATA)>
+   <!ELEMENT pagecount     (#PCDATA)>
+   <!ELEMENT wordcount     (#PCDATA)>
+   <!ELEMENT charcount     (#PCDATA)>
+   <!ELEMENT appname       (#PCDATA)>
+   <!ELEMENT security      (#PCDATA)>                            
+                                
+   <!ELEMENT table         (col+,row*)>
+   <!ATTLIST table
+                name        CDATA #REQUIRED>
+
+   <!ELEMENT col           (#PCDATA)>
+   <!ATTLIST col
+                 key       (yes|no) #IMPLIED
+                 def       CDATA #IMPLIED>
+                 
+   <!ELEMENT row            (td+)>
+   
+   <!ELEMENT td             (#PCDATA)>
+   <!ATTLIST td
+                 href       CDATA #IMPLIED
+                 dt:dt     (string|bin.base64) #IMPLIED
+                 md5        CDATA #IMPLIED>
+]>
+<msi version="2.0" xmlns:dt="urn:schemas-microsoft-com:datatypes">
+	
+	<summary>
+		<codepage>1252</codepage>
+		<title>Installation Database</title>
+		<subject>##ID_STRING5##</subject>
+		<author>##COMPANY_NAME##</author>
+		<keywords>Installer,MSI,Database</keywords>
+		<comments>##ID_STRING6##</comments>
+		<template>Intel;1033</template>
+		<lastauthor>Administrator</lastauthor>
+		<revnumber>{C3AC9A0C-8E2C-4998-8FD3-894123CF9D37}</revnumber>
+		<lastprinted/>
+		<createdtm>06/21/1999 09:00</createdtm>
+		<lastsavedtm>07/14/2000 12:50</lastsavedtm>
+		<pagecount>200</pagecount>
+		<wordcount>0</wordcount>
+		<charcount/>
+		<appname>FLEXnet InstallShield</appname>
+		<security>1</security>
+	</summary>
+	
+	<table name="ActionText">
+		<col key="yes" def="s72">Action</col>
+		<col def="L64">Description</col>
+		<col def="L128">Template</col>
+		<row><td>Advertise</td><td>##IDS_ACTIONTEXT_Advertising##</td><td/></row>
+		<row><td>AllocateRegistrySpace</td><td>##IDS_ACTIONTEXT_AllocatingRegistry##</td><td>##IDS_ACTIONTEXT_FreeSpace##</td></row>
+		<row><td>AppSearch</td><td>##IDS_ACTIONTEXT_SearchInstalled##</td><td>##IDS_ACTIONTEXT_PropertySignature##</td></row>
+		<row><td>BindImage</td><td>##IDS_ACTIONTEXT_BindingExes##</td><td>##IDS_ACTIONTEXT_File##</td></row>
+		<row><td>CCPSearch</td><td>##IDS_ACTIONTEXT_UnregisterModules##</td><td/></row>
+		<row><td>CostFinalize</td><td>##IDS_ACTIONTEXT_ComputingSpace3##</td><td/></row>
+		<row><td>CostInitialize</td><td>##IDS_ACTIONTEXT_ComputingSpace##</td><td/></row>
+		<row><td>CreateFolders</td><td>##IDS_ACTIONTEXT_CreatingFolders##</td><td>##IDS_ACTIONTEXT_Folder##</td></row>
+		<row><td>CreateShortcuts</td><td>##IDS_ACTIONTEXT_CreatingShortcuts##</td><td>##IDS_ACTIONTEXT_Shortcut##</td></row>
+		<row><td>DeleteServices</td><td>##IDS_ACTIONTEXT_DeletingServices##</td><td>##IDS_ACTIONTEXT_Service##</td></row>
+		<row><td>DuplicateFiles</td><td>##IDS_ACTIONTEXT_CreatingDuplicate##</td><td>##IDS_ACTIONTEXT_FileDirectorySize##</td></row>
+		<row><td>FileCost</td><td>##IDS_ACTIONTEXT_ComputingSpace2##</td><td/></row>
+		<row><td>FindRelatedProducts</td><td>##IDS_ACTIONTEXT_SearchForRelated##</td><td>##IDS_ACTIONTEXT_FoundApp##</td></row>
+		<row><td>GenerateScript</td><td>##IDS_ACTIONTEXT_GeneratingScript##</td><td>##IDS_ACTIONTEXT_1##</td></row>
+		<row><td>InstallAdminPackage</td><td>##IDS_ACTIONTEXT_CopyingNetworkFiles##</td><td>##IDS_ACTIONTEXT_FileDirSize##</td></row>
+		<row><td>InstallFiles</td><td>##IDS_ACTIONTEXT_CopyingNewFiles##</td><td>##IDS_ACTIONTEXT_FileDirSize2##</td></row>
+		<row><td>InstallODBC</td><td>##IDS_ACTIONTEXT_InstallODBC##</td><td/></row>
+		<row><td>InstallSFPCatalogFile</td><td>##IDS_ACTIONTEXT_InstallingSystemCatalog##</td><td>##IDS_ACTIONTEXT_FileDependencies##</td></row>
+		<row><td>InstallServices</td><td>##IDS_ACTIONTEXT_InstallServices##</td><td>##IDS_ACTIONTEXT_Service2##</td></row>
+		<row><td>InstallValidate</td><td>##IDS_ACTIONTEXT_Validating##</td><td/></row>
+		<row><td>LaunchConditions</td><td>##IDS_ACTIONTEXT_EvaluateLaunchConditions##</td><td/></row>
+		<row><td>MigrateFeatureStates</td><td>##IDS_ACTIONTEXT_MigratingFeatureStates##</td><td>##IDS_ACTIONTEXT_Application##</td></row>
+		<row><td>MoveFiles</td><td>##IDS_ACTIONTEXT_MovingFiles##</td><td>##IDS_ACTIONTEXT_FileDirSize3##</td></row>
+		<row><td>PatchFiles</td><td>##IDS_ACTIONTEXT_PatchingFiles##</td><td>##IDS_ACTIONTEXT_FileDirSize4##</td></row>
+		<row><td>ProcessComponents</td><td>##IDS_ACTIONTEXT_UpdateComponentRegistration##</td><td/></row>
+		<row><td>PublishComponents</td><td>##IDS_ACTIONTEXT_PublishingQualifiedComponents##</td><td>##IDS_ACTIONTEXT_ComponentIDQualifier##</td></row>
+		<row><td>PublishFeatures</td><td>##IDS_ACTIONTEXT_PublishProductFeatures##</td><td>##IDS_ACTIONTEXT_FeatureColon##</td></row>
+		<row><td>PublishProduct</td><td>##IDS_ACTIONTEXT_PublishProductInfo##</td><td/></row>
+		<row><td>RMCCPSearch</td><td>##IDS_ACTIONTEXT_SearchingQualifyingProducts##</td><td/></row>
+		<row><td>RegisterClassInfo</td><td>##IDS_ACTIONTEXT_RegisterClassServer##</td><td>##IDS_ACTIONTEXT_ClassId##</td></row>
+		<row><td>RegisterComPlus</td><td>##IDS_ACTIONTEXT_RegisteringComPlus##</td><td>##IDS_ACTIONTEXT_AppIdAppTypeRSN##</td></row>
+		<row><td>RegisterExtensionInfo</td><td>##IDS_ACTIONTEXT_RegisterExtensionServers##</td><td>##IDS_ACTIONTEXT_Extension2##</td></row>
+		<row><td>RegisterFonts</td><td>##IDS_ACTIONTEXT_RegisterFonts##</td><td>##IDS_ACTIONTEXT_Font##</td></row>
+		<row><td>RegisterMIMEInfo</td><td>##IDS_ACTIONTEXT_RegisterMimeInfo##</td><td>##IDS_ACTIONTEXT_ContentTypeExtension##</td></row>
+		<row><td>RegisterProduct</td><td>##IDS_ACTIONTEXT_RegisteringProduct##</td><td>##IDS_ACTIONTEXT_1b##</td></row>
+		<row><td>RegisterProgIdInfo</td><td>##IDS_ACTIONTEXT_RegisteringProgIdentifiers##</td><td>##IDS_ACTIONTEXT_ProgID2##</td></row>
+		<row><td>RegisterTypeLibraries</td><td>##IDS_ACTIONTEXT_RegisterTypeLibs##</td><td>##IDS_ACTIONTEXT_LibId##</td></row>
+		<row><td>RegisterUser</td><td>##IDS_ACTIONTEXT_RegUser##</td><td>##IDS_ACTIONTEXT_1c##</td></row>
+		<row><td>RemoveDuplicateFiles</td><td>##IDS_ACTIONTEXT_RemovingDuplicates##</td><td>##IDS_ACTIONTEXT_FileDir##</td></row>
+		<row><td>RemoveEnvironmentStrings</td><td>##IDS_ACTIONTEXT_UpdateEnvironmentStrings##</td><td>##IDS_ACTIONTEXT_NameValueAction2##</td></row>
+		<row><td>RemoveExistingProducts</td><td>##IDS_ACTIONTEXT_RemoveApps##</td><td>##IDS_ACTIONTEXT_AppCommandLine##</td></row>
+		<row><td>RemoveFiles</td><td>##IDS_ACTIONTEXT_RemovingFiles##</td><td>##IDS_ACTIONTEXT_FileDir2##</td></row>
+		<row><td>RemoveFolders</td><td>##IDS_ACTIONTEXT_RemovingFolders##</td><td>##IDS_ACTIONTEXT_Folder1##</td></row>
+		<row><td>RemoveIniValues</td><td>##IDS_ACTIONTEXT_RemovingIni##</td><td>##IDS_ACTIONTEXT_FileSectionKeyValue##</td></row>
+		<row><td>RemoveODBC</td><td>##IDS_ACTIONTEXT_RemovingODBC##</td><td/></row>
+		<row><td>RemoveRegistryValues</td><td>##IDS_ACTIONTEXT_RemovingRegistry##</td><td>##IDS_ACTIONTEXT_KeyName##</td></row>
+		<row><td>RemoveShortcuts</td><td>##IDS_ACTIONTEXT_RemovingShortcuts##</td><td>##IDS_ACTIONTEXT_Shortcut1##</td></row>
+		<row><td>Rollback</td><td>##IDS_ACTIONTEXT_RollingBack##</td><td>##IDS_ACTIONTEXT_1d##</td></row>
+		<row><td>RollbackCleanup</td><td>##IDS_ACTIONTEXT_RemovingBackup##</td><td>##IDS_ACTIONTEXT_File2##</td></row>
+		<row><td>SelfRegModules</td><td>##IDS_ACTIONTEXT_RegisteringModules##</td><td>##IDS_ACTIONTEXT_FileFolder##</td></row>
+		<row><td>SelfUnregModules</td><td>##IDS_ACTIONTEXT_UnregisterModules##</td><td>##IDS_ACTIONTEXT_FileFolder2##</td></row>
+		<row><td>SetODBCFolders</td><td>##IDS_ACTIONTEXT_InitializeODBCDirs##</td><td/></row>
+		<row><td>StartServices</td><td>##IDS_ACTIONTEXT_StartingServices##</td><td>##IDS_ACTIONTEXT_Service3##</td></row>
+		<row><td>StopServices</td><td>##IDS_ACTIONTEXT_StoppingServices##</td><td>##IDS_ACTIONTEXT_Service4##</td></row>
+		<row><td>UnmoveFiles</td><td>##IDS_ACTIONTEXT_RemovingMoved##</td><td>##IDS_ACTIONTEXT_FileDir3##</td></row>
+		<row><td>UnpublishComponents</td><td>##IDS_ACTIONTEXT_UnpublishQualified##</td><td>##IDS_ACTIONTEXT_ComponentIdQualifier2##</td></row>
+		<row><td>UnpublishFeatures</td><td>##IDS_ACTIONTEXT_UnpublishProductFeatures##</td><td>##IDS_ACTIONTEXT_Feature##</td></row>
+		<row><td>UnpublishProduct</td><td>##IDS_ACTIONTEXT_UnpublishingProductInfo##</td><td/></row>
+		<row><td>UnregisterClassInfo</td><td>##IDS_ACTIONTEXT_UnregisterClassServers##</td><td>##IDS_ACTIONTEXT_ClsID##</td></row>
+		<row><td>UnregisterComPlus</td><td>##IDS_ACTIONTEXT_UnregisteringComPlus##</td><td>##IDS_ACTIONTEXT_AppId##</td></row>
+		<row><td>UnregisterExtensionInfo</td><td>##IDS_ACTIONTEXT_UnregisterExtensionServers##</td><td>##IDS_ACTIONTEXT_Extension##</td></row>
+		<row><td>UnregisterFonts</td><td>##IDS_ACTIONTEXT_UnregisteringFonts##</td><td>##IDS_ACTIONTEXT_Font2##</td></row>
+		<row><td>UnregisterMIMEInfo</td><td>##IDS_ACTIONTEXT_UnregisteringMimeInfo##</td><td>##IDS_ACTIONTEXT_ContentTypeExtension2##</td></row>
+		<row><td>UnregisterProgIdInfo</td><td>##IDS_ACTIONTEXT_UnregisteringProgramIds##</td><td>##IDS_ACTIONTEXT_ProgID##</td></row>
+		<row><td>UnregisterTypeLibraries</td><td>##IDS_ACTIONTEXT_UnregTypeLibs##</td><td>##IDS_ACTIONTEXT_Libid2##</td></row>
+		<row><td>WriteEnvironmentStrings</td><td>##IDS_ACTIONTEXT_EnvironmentStrings##</td><td>##IDS_ACTIONTEXT_NameValueAction##</td></row>
+		<row><td>WriteIniValues</td><td>##IDS_ACTIONTEXT_WritingINI##</td><td>##IDS_ACTIONTEXT_FileSectionKeyValue2##</td></row>
+		<row><td>WriteRegistryValues</td><td>##IDS_ACTIONTEXT_WritingRegistry##</td><td>##IDS_ACTIONTEXT_KeyNameValue##</td></row>
+		<row><td>caCreateVRoots</td><td>##IDS_ACTIONTEXT_CreatingIISRoots##</td><td/></row>
+		<row><td>caRemoveVRoots</td><td>##IDS_ACTIONTEXT_RemovingIISRoots##</td><td/></row>
+	</table>
+
+	<table name="AdminExecuteSequence">
+		<col key="yes" def="s72">Action</col>
+		<col def="S255">Condition</col>
+		<col def="I2">Sequence</col>
+		<col def="S255">ISComments</col>
+		<col def="I4">ISAttributes</col>
+		<row><td>CostFinalize</td><td/><td>1000</td><td>CostFinalize</td><td/></row>
+		<row><td>CostInitialize</td><td/><td>800</td><td>CostInitialize</td><td/></row>
+		<row><td>FileCost</td><td/><td>900</td><td>FileCost</td><td/></row>
+		<row><td>InstallAdminPackage</td><td/><td>3900</td><td>InstallAdminPackage</td><td/></row>
+		<row><td>InstallFiles</td><td/><td>4000</td><td>InstallFiles</td><td/></row>
+		<row><td>InstallFinalize</td><td/><td>6600</td><td>InstallFinalize</td><td/></row>
+		<row><td>InstallInitialize</td><td/><td>1500</td><td>InstallInitialize</td><td/></row>
+		<row><td>InstallValidate</td><td/><td>1400</td><td>InstallValidate</td><td/></row>
+		<row><td>ScheduleReboot</td><td>ISSCHEDULEREBOOT</td><td>4010</td><td>ScheduleReboot</td><td/></row>
+	</table>
+
+	<table name="AdminUISequence">
+		<col key="yes" def="s72">Action</col>
+		<col def="S255">Condition</col>
+		<col def="I2">Sequence</col>
+		<col def="S255">ISComments</col>
+		<col def="I4">ISAttributes</col>
+		<row><td>AdminWelcome</td><td/><td>1010</td><td>AdminWelcome</td><td/></row>
+		<row><td>CostFinalize</td><td/><td>1000</td><td>CostFinalize</td><td/></row>
+		<row><td>CostInitialize</td><td/><td>800</td><td>CostInitialize</td><td/></row>
+		<row><td>ExecuteAction</td><td/><td>1300</td><td>ExecuteAction</td><td/></row>
+		<row><td>FileCost</td><td/><td>900</td><td>FileCost</td><td/></row>
+		<row><td>SetupCompleteError</td><td/><td>-3</td><td>SetupCompleteError</td><td/></row>
+		<row><td>SetupCompleteSuccess</td><td/><td>-1</td><td>SetupCompleteSuccess</td><td/></row>
+		<row><td>SetupInitialization</td><td/><td>50</td><td>SetupInitialization</td><td/></row>
+		<row><td>SetupInterrupted</td><td/><td>-2</td><td>SetupInterrupted</td><td/></row>
+		<row><td>SetupProgress</td><td/><td>1020</td><td>SetupProgress</td><td/></row>
+	</table>
+
+	<table name="AdvtExecuteSequence">
+		<col key="yes" def="s72">Action</col>
+		<col def="S255">Condition</col>
+		<col def="I2">Sequence</col>
+		<col def="S255">ISComments</col>
+		<col def="I4">ISAttributes</col>
+		<row><td>CostFinalize</td><td/><td>1000</td><td>CostFinalize</td><td/></row>
+		<row><td>CostInitialize</td><td/><td>800</td><td>CostInitialize</td><td/></row>
+		<row><td>CreateShortcuts</td><td/><td>4500</td><td>CreateShortcuts</td><td/></row>
+		<row><td>InstallFinalize</td><td/><td>6600</td><td>InstallFinalize</td><td/></row>
+		<row><td>InstallInitialize</td><td/><td>1500</td><td>InstallInitialize</td><td/></row>
+		<row><td>InstallValidate</td><td/><td>1400</td><td>InstallValidate</td><td/></row>
+		<row><td>MsiPublishAssemblies</td><td/><td>6250</td><td>MsiPublishAssemblies</td><td/></row>
+		<row><td>PublishComponents</td><td/><td>6200</td><td>PublishComponents</td><td/></row>
+		<row><td>PublishFeatures</td><td/><td>6300</td><td>PublishFeatures</td><td/></row>
+		<row><td>PublishProduct</td><td/><td>6400</td><td>PublishProduct</td><td/></row>
+		<row><td>RegisterClassInfo</td><td/><td>4600</td><td>RegisterClassInfo</td><td/></row>
+		<row><td>RegisterExtensionInfo</td><td/><td>4700</td><td>RegisterExtensionInfo</td><td/></row>
+		<row><td>RegisterMIMEInfo</td><td/><td>4900</td><td>RegisterMIMEInfo</td><td/></row>
+		<row><td>RegisterProgIdInfo</td><td/><td>4800</td><td>RegisterProgIdInfo</td><td/></row>
+		<row><td>RegisterTypeLibraries</td><td/><td>4910</td><td>RegisterTypeLibraries</td><td/></row>
+		<row><td>ScheduleReboot</td><td>ISSCHEDULEREBOOT</td><td>6410</td><td>ScheduleReboot</td><td/></row>
+	</table>
+
+	<table name="AdvtUISequence">
+		<col key="yes" def="s72">Action</col>
+		<col def="S255">Condition</col>
+		<col def="I2">Sequence</col>
+		<col def="S255">ISComments</col>
+		<col def="I4">ISAttributes</col>
+	</table>
+
+	<table name="AppId">
+		<col key="yes" def="s38">AppId</col>
+		<col def="S255">RemoteServerName</col>
+		<col def="S255">LocalService</col>
+		<col def="S255">ServiceParameters</col>
+		<col def="S255">DllSurrogate</col>
+		<col def="I2">ActivateAtStorage</col>
+		<col def="I2">RunAsInteractiveUser</col>
+	</table>
+
+	<table name="AppSearch">
+		<col key="yes" def="s72">Property</col>
+		<col key="yes" def="s72">Signature_</col>
+		<row><td>IISROOTFOLDER</td><td>_IISROOTFOLDER</td></row>
+		<row><td>IIS_VERSION</td><td>_IIS_VERSION</td></row>
+	</table>
+
+	<table name="BBControl">
+		<col key="yes" def="s50">Billboard_</col>
+		<col key="yes" def="s50">BBControl</col>
+		<col def="s50">Type</col>
+		<col def="i2">X</col>
+		<col def="i2">Y</col>
+		<col def="i2">Width</col>
+		<col def="i2">Height</col>
+		<col def="I4">Attributes</col>
+		<col def="L50">Text</col>
+	</table>
+
+	<table name="Billboard">
+		<col key="yes" def="s50">Billboard</col>
+		<col def="s38">Feature_</col>
+		<col def="S50">Action</col>
+		<col def="I2">Ordering</col>
+	</table>
+
+	<table name="Binary">
+		<col key="yes" def="s72">Name</col>
+		<col def="V0">Data</col>
+		<col def="S255">ISBuildSourcePath</col>
+		<row><td>ISSELFREG.DLL</td><td/><td>&lt;ISProductFolder&gt;\redist\language independent\i386\isregsvr.dll</td></row>
+		<row><td>NewBinary1</td><td/><td>&lt;ISProductFolder&gt;\Redist\Language Independent\OS Independent\IsDialogBanner.ibd</td></row>
+		<row><td>NewBinary10</td><td/><td>&lt;ISProductFolder&gt;\Redist\Language Independent\OS Independent\CompleteSetupIco.ibd</td></row>
+		<row><td>NewBinary11</td><td/><td>&lt;ISProductFolder&gt;\Redist\Language Independent\OS Independent\CustomSetupIco.ibd</td></row>
+		<row><td>NewBinary12</td><td/><td>&lt;ISProductFolder&gt;\Redist\Language Independent\OS Independent\DestIcon.ibd</td></row>
+		<row><td>NewBinary13</td><td/><td>&lt;ISProductFolder&gt;\Redist\Language Independent\OS Independent\NetworkInstall.ico</td></row>
+		<row><td>NewBinary14</td><td/><td>&lt;ISProductFolder&gt;\Redist\Language Independent\OS Independent\DontInstall.ico</td></row>
+		<row><td>NewBinary15</td><td/><td>&lt;ISProductFolder&gt;\Redist\Language Independent\OS Independent\Install.ico</td></row>
+		<row><td>NewBinary16</td><td/><td>&lt;ISProductFolder&gt;\Redist\Language Independent\OS Independent\InstallFirstUse.ico</td></row>
+		<row><td>NewBinary17</td><td/><td>&lt;ISProductFolder&gt;\Redist\Language Independent\OS Independent\InstallPartial.ico</td></row>
+		<row><td>NewBinary18</td><td/><td>&lt;ISProductFolder&gt;\Redist\Language Independent\OS Independent\InstallStateMenu.ico</td></row>
+		<row><td>NewBinary19</td><td/><td>&lt;ISProjectFolder&gt;\iisfilter.vbs</td></row>
+		<row><td>NewBinary2</td><td/><td>&lt;ISProductFolder&gt;\Redist\Language Independent\OS Independent\New.ibd</td></row>
+		<row><td>NewBinary3</td><td/><td>&lt;ISProductFolder&gt;\Redist\Language Independent\OS Independent\Up.ibd</td></row>
+		<row><td>NewBinary4</td><td/><td>&lt;ISProductFolder&gt;\Redist\Language Independent\OS Independent\WarningIcon.ibd</td></row>
+		<row><td>NewBinary5</td><td/><td>&lt;ISProductFolder&gt;\Redist\Language Independent\OS Independent\IsDialogBitmap.ibd</td></row>
+		<row><td>NewBinary6</td><td/><td>&lt;ISProductFolder&gt;\Redist\Language Independent\OS Independent\CustomSetupIco.ibd</td></row>
+		<row><td>NewBinary7</td><td/><td>&lt;ISProductFolder&gt;\Redist\Language Independent\OS Independent\ReinstIco.ibd</td></row>
+		<row><td>NewBinary8</td><td/><td>&lt;ISProductFolder&gt;\Redist\Language Independent\OS Independent\RemoveIco.ibd</td></row>
+		<row><td>NewBinary9</td><td/><td>&lt;ISProductFolder&gt;\Redist\Language Independent\OS Independent\SetupIcon.ibd</td></row>
+		<row><td>SetAllUsers.dll</td><td/><td>&lt;ISProductFolder&gt;\redist\language independent\i386\SetAllUsers.dll</td></row>
+		<row><td>binIISHelper</td><td/><td>&lt;ISProductFolder&gt;\Support\IISHelper.dll</td></row>
+	</table>
+
+	<table name="BindImage">
+		<col key="yes" def="s72">File_</col>
+		<col def="S255">Path</col>
+	</table>
+
+	<table name="CCPSearch">
+		<col key="yes" def="s72">Signature_</col>
+	</table>
+
+	<table name="CheckBox">
+		<col key="yes" def="s72">Property</col>
+		<col def="S64">Value</col>
+		<row><td>ISCHECKFORPRODUCTUPDATES</td><td>1</td></row>
+		<row><td>LAUNCHPROGRAM</td><td>1</td></row>
+	</table>
+
+	<table name="Class">
+		<col key="yes" def="s38">CLSID</col>
+		<col key="yes" def="s32">Context</col>
+		<col key="yes" def="s72">Component_</col>
+		<col def="S255">ProgId_Default</col>
+		<col def="L255">Description</col>
+		<col def="S38">AppId_</col>
+		<col def="S255">FileTypeMask</col>
+		<col def="S72">Icon_</col>
+		<col def="I2">IconIndex</col>
+		<col def="S32">DefInprocHandler</col>
+		<col def="S255">Argument</col>
+		<col def="s38">Feature_</col>
+		<col def="I2">Attributes</col>
+	</table>
+
+	<table name="ComboBox">
+		<col key="yes" def="s72">Property</col>
+		<col key="yes" def="i2">Order</col>
+		<col def="s64">Value</col>
+		<col def="L64">Text</col>
+		<row><td>IS_SQLSERVER_SERVER</td><td>1</td><td>TestValue</td><td/></row>
+	</table>
+
+	<table name="CompLocator">
+		<col key="yes" def="s72">Signature_</col>
+		<col def="s38">ComponentId</col>
+		<col def="I2">Type</col>
+	</table>
+
+	<table name="Complus">
+		<col key="yes" def="s72">Component_</col>
+		<col key="yes" def="I2">ExpType</col>
+	</table>
+
+	<table name="Component">
+		<col key="yes" def="s72">Component</col>
+		<col def="S38">ComponentId</col>
+		<col def="s72">Directory_</col>
+		<col def="i2">Attributes</col>
+		<col def="S255">Condition</col>
+		<col def="S72">KeyPath</col>
+		<col def="I4">ISAttributes</col>
+		<col def="S255">ISComments</col>
+		<col def="S255">ISScanAtBuildFile</col>
+		<col def="S255">ISRegFileToMergeAtBuild</col>
+		<col def="S0">ISDotNetInstallerArgsInstall</col>
+		<col def="S0">ISDotNetInstallerArgsCommit</col>
+		<col def="S0">ISDotNetInstallerArgsUninstall</col>
+		<col def="S0">ISDotNetInstallerArgsRollback</col>
+		<row><td>AllOtherFiles</td><td>{01621138-5B6F-47D3-B183-8F6107690955}</td><td>BIN</td><td>8</td><td/><td/><td>17</td><td/><td/><td/><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td></row>
+		<row><td>AllOtherFiles1</td><td>{918FECBF-6B1D-4648-A0C9-BAE694AB1DF3}</td><td>CONF</td><td>8</td><td/><td/><td>17</td><td/><td/><td/><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td></row>
+		<row><td>AllOtherFiles10</td><td>{E461BDCC-5045-469B-96DF-CDB381D7413E}</td><td>BIN</td><td>8</td><td/><td/><td>17</td><td/><td/><td/><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td></row>
+		<row><td>AllOtherFiles11</td><td>{BE301E37-3373-4655-B6E0-B39ACCEC85DB}</td><td>BIN</td><td>8</td><td/><td/><td>17</td><td/><td/><td/><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td></row>
+		<row><td>AllOtherFiles12</td><td>{CEBD938A-39BA-4ACD-94F4-E87FF5D65383}</td><td>LOG</td><td>8</td><td/><td/><td>17</td><td/><td/><td/><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td></row>
+		<row><td>AllOtherFiles2</td><td>{BC4C5E05-7E81-40D6-A477-29D1658190F8}</td><td>LOG</td><td>8</td><td/><td/><td>17</td><td/><td/><td/><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td></row>
+		<row><td>AllOtherFiles3</td><td>{66FFA73A-17D7-46D7-911F-3E515C813FFC}</td><td>INSTALLDIR</td><td>8</td><td/><td/><td>17</td><td/><td/><td/><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td></row>
+		<row><td>AllOtherFiles4</td><td>{F7546761-05DF-4ED7-9F6D-854F42CE2333}</td><td>INSTALLDIR</td><td>8</td><td/><td/><td>145</td><td/><td/><td/><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td></row>
+		<row><td>AllOtherFiles5</td><td>{56403C1F-20CA-423C-A296-D95AB3E1E7FF}</td><td>CONF</td><td>8</td><td/><td/><td>17</td><td/><td/><td/><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td></row>
+		<row><td>AllOtherFiles6</td><td>{2D786CD4-E0CB-47FF-9981-F3F1FC266B1A}</td><td>INSTALLDIR</td><td>8</td><td/><td/><td>17</td><td/><td/><td/><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td></row>
+		<row><td>AllOtherFiles7</td><td>{990D5874-56BB-457E-A316-C048C7AF214A}</td><td>INSTALLDIR</td><td>8</td><td/><td/><td>17</td><td/><td/><td/><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td></row>
+		<row><td>AllOtherFiles8</td><td>{C8EC2CC3-13A1-4EED-B195-D44739A00048}</td><td>BIN</td><td>8</td><td/><td/><td>145</td><td/><td/><td/><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td></row>
+		<row><td>AllOtherFiles9</td><td>{80382F22-06B4-4290-B60B-908D38ECBA4D}</td><td>CONF</td><td>8</td><td/><td/><td>17</td><td/><td/><td/><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td></row>
+		<row><td>ISRegistryComponent</td><td>{60AC5270-5430-4CC4-AF22-BE198804A039}</td><td>INSTALLDIR</td><td>8</td><td/><td/><td>17</td><td/><td/><td/><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td></row>
+		<row><td>ISRegistryComponent1</td><td>{695FA56F-E422-429B-AA91-884E5F5DEDCF}</td><td>INSTALLDIR</td><td>8</td><td/><td/><td>17</td><td/><td/><td/><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td></row>
+		<row><td>VirtualDirComponent</td><td>{F2BD5AC6-AB0C-4ED5-889E-148870C25009}</td><td>INSTALLDIR</td><td>8</td><td/><td/><td>17</td><td/><td/><td/><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td><td>/LogFile=</td></row>
+	</table>
+
+	<table name="Condition">
+		<col key="yes" def="s38">Feature_</col>
+		<col key="yes" def="i2">Level</col>
+		<col def="S255">Condition</col>
+	</table>
+
+	<table name="Control">
+		<col key="yes" def="s72">Dialog_</col>
+		<col key="yes" def="s50">Control</col>
+		<col def="s20">Type</col>
+		<col def="i2">X</col>
+		<col def="i2">Y</col>
+		<col def="i2">Width</col>
+		<col def="i2">Height</col>
+		<col def="I4">Attributes</col>
+		<col def="S72">Property</col>
+		<col def="L0">Text</col>
+		<col def="S50">Control_Next</col>
+		<col def="L50">Help</col>
+		<col def="I4">ISWindowStyle</col>
+		<col def="I4">ISControlId</col>
+		<col def="S255">ISBuildSourcePath</col>
+		<col def="S72">Binary_</col>
+		<row><td>AdminChangeFolder</td><td>Banner</td><td>Bitmap</td><td>0</td><td>0</td><td>374</td><td>44</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary1</td></row>
+		<row><td>AdminChangeFolder</td><td>BannerLine</td><td>Line</td><td>0</td><td>44</td><td>374</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>AdminChangeFolder</td><td>Branding1</td><td>Text</td><td>4</td><td>229</td><td>50</td><td>13</td><td>3</td><td/><td>##IDS_INSTALLSHIELD_FORMATTED##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>AdminChangeFolder</td><td>Branding2</td><td>Text</td><td>3</td><td>228</td><td>50</td><td>13</td><td>65537</td><td/><td>##IDS_INSTALLSHIELD##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>AdminChangeFolder</td><td>Cancel</td><td>PushButton</td><td>301</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_CANCEL##</td><td>ComboText</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>AdminChangeFolder</td><td>Combo</td><td>DirectoryCombo</td><td>21</td><td>64</td><td>277</td><td>80</td><td>458755</td><td>TARGETDIR</td><td>##IDS__IsAdminInstallBrowse_4##</td><td>Up</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>AdminChangeFolder</td><td>ComboText</td><td>Text</td><td>21</td><td>50</td><td>99</td><td>14</td><td>3</td><td/><td>##IDS__IsAdminInstallBrowse_LookIn##</td><td>Combo</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>AdminChangeFolder</td><td>DlgDesc</td><td>Text</td><td>21</td><td>23</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS__IsAdminInstallBrowse_BrowseDestination##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>AdminChangeFolder</td><td>DlgLine</td><td>Line</td><td>48</td><td>234</td><td>326</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>AdminChangeFolder</td><td>DlgTitle</td><td>Text</td><td>13</td><td>6</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS__IsAdminInstallBrowse_ChangeDestination##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>AdminChangeFolder</td><td>List</td><td>DirectoryList</td><td>21</td><td>90</td><td>332</td><td>97</td><td>7</td><td>TARGETDIR</td><td>##IDS__IsAdminInstallBrowse_8##</td><td>TailText</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>AdminChangeFolder</td><td>NewFolder</td><td>PushButton</td><td>335</td><td>66</td><td>19</td><td>19</td><td>3670019</td><td/><td/><td>List</td><td>##IDS__IsAdminInstallBrowse_CreateFolder##</td><td>0</td><td/><td/><td>NewBinary2</td></row>
+		<row><td>AdminChangeFolder</td><td>OK</td><td>PushButton</td><td>230</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_OK##</td><td>Cancel</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>AdminChangeFolder</td><td>Tail</td><td>PathEdit</td><td>21</td><td>207</td><td>332</td><td>17</td><td>3</td><td>TARGETDIR</td><td>##IDS__IsAdminInstallBrowse_11##</td><td>OK</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>AdminChangeFolder</td><td>TailText</td><td>Text</td><td>21</td><td>193</td><td>99</td><td>13</td><td>3</td><td/><td>##IDS__IsAdminInstallBrowse_FolderName##</td><td>Tail</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>AdminChangeFolder</td><td>Up</td><td>PushButton</td><td>310</td><td>66</td><td>19</td><td>19</td><td>3670019</td><td/><td/><td>NewFolder</td><td>##IDS__IsAdminInstallBrowse_UpOneLevel##</td><td>0</td><td/><td/><td>NewBinary3</td></row>
+		<row><td>AdminNetworkLocation</td><td>Back</td><td>PushButton</td><td>164</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_BACK##</td><td>InstallNow</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>AdminNetworkLocation</td><td>Banner</td><td>Bitmap</td><td>0</td><td>0</td><td>374</td><td>44</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary1</td></row>
+		<row><td>AdminNetworkLocation</td><td>BannerLine</td><td>Line</td><td>0</td><td>44</td><td>374</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>AdminNetworkLocation</td><td>Branding1</td><td>Text</td><td>4</td><td>229</td><td>50</td><td>13</td><td>3</td><td/><td>##IDS_INSTALLSHIELD_FORMATTED##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>AdminNetworkLocation</td><td>Branding2</td><td>Text</td><td>3</td><td>228</td><td>50</td><td>13</td><td>65537</td><td/><td>##IDS_INSTALLSHIELD##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>AdminNetworkLocation</td><td>Browse</td><td>PushButton</td><td>286</td><td>124</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS__IsAdminInstallPoint_Change##</td><td>Back</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>AdminNetworkLocation</td><td>Cancel</td><td>PushButton</td><td>301</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_CANCEL##</td><td>SetupPathEdit</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>AdminNetworkLocation</td><td>DlgDesc</td><td>Text</td><td>21</td><td>23</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS__IsAdminInstallPoint_SpecifyNetworkLocation##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>AdminNetworkLocation</td><td>DlgLine</td><td>Line</td><td>48</td><td>234</td><td>326</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>AdminNetworkLocation</td><td>DlgText</td><td>Text</td><td>21</td><td>51</td><td>326</td><td>40</td><td>3</td><td/><td>##IDS__IsAdminInstallPoint_EnterNetworkLocation##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>AdminNetworkLocation</td><td>DlgTitle</td><td>Text</td><td>13</td><td>6</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS__IsAdminInstallPoint_NetworkLocationFormatted##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>AdminNetworkLocation</td><td>InstallNow</td><td>PushButton</td><td>230</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS__IsAdminInstallPoint_Install##</td><td>Cancel</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>AdminNetworkLocation</td><td>LBBrowse</td><td>Text</td><td>21</td><td>90</td><td>100</td><td>10</td><td>3</td><td/><td>##IDS__IsAdminInstallPoint_NetworkLocation##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>AdminNetworkLocation</td><td>SetupPathEdit</td><td>PathEdit</td><td>21</td><td>102</td><td>330</td><td>17</td><td>3</td><td>TARGETDIR</td><td/><td>Browse</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>AdminWelcome</td><td>Back</td><td>PushButton</td><td>164</td><td>243</td><td>66</td><td>17</td><td>1</td><td/><td>##IDS_BACK##</td><td>Next</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>AdminWelcome</td><td>Cancel</td><td>PushButton</td><td>301</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_CANCEL##</td><td>Back</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>AdminWelcome</td><td>DlgLine</td><td>Line</td><td>0</td><td>234</td><td>326</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>AdminWelcome</td><td>Image</td><td>Bitmap</td><td>0</td><td>0</td><td>374</td><td>234</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary5</td></row>
+		<row><td>AdminWelcome</td><td>Next</td><td>PushButton</td><td>230</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_NEXT##</td><td>Cancel</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>AdminWelcome</td><td>TextLine1</td><td>Text</td><td>135</td><td>8</td><td>225</td><td>45</td><td>65539</td><td/><td>##IDS__IsAdminInstallPointWelcome_Wizard##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>AdminWelcome</td><td>TextLine2</td><td>Text</td><td>135</td><td>55</td><td>228</td><td>45</td><td>65539</td><td/><td>##IDS__IsAdminInstallPointWelcome_ServerImage##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CancelSetup</td><td>Icon</td><td>Icon</td><td>15</td><td>15</td><td>24</td><td>24</td><td>5242881</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary4</td></row>
+		<row><td>CancelSetup</td><td>No</td><td>PushButton</td><td>135</td><td>57</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS__IsCancelDlg_No##</td><td>Yes</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CancelSetup</td><td>Text</td><td>Text</td><td>48</td><td>15</td><td>194</td><td>30</td><td>3</td><td/><td>##IDS__IsCancelDlg_ConfirmCancel##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CancelSetup</td><td>Yes</td><td>PushButton</td><td>62</td><td>57</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS__IsCancelDlg_Yes##</td><td>No</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetup</td><td>Back</td><td>PushButton</td><td>164</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_BACK##</td><td>Next</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetup</td><td>Banner</td><td>Bitmap</td><td>0</td><td>0</td><td>374</td><td>44</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary1</td></row>
+		<row><td>CustomSetup</td><td>BannerLine</td><td>Line</td><td>0</td><td>44</td><td>374</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetup</td><td>Branding1</td><td>Text</td><td>4</td><td>229</td><td>50</td><td>13</td><td>3</td><td/><td>##IDS_INSTALLSHIELD_FORMATTED##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetup</td><td>Branding2</td><td>Text</td><td>3</td><td>228</td><td>50</td><td>13</td><td>65537</td><td/><td>##IDS_INSTALLSHIELD##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetup</td><td>Cancel</td><td>PushButton</td><td>301</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_CANCEL##</td><td>Tree</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetup</td><td>ChangeFolder</td><td>PushButton</td><td>301</td><td>203</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS__IsCustomSelectionDlg_Change##</td><td>Help</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetup</td><td>Details</td><td>PushButton</td><td>93</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS__IsCustomSelectionDlg_Space##</td><td>Back</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetup</td><td>DlgDesc</td><td>Text</td><td>17</td><td>23</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS__IsCustomSelectionDlg_SelectFeatures##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetup</td><td>DlgLine</td><td>Line</td><td>48</td><td>234</td><td>326</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetup</td><td>DlgText</td><td>Text</td><td>9</td><td>51</td><td>360</td><td>10</td><td>3</td><td/><td>##IDS__IsCustomSelectionDlg_ClickFeatureIcon##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetup</td><td>DlgTitle</td><td>Text</td><td>9</td><td>6</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS__IsCustomSelectionDlg_CustomSetup##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetup</td><td>FeatureGroup</td><td>GroupBox</td><td>235</td><td>67</td><td>131</td><td>120</td><td>1</td><td/><td>##IDS__IsCustomSelectionDlg_FeatureDescription##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetup</td><td>Help</td><td>PushButton</td><td>22</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS__IsCustomSelectionDlg_Help##</td><td>Details</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetup</td><td>InstallLabel</td><td>Text</td><td>8</td><td>190</td><td>360</td><td>10</td><td>3</td><td/><td>##IDS__IsCustomSelectionDlg_InstallTo##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetup</td><td>ItemDescription</td><td>Text</td><td>241</td><td>80</td><td>120</td><td>50</td><td>3</td><td/><td>##IDS__IsCustomSelectionDlg_MultilineDescription##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetup</td><td>Location</td><td>Text</td><td>8</td><td>203</td><td>291</td><td>20</td><td>3</td><td/><td>##IDS__IsCustomSelectionDlg_FeaturePath##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetup</td><td>Next</td><td>PushButton</td><td>230</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_NEXT##</td><td>Cancel</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetup</td><td>Size</td><td>Text</td><td>241</td><td>133</td><td>120</td><td>50</td><td>3</td><td/><td>##IDS__IsCustomSelectionDlg_FeatureSize##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetup</td><td>Tree</td><td>SelectionTree</td><td>8</td><td>70</td><td>220</td><td>118</td><td>7</td><td>_BrowseProperty</td><td/><td>ChangeFolder</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetupTips</td><td>Banner</td><td>Bitmap</td><td>0</td><td>0</td><td>374</td><td>44</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary1</td></row>
+		<row><td>CustomSetupTips</td><td>BannerLine</td><td>Line</td><td>0</td><td>44</td><td>374</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetupTips</td><td>Branding1</td><td>Text</td><td>4</td><td>229</td><td>50</td><td>13</td><td>3</td><td/><td>##IDS_INSTALLSHIELD_FORMATTED##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetupTips</td><td>Branding2</td><td>Text</td><td>3</td><td>228</td><td>50</td><td>13</td><td>65537</td><td/><td>##IDS_INSTALLSHIELD##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetupTips</td><td>DlgDesc</td><td>Text</td><td>21</td><td>23</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS_SetupTips_CustomSetupDescription##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetupTips</td><td>DlgLine</td><td>Line</td><td>48</td><td>234</td><td>326</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetupTips</td><td>DlgTitle</td><td>Text</td><td>13</td><td>6</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS_SetupTips_CustomSetup##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetupTips</td><td>DontInstall</td><td>Icon</td><td>21</td><td>155</td><td>24</td><td>24</td><td>5242881</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary14</td></row>
+		<row><td>CustomSetupTips</td><td>DontInstallText</td><td>Text</td><td>60</td><td>155</td><td>300</td><td>20</td><td>3</td><td/><td>##IDS_SetupTips_WillNotBeInstalled##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetupTips</td><td>FirstInstallText</td><td>Text</td><td>60</td><td>180</td><td>300</td><td>20</td><td>3</td><td/><td>##IDS_SetupTips_Advertise##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetupTips</td><td>Install</td><td>Icon</td><td>21</td><td>105</td><td>24</td><td>24</td><td>5242881</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary15</td></row>
+		<row><td>CustomSetupTips</td><td>InstallFirstUse</td><td>Icon</td><td>21</td><td>180</td><td>24</td><td>24</td><td>5242881</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary16</td></row>
+		<row><td>CustomSetupTips</td><td>InstallPartial</td><td>Icon</td><td>21</td><td>130</td><td>24</td><td>24</td><td>5242881</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary17</td></row>
+		<row><td>CustomSetupTips</td><td>InstallStateMenu</td><td>Icon</td><td>21</td><td>52</td><td>24</td><td>24</td><td>5242881</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary18</td></row>
+		<row><td>CustomSetupTips</td><td>InstallStateText</td><td>Text</td><td>21</td><td>91</td><td>300</td><td>10</td><td>3</td><td/><td>##IDS_SetupTips_InstallState##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetupTips</td><td>InstallText</td><td>Text</td><td>60</td><td>105</td><td>300</td><td>20</td><td>3</td><td/><td>##IDS_SetupTips_AllInstalledLocal##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetupTips</td><td>MenuText</td><td>Text</td><td>50</td><td>52</td><td>300</td><td>36</td><td>3</td><td/><td>##IDS_SetupTips_IconInstallState##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetupTips</td><td>NetworkInstall</td><td>Icon</td><td>21</td><td>205</td><td>24</td><td>24</td><td>5242881</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary13</td></row>
+		<row><td>CustomSetupTips</td><td>NetworkInstallText</td><td>Text</td><td>60</td><td>205</td><td>300</td><td>20</td><td>3</td><td/><td>##IDS_SetupTips_Network##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetupTips</td><td>OK</td><td>PushButton</td><td>301</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_SetupTips_OK##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomSetupTips</td><td>PartialText</td><td>Text</td><td>60</td><td>130</td><td>300</td><td>20</td><td>3</td><td/><td>##IDS_SetupTips_SubFeaturesInstalledLocal##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomerInformation</td><td>Back</td><td>PushButton</td><td>164</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_BACK##</td><td>Next</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomerInformation</td><td>Banner</td><td>Bitmap</td><td>0</td><td>0</td><td>374</td><td>44</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary1</td></row>
+		<row><td>CustomerInformation</td><td>BannerLine</td><td>Line</td><td>0</td><td>44</td><td>374</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomerInformation</td><td>Branding1</td><td>Text</td><td>4</td><td>229</td><td>50</td><td>13</td><td>3</td><td/><td>##IDS_INSTALLSHIELD_FORMATTED##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomerInformation</td><td>Branding2</td><td>Text</td><td>3</td><td>228</td><td>50</td><td>13</td><td>65537</td><td/><td>##IDS_INSTALLSHIELD##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomerInformation</td><td>Cancel</td><td>PushButton</td><td>301</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_CANCEL##</td><td>NameLabel</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomerInformation</td><td>CompanyEdit</td><td>Edit</td><td>21</td><td>100</td><td>237</td><td>17</td><td>3</td><td>COMPANYNAME</td><td>##IDS__IsRegisterUserDlg_Tahoma80##</td><td>SerialLabel</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomerInformation</td><td>CompanyLabel</td><td>Text</td><td>21</td><td>89</td><td>75</td><td>10</td><td>3</td><td/><td>##IDS__IsRegisterUserDlg_Organization##</td><td>CompanyEdit</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomerInformation</td><td>DlgDesc</td><td>Text</td><td>21</td><td>23</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS__IsRegisterUserDlg_PleaseEnterInfo##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomerInformation</td><td>DlgLine</td><td>Line</td><td>48</td><td>234</td><td>326</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomerInformation</td><td>DlgRadioGroupText</td><td>Text</td><td>21</td><td>161</td><td>300</td><td>14</td><td>3</td><td/><td>##IDS__IsRegisterUserDlg_InstallFor##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomerInformation</td><td>DlgTitle</td><td>Text</td><td>13</td><td>6</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS__IsRegisterUserDlg_CustomerInformation##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomerInformation</td><td>NameEdit</td><td>Edit</td><td>21</td><td>63</td><td>237</td><td>17</td><td>3</td><td>USERNAME</td><td>##IDS__IsRegisterUserDlg_Tahoma50##</td><td>CompanyLabel</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomerInformation</td><td>NameLabel</td><td>Text</td><td>21</td><td>52</td><td>75</td><td>10</td><td>3</td><td/><td>##IDS__IsRegisterUserDlg_UserName##</td><td>NameEdit</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomerInformation</td><td>Next</td><td>PushButton</td><td>230</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_NEXT##</td><td>Cancel</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomerInformation</td><td>RadioGroup</td><td>RadioButtonGroup</td><td>63</td><td>170</td><td>300</td><td>50</td><td>3</td><td>ApplicationUsers</td><td>##IDS__IsRegisterUserDlg_16##</td><td>Back</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomerInformation</td><td>SerialLabel</td><td>Text</td><td>21</td><td>127</td><td>109</td><td>10</td><td>2</td><td/><td>##IDS__IsRegisterUserDlg_SerialNumber##</td><td>SerialNumber</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>CustomerInformation</td><td>SerialNumber</td><td>MaskedEdit</td><td>21</td><td>138</td><td>237</td><td>17</td><td>2</td><td>ISX_SERIALNUM</td><td/><td>RadioGroup</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DatabaseFolder</td><td>Back</td><td>PushButton</td><td>164</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_BACK##</td><td>Next</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DatabaseFolder</td><td>Banner</td><td>Bitmap</td><td>0</td><td>0</td><td>374</td><td>44</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary1</td></row>
+		<row><td>DatabaseFolder</td><td>BannerLine</td><td>Line</td><td>0</td><td>44</td><td>374</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DatabaseFolder</td><td>Branding1</td><td>Text</td><td>4</td><td>229</td><td>50</td><td>13</td><td>3</td><td/><td>##IDS_INSTALLSHIELD_FORMATTED##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DatabaseFolder</td><td>Branding2</td><td>Text</td><td>3</td><td>228</td><td>50</td><td>13</td><td>65537</td><td/><td>##IDS_INSTALLSHIELD##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DatabaseFolder</td><td>Cancel</td><td>PushButton</td><td>301</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_CANCEL##</td><td>ChangeFolder</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DatabaseFolder</td><td>ChangeFolder</td><td>PushButton</td><td>301</td><td>65</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_CHANGE##</td><td>Back</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DatabaseFolder</td><td>DatabaseFolder</td><td>Icon</td><td>21</td><td>52</td><td>24</td><td>24</td><td>5242881</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary12</td></row>
+		<row><td>DatabaseFolder</td><td>DlgDesc</td><td>Text</td><td>21</td><td>23</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS__DatabaseFolder_ChangeFolder##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DatabaseFolder</td><td>DlgLine</td><td>Line</td><td>48</td><td>234</td><td>326</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DatabaseFolder</td><td>DlgTitle</td><td>Text</td><td>13</td><td>6</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS__DatabaseFolder_DatabaseFolder##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DatabaseFolder</td><td>LocLabel</td><td>Text</td><td>57</td><td>52</td><td>290</td><td>10</td><td>3</td><td/><td>##IDS_DatabaseFolder_InstallDatabaseTo##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DatabaseFolder</td><td>Location</td><td>Text</td><td>57</td><td>65</td><td>240</td><td>40</td><td>3</td><td>_BrowseProperty</td><td>##IDS__DatabaseFolder_DatabaseDir##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DatabaseFolder</td><td>Next</td><td>PushButton</td><td>230</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_NEXT##</td><td>Cancel</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DestinationFolder</td><td>Back</td><td>PushButton</td><td>164</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_BACK##</td><td>Next</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DestinationFolder</td><td>Banner</td><td>Bitmap</td><td>0</td><td>0</td><td>374</td><td>44</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary1</td></row>
+		<row><td>DestinationFolder</td><td>BannerLine</td><td>Line</td><td>0</td><td>44</td><td>374</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DestinationFolder</td><td>Branding1</td><td>Text</td><td>4</td><td>229</td><td>50</td><td>13</td><td>3</td><td/><td>##IDS_INSTALLSHIELD_FORMATTED##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DestinationFolder</td><td>Branding2</td><td>Text</td><td>3</td><td>228</td><td>50</td><td>13</td><td>65537</td><td/><td>##IDS_INSTALLSHIELD##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DestinationFolder</td><td>Cancel</td><td>PushButton</td><td>301</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_CANCEL##</td><td>ChangeFolder</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DestinationFolder</td><td>ChangeFolder</td><td>PushButton</td><td>301</td><td>65</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS__DestinationFolder_Change##</td><td>Back</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DestinationFolder</td><td>DestFolder</td><td>Icon</td><td>21</td><td>52</td><td>24</td><td>24</td><td>5242881</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary12</td></row>
+		<row><td>DestinationFolder</td><td>DlgDesc</td><td>Text</td><td>21</td><td>23</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS__DestinationFolder_ChangeFolder##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DestinationFolder</td><td>DlgLine</td><td>Line</td><td>48</td><td>234</td><td>326</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DestinationFolder</td><td>DlgTitle</td><td>Text</td><td>13</td><td>6</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS__DestinationFolder_DestinationFolder##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DestinationFolder</td><td>LocLabel</td><td>Text</td><td>57</td><td>52</td><td>290</td><td>10</td><td>3</td><td/><td>##IDS__DestinationFolder_InstallTo##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DestinationFolder</td><td>Location</td><td>Text</td><td>57</td><td>65</td><td>240</td><td>40</td><td>3</td><td>_BrowseProperty</td><td>##IDS_INSTALLDIR##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DestinationFolder</td><td>Next</td><td>PushButton</td><td>230</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_NEXT##</td><td>Cancel</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DiskSpaceRequirements</td><td>Banner</td><td>Bitmap</td><td>0</td><td>0</td><td>374</td><td>44</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary1</td></row>
+		<row><td>DiskSpaceRequirements</td><td>BannerLine</td><td>Line</td><td>0</td><td>44</td><td>374</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DiskSpaceRequirements</td><td>Branding1</td><td>Text</td><td>4</td><td>229</td><td>50</td><td>13</td><td>3</td><td/><td>##IDS_INSTALLSHIELD_FORMATTED##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DiskSpaceRequirements</td><td>Branding2</td><td>Text</td><td>3</td><td>228</td><td>50</td><td>13</td><td>65537</td><td/><td>##IDS_INSTALLSHIELD##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DiskSpaceRequirements</td><td>DlgDesc</td><td>Text</td><td>17</td><td>23</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS__IsFeatureDetailsDlg_SpaceRequired##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DiskSpaceRequirements</td><td>DlgLine</td><td>Line</td><td>48</td><td>234</td><td>326</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DiskSpaceRequirements</td><td>DlgText</td><td>Text</td><td>10</td><td>185</td><td>358</td><td>41</td><td>3</td><td/><td>##IDS__IsFeatureDetailsDlg_VolumesTooSmall##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DiskSpaceRequirements</td><td>DlgTitle</td><td>Text</td><td>9</td><td>6</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS__IsFeatureDetailsDlg_DiskSpaceRequirements##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DiskSpaceRequirements</td><td>List</td><td>VolumeCostList</td><td>8</td><td>55</td><td>358</td><td>125</td><td>393223</td><td/><td>##IDS__IsFeatureDetailsDlg_Numbers##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>DiskSpaceRequirements</td><td>OK</td><td>PushButton</td><td>301</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS__IsFeatureDetailsDlg_OK##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>FilesInUse</td><td>Banner</td><td>Bitmap</td><td>0</td><td>0</td><td>374</td><td>44</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary1</td></row>
+		<row><td>FilesInUse</td><td>BannerLine</td><td>Line</td><td>0</td><td>44</td><td>374</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>FilesInUse</td><td>Branding1</td><td>Text</td><td>4</td><td>229</td><td>50</td><td>13</td><td>3</td><td/><td>##IDS_INSTALLSHIELD_FORMATTED##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>FilesInUse</td><td>Branding2</td><td>Text</td><td>3</td><td>228</td><td>50</td><td>13</td><td>65537</td><td/><td>##IDS_INSTALLSHIELD##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>FilesInUse</td><td>DlgDesc</td><td>Text</td><td>21</td><td>23</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS__IsFilesInUse_FilesInUseMessage##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>FilesInUse</td><td>DlgLine</td><td>Line</td><td>48</td><td>234</td><td>326</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>FilesInUse</td><td>DlgText</td><td>Text</td><td>21</td><td>51</td><td>348</td><td>29</td><td>3</td><td/><td>##IDS__IsFilesInUse_ApplicationsUsingFiles##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>FilesInUse</td><td>DlgTitle</td><td>Text</td><td>13</td><td>6</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS__IsFilesInUse_FilesInUse##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>FilesInUse</td><td>Exit</td><td>PushButton</td><td>301</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS__IsFilesInUse_Exit##</td><td>List</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>FilesInUse</td><td>Ignore</td><td>PushButton</td><td>230</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS__IsFilesInUse_Ignore##</td><td>Exit</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>FilesInUse</td><td>List</td><td>ListBox</td><td>21</td><td>87</td><td>331</td><td>135</td><td>7</td><td>FileInUseProcess</td><td/><td>Retry</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>FilesInUse</td><td>Retry</td><td>PushButton</td><td>164</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS__IsFilesInUse_Retry##</td><td>Ignore</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>InstallChangeFolder</td><td>Banner</td><td>Bitmap</td><td>0</td><td>0</td><td>374</td><td>44</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary1</td></row>
+		<row><td>InstallChangeFolder</td><td>BannerLine</td><td>Line</td><td>0</td><td>44</td><td>374</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>InstallChangeFolder</td><td>Branding1</td><td>Text</td><td>4</td><td>229</td><td>50</td><td>13</td><td>3</td><td/><td>##IDS_INSTALLSHIELD_FORMATTED##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>InstallChangeFolder</td><td>Branding2</td><td>Text</td><td>3</td><td>228</td><td>50</td><td>13</td><td>65537</td><td/><td>##IDS_INSTALLSHIELD##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>InstallChangeFolder</td><td>Cancel</td><td>PushButton</td><td>301</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_CANCEL##</td><td>ComboText</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>InstallChangeFolder</td><td>Combo</td><td>DirectoryCombo</td><td>21</td><td>64</td><td>277</td><td>80</td><td>4128779</td><td>_BrowseProperty</td><td>##IDS__IsBrowseFolderDlg_4##</td><td>Up</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>InstallChangeFolder</td><td>ComboText</td><td>Text</td><td>21</td><td>50</td><td>99</td><td>14</td><td>3</td><td/><td>##IDS__IsBrowseFolderDlg_LookIn##</td><td>Combo</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>InstallChangeFolder</td><td>DlgDesc</td><td>Text</td><td>21</td><td>23</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS__IsBrowseFolderDlg_BrowseDestFolder##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>InstallChangeFolder</td><td>DlgLine</td><td>Line</td><td>48</td><td>234</td><td>326</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>InstallChangeFolder</td><td>DlgTitle</td><td>Text</td><td>13</td><td>6</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS__IsBrowseFolderDlg_ChangeCurrentFolder##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>InstallChangeFolder</td><td>List</td><td>DirectoryList</td><td>21</td><td>90</td><td>332</td><td>97</td><td>15</td><td>_BrowseProperty</td><td>##IDS__IsBrowseFolderDlg_8##</td><td>TailText</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>InstallChangeFolder</td><td>NewFolder</td><td>PushButton</td><td>335</td><td>66</td><td>19</td><td>19</td><td>3670019</td><td/><td/><td>List</td><td>##IDS__IsBrowseFolderDlg_CreateFolder##</td><td>0</td><td/><td/><td>NewBinary2</td></row>
+		<row><td>InstallChangeFolder</td><td>OK</td><td>PushButton</td><td>230</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS__IsBrowseFolderDlg_OK##</td><td>Cancel</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>InstallChangeFolder</td><td>Tail</td><td>PathEdit</td><td>21</td><td>207</td><td>332</td><td>17</td><td>15</td><td>_BrowseProperty</td><td>##IDS__IsBrowseFolderDlg_11##</td><td>OK</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>InstallChangeFolder</td><td>TailText</td><td>Text</td><td>21</td><td>193</td><td>99</td><td>13</td><td>3</td><td/><td>##IDS__IsBrowseFolderDlg_FolderName##</td><td>Tail</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>InstallChangeFolder</td><td>Up</td><td>PushButton</td><td>310</td><td>66</td><td>19</td><td>19</td><td>3670019</td><td/><td/><td>NewFolder</td><td>##IDS__IsBrowseFolderDlg_UpOneLevel##</td><td>0</td><td/><td/><td>NewBinary3</td></row>
+		<row><td>InstallWelcome</td><td>Back</td><td>PushButton</td><td>164</td><td>243</td><td>66</td><td>17</td><td>1</td><td/><td>##IDS_BACK##</td><td>Copyright</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>InstallWelcome</td><td>Cancel</td><td>PushButton</td><td>301</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_CANCEL##</td><td>Back</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>InstallWelcome</td><td>Copyright</td><td>Text</td><td>135</td><td>144</td><td>228</td><td>73</td><td>65539</td><td/><td>##IDS__IsWelcomeDlg_WarningCopyright##</td><td>Next</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>InstallWelcome</td><td>DlgLine</td><td>Line</td><td>0</td><td>234</td><td>374</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>InstallWelcome</td><td>Image</td><td>Bitmap</td><td>0</td><td>0</td><td>374</td><td>234</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary5</td></row>
+		<row><td>InstallWelcome</td><td>Next</td><td>PushButton</td><td>230</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_NEXT##</td><td>Cancel</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>InstallWelcome</td><td>TextLine1</td><td>Text</td><td>135</td><td>8</td><td>225</td><td>45</td><td>65539</td><td/><td>##IDS__IsWelcomeDlg_WelcomeProductName##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>InstallWelcome</td><td>TextLine2</td><td>Text</td><td>135</td><td>55</td><td>228</td><td>45</td><td>65539</td><td/><td>##IDS__IsWelcomeDlg_InstallProductName##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>LicenseAgreement</td><td>Agree</td><td>RadioButtonGroup</td><td>8</td><td>190</td><td>291</td><td>40</td><td>3</td><td>AgreeToLicense</td><td/><td>Back</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>LicenseAgreement</td><td>Back</td><td>PushButton</td><td>164</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_BACK##</td><td>Next</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>LicenseAgreement</td><td>Banner</td><td>Bitmap</td><td>0</td><td>0</td><td>374</td><td>44</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary1</td></row>
+		<row><td>LicenseAgreement</td><td>BannerLine</td><td>Line</td><td>0</td><td>44</td><td>374</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>LicenseAgreement</td><td>Branding1</td><td>Text</td><td>4</td><td>229</td><td>50</td><td>13</td><td>3</td><td/><td>##IDS_INSTALLSHIELD_FORMATTED##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>LicenseAgreement</td><td>Branding2</td><td>Text</td><td>3</td><td>228</td><td>50</td><td>13</td><td>65537</td><td/><td>##IDS_INSTALLSHIELD##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>LicenseAgreement</td><td>Cancel</td><td>PushButton</td><td>301</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_CANCEL##</td><td>ISPrintButton</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>LicenseAgreement</td><td>DlgDesc</td><td>Text</td><td>21</td><td>23</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS__IsLicenseDlg_ReadLicenseAgreement##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>LicenseAgreement</td><td>DlgLine</td><td>Line</td><td>48</td><td>234</td><td>326</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>LicenseAgreement</td><td>DlgTitle</td><td>Text</td><td>13</td><td>6</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS__IsLicenseDlg_LicenseAgreement##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>LicenseAgreement</td><td>ISPrintButton</td><td>PushButton</td><td>301</td><td>188</td><td>65</td><td>17</td><td>3</td><td/><td>##IDS_PRINT_BUTTON##</td><td>Agree</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>LicenseAgreement</td><td>Memo</td><td>ScrollableText</td><td>8</td><td>55</td><td>358</td><td>130</td><td>7</td><td/><td/><td/><td/><td>0</td><td/><td>&lt;ISProjectFolder&gt;\License.rtf</td><td/></row>
+		<row><td>LicenseAgreement</td><td>Next</td><td>PushButton</td><td>230</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_NEXT##</td><td>Cancel</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>MaintenanceType</td><td>Back</td><td>PushButton</td><td>164</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_BACK##</td><td>Next</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>MaintenanceType</td><td>Banner</td><td>Bitmap</td><td>0</td><td>0</td><td>374</td><td>44</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary1</td></row>
+		<row><td>MaintenanceType</td><td>BannerLine</td><td>Line</td><td>0</td><td>44</td><td>374</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>MaintenanceType</td><td>Branding1</td><td>Text</td><td>4</td><td>229</td><td>50</td><td>13</td><td>3</td><td/><td>##IDS_INSTALLSHIELD_FORMATTED##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>MaintenanceType</td><td>Branding2</td><td>Text</td><td>3</td><td>228</td><td>50</td><td>13</td><td>65537</td><td/><td>##IDS_INSTALLSHIELD##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>MaintenanceType</td><td>Cancel</td><td>PushButton</td><td>301</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_CANCEL##</td><td>RadioGroup</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>MaintenanceType</td><td>DlgDesc</td><td>Text</td><td>21</td><td>23</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS__IsMaintenanceDlg_MaitenanceOptions##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>MaintenanceType</td><td>DlgLine</td><td>Line</td><td>48</td><td>234</td><td>326</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>MaintenanceType</td><td>DlgTitle</td><td>Text</td><td>13</td><td>6</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS__IsMaintenanceDlg_ProgramMaintenance##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>MaintenanceType</td><td>Ico1</td><td>Icon</td><td>35</td><td>75</td><td>24</td><td>24</td><td>5242881</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary6</td></row>
+		<row><td>MaintenanceType</td><td>Ico2</td><td>Icon</td><td>35</td><td>135</td><td>24</td><td>24</td><td>5242881</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary7</td></row>
+		<row><td>MaintenanceType</td><td>Ico3</td><td>Icon</td><td>35</td><td>195</td><td>24</td><td>24</td><td>5242881</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary8</td></row>
+		<row><td>MaintenanceType</td><td>Next</td><td>PushButton</td><td>230</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_NEXT##</td><td>Cancel</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>MaintenanceType</td><td>RadioGroup</td><td>RadioButtonGroup</td><td>21</td><td>55</td><td>290</td><td>170</td><td>3</td><td>_IsMaintenance</td><td>##IDS__IsMaintenanceDlg_11##</td><td>Back</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>MaintenanceType</td><td>Text1</td><td>Text</td><td>80</td><td>72</td><td>260</td><td>35</td><td>3</td><td/><td>##IDS__IsMaintenanceDlg_ChangeFeatures##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>MaintenanceType</td><td>Text2</td><td>Text</td><td>80</td><td>135</td><td>260</td><td>35</td><td>3</td><td/><td>##IDS__IsMaintenanceDlg_RepairMessage##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>MaintenanceType</td><td>Text3</td><td>Text</td><td>80</td><td>192</td><td>260</td><td>35</td><td>3</td><td/><td>##IDS__IsMaintenanceDlg_RemoveProductName##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>MaintenanceWelcome</td><td>Back</td><td>PushButton</td><td>164</td><td>243</td><td>66</td><td>17</td><td>1</td><td/><td>##IDS_BACK##</td><td>Next</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>MaintenanceWelcome</td><td>Cancel</td><td>PushButton</td><td>301</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_CANCEL##</td><td>Back</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>MaintenanceWelcome</td><td>DlgLine</td><td>Line</td><td>0</td><td>234</td><td>374</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>MaintenanceWelcome</td><td>Image</td><td>Bitmap</td><td>0</td><td>0</td><td>374</td><td>234</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary5</td></row>
+		<row><td>MaintenanceWelcome</td><td>Next</td><td>PushButton</td><td>230</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_NEXT##</td><td>Cancel</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>MaintenanceWelcome</td><td>TextLine1</td><td>Text</td><td>135</td><td>8</td><td>225</td><td>45</td><td>65539</td><td/><td>##IDS__IsMaintenanceWelcome_WizardWelcome##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>MaintenanceWelcome</td><td>TextLine2</td><td>Text</td><td>135</td><td>55</td><td>228</td><td>50</td><td>65539</td><td/><td>##IDS__IsMaintenanceWelcome_MaintenanceOptionsDescription##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>OutOfSpace</td><td>Banner</td><td>Bitmap</td><td>0</td><td>0</td><td>374</td><td>44</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary1</td></row>
+		<row><td>OutOfSpace</td><td>BannerLine</td><td>Line</td><td>0</td><td>44</td><td>374</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>OutOfSpace</td><td>Branding1</td><td>Text</td><td>4</td><td>229</td><td>50</td><td>13</td><td>3</td><td/><td>##IDS_INSTALLSHIELD_FORMATTED##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>OutOfSpace</td><td>Branding2</td><td>Text</td><td>3</td><td>228</td><td>50</td><td>13</td><td>65537</td><td/><td>##IDS_INSTALLSHIELD##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>OutOfSpace</td><td>DlgDesc</td><td>Text</td><td>21</td><td>23</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS__IsDiskSpaceDlg_DiskSpace##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>OutOfSpace</td><td>DlgLine</td><td>Line</td><td>48</td><td>234</td><td>326</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>OutOfSpace</td><td>DlgText</td><td>Text</td><td>21</td><td>51</td><td>326</td><td>43</td><td>3</td><td/><td>##IDS__IsDiskSpaceDlg_HighlightedVolumes##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>OutOfSpace</td><td>DlgTitle</td><td>Text</td><td>13</td><td>6</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS__IsDiskSpaceDlg_OutOfDiskSpace##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>OutOfSpace</td><td>List</td><td>VolumeCostList</td><td>21</td><td>95</td><td>332</td><td>120</td><td>393223</td><td/><td>##IDS__IsDiskSpaceDlg_Numbers##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>OutOfSpace</td><td>Resume</td><td>PushButton</td><td>301</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS__IsDiskSpaceDlg_OK##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>PatchWelcome</td><td>Back</td><td>PushButton</td><td>164</td><td>243</td><td>66</td><td>17</td><td>1</td><td/><td>##IDS_BACK##</td><td>Next</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>PatchWelcome</td><td>Cancel</td><td>PushButton</td><td>301</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_CANCEL##</td><td>Back</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>PatchWelcome</td><td>DlgLine</td><td>Line</td><td>0</td><td>234</td><td>374</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>PatchWelcome</td><td>Image</td><td>Bitmap</td><td>0</td><td>0</td><td>374</td><td>234</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary5</td></row>
+		<row><td>PatchWelcome</td><td>Next</td><td>PushButton</td><td>230</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS__IsPatchDlg_Update##</td><td>Cancel</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>PatchWelcome</td><td>TextLine1</td><td>Text</td><td>135</td><td>8</td><td>225</td><td>45</td><td>65539</td><td/><td>##IDS__IsPatchDlg_WelcomePatchWizard##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>PatchWelcome</td><td>TextLine2</td><td>Text</td><td>135</td><td>54</td><td>228</td><td>45</td><td>65539</td><td/><td>##IDS__IsPatchDlg_PatchClickUpdate##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>ReadyToInstall</td><td>Back</td><td>PushButton</td><td>164</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_BACK##</td><td>InstallNow</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>ReadyToInstall</td><td>Banner</td><td>Bitmap</td><td>0</td><td>0</td><td>374</td><td>44</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary1</td></row>
+		<row><td>ReadyToInstall</td><td>BannerLine</td><td>Line</td><td>0</td><td>44</td><td>374</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>ReadyToInstall</td><td>Branding1</td><td>Text</td><td>4</td><td>229</td><td>50</td><td>13</td><td>3</td><td/><td>##IDS_INSTALLSHIELD_FORMATTED##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>ReadyToInstall</td><td>Branding2</td><td>Text</td><td>3</td><td>228</td><td>50</td><td>13</td><td>65537</td><td/><td>##IDS_INSTALLSHIELD##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>ReadyToInstall</td><td>Cancel</td><td>PushButton</td><td>301</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_CANCEL##</td><td>Back</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>ReadyToInstall</td><td>DlgDesc</td><td>Text</td><td>21</td><td>23</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS__IsVerifyReadyDlg_WizardReady##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>ReadyToInstall</td><td>DlgLine</td><td>Line</td><td>48</td><td>234</td><td>374</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>ReadyToInstall</td><td>DlgText</td><td>Text</td><td>21</td><td>51</td><td>326</td><td>20</td><td>3</td><td/><td>##IDS__IsVerifyReadyDlg_ClickInstall##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>ReadyToInstall</td><td>DlgText1</td><td>Text</td><td>21</td><td>70</td><td>330</td><td>24</td><td>3</td><td/><td>##IDS__IsVerifyReadyDlg_BackOrCancel##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>ReadyToInstall</td><td>DlgTitle</td><td>Text</td><td>13</td><td>6</td><td>292</td><td>25</td><td>65538</td><td/><td>##IDS__IsVerifyReadyDlg_ModifyReady##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>ReadyToInstall</td><td>DlgTitle2</td><td>Text</td><td>13</td><td>6</td><td>292</td><td>25</td><td>65538</td><td/><td>##IDS__IsVerifyReadyDlg_ReadyRepair##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>ReadyToInstall</td><td>DlgTitle3</td><td>Text</td><td>13</td><td>6</td><td>292</td><td>25</td><td>65538</td><td/><td>##IDS__IsVerifyReadyDlg_ReadyInstall##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>ReadyToInstall</td><td>InstallNow</td><td>PushButton</td><td>230</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS__IsVerifyReadyDlg_Install##</td><td>Cancel</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>ReadyToRemove</td><td>Back</td><td>PushButton</td><td>164</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_BACK##</td><td>RemoveNow</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>ReadyToRemove</td><td>Banner</td><td>Bitmap</td><td>0</td><td>0</td><td>374</td><td>44</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary1</td></row>
+		<row><td>ReadyToRemove</td><td>BannerLine</td><td>Line</td><td>0</td><td>44</td><td>374</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>ReadyToRemove</td><td>Branding1</td><td>Text</td><td>4</td><td>229</td><td>50</td><td>13</td><td>3</td><td/><td>##IDS_INSTALLSHIELD_FORMATTED##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>ReadyToRemove</td><td>Branding2</td><td>Text</td><td>3</td><td>228</td><td>50</td><td>13</td><td>65537</td><td/><td>##IDS_INSTALLSHIELD##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>ReadyToRemove</td><td>Cancel</td><td>PushButton</td><td>301</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_CANCEL##</td><td>Back</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>ReadyToRemove</td><td>DlgDesc</td><td>Text</td><td>21</td><td>23</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS__IsVerifyRemoveAllDlg_ChoseRemoveProgram##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>ReadyToRemove</td><td>DlgLine</td><td>Line</td><td>48</td><td>234</td><td>326</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>ReadyToRemove</td><td>DlgText</td><td>Text</td><td>21</td><td>51</td><td>326</td><td>24</td><td>3</td><td/><td>##IDS__IsVerifyRemoveAllDlg_ClickRemove##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>ReadyToRemove</td><td>DlgText1</td><td>Text</td><td>21</td><td>79</td><td>330</td><td>23</td><td>3</td><td/><td>##IDS__IsVerifyRemoveAllDlg_ClickBack##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>ReadyToRemove</td><td>DlgText2</td><td>Text</td><td>21</td><td>102</td><td>330</td><td>24</td><td>3</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>ReadyToRemove</td><td>DlgTitle</td><td>Text</td><td>13</td><td>6</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS__IsVerifyRemoveAllDlg_RemoveProgram##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>ReadyToRemove</td><td>RemoveNow</td><td>PushButton</td><td>230</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS__IsVerifyRemoveAllDlg_Remove##</td><td>Cancel</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SQLBrowse</td><td>Branding1</td><td>Text</td><td>4</td><td>227</td><td>50</td><td>13</td><td>3</td><td/><td>##IDS_INSTALLSHIELD_FORMATTED##</td><td/><td/><td>0</td><td>0</td><td/><td/></row>
+		<row><td>SQLBrowse</td><td>Branding2</td><td>Text</td><td>3</td><td>226</td><td>50</td><td>13</td><td>65537</td><td/><td>##IDS_INSTALLSHIELD##</td><td/><td/><td>0</td><td>0</td><td/><td/></row>
+		<row><td>SQLBrowse</td><td>Cancel</td><td>PushButton</td><td>194</td><td>241</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_CANCEL##</td><td>TxtSQLBrowse</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SQLBrowse</td><td>DlgLine</td><td>Line</td><td>48</td><td>232</td><td>224</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td>0</td><td/><td/></row>
+		<row><td>SQLBrowse</td><td>OK</td><td>PushButton</td><td>125</td><td>241</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_OK##</td><td>lstSQLServer</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SQLBrowse</td><td>TxtSQLBrowse</td><td>Text</td><td>13</td><td>8</td><td>245</td><td>31</td><td>65539</td><td/><td>##IDS_SQLBROWSE_INTRO##</td><td>OK</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SQLBrowse</td><td>lstSQLServer</td><td>ListBox</td><td>14</td><td>45</td><td>245</td><td>165</td><td>7</td><td>IS_SQLSERVER_LIST</td><td/><td>Cancel</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SQLLogin</td><td>Back</td><td>PushButton</td><td>164</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_BACK##</td><td>Next</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SQLLogin</td><td>Banner</td><td>Bitmap</td><td>0</td><td>0</td><td>374</td><td>44</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary1</td></row>
+		<row><td>SQLLogin</td><td>BannerLine</td><td>Line</td><td>0</td><td>44</td><td>374</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SQLLogin</td><td>Branding1</td><td>Text</td><td>4</td><td>229</td><td>50</td><td>13</td><td>3</td><td/><td>##IDS_INSTALLSHIELD_FORMATTED##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SQLLogin</td><td>Branding2</td><td>Text</td><td>3</td><td>228</td><td>50</td><td>13</td><td>65537</td><td/><td>##IDS_INSTALLSHIELD##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SQLLogin</td><td>BtnSQLBrowse</td><td>PushButton</td><td>301</td><td>106</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_SQLLOGIN_BROWSE##</td><td>lblAuthentication</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SQLLogin</td><td>Cancel</td><td>PushButton</td><td>301</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_CANCEL##</td><td>lblServer</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SQLLogin</td><td>DlgDesc</td><td>Text</td><td>21</td><td>23</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS_SQLLOGIN_DESC##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SQLLogin</td><td>DlgLine</td><td>Line</td><td>48</td><td>234</td><td>326</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SQLLogin</td><td>DlgTitle</td><td>Text</td><td>13</td><td>7</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS_SQLLOGIN_TITLE##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SQLLogin</td><td>EdtLogin</td><td>Edit</td><td>92</td><td>184</td><td>218</td><td>16</td><td>3</td><td>IS_SQLSERVER_USERNAME</td><td/><td>lblPswd</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SQLLogin</td><td>EdtPswd</td><td>Edit</td><td>92</td><td>208</td><td>218</td><td>16</td><td>2097155</td><td>IS_SQLSERVER_PASSWORD</td><td/><td>Back</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SQLLogin</td><td>Next</td><td>PushButton</td><td>229</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_NEXT##</td><td>Cancel</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SQLLogin</td><td>RadioButtonGroup1</td><td>RadioButtonGroup</td><td>25</td><td>145</td><td>343</td><td>34</td><td>3</td><td>IS_SQLSERVER_AUTHENTICATION</td><td/><td>lblLoginID</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SQLLogin</td><td>cboServers</td><td>ComboBox</td><td>18</td><td>106</td><td>278</td><td>80</td><td>65539</td><td>IS_SQLSERVER_SERVER</td><td/><td>BtnSQLBrowse</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SQLLogin</td><td>lblAuthentication</td><td>Text</td><td>18</td><td>129</td><td>334</td><td>14</td><td>131075</td><td/><td>##IDS_SQLLOGIN_CONNECT##</td><td>RadioButtonGroup1</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SQLLogin</td><td>lblLoginID</td><td>Text</td><td>43</td><td>186</td><td>48</td><td>13</td><td>3</td><td/><td>##IDS_SQLLOGIN_ID##</td><td>EdtLogin</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SQLLogin</td><td>lblPswd</td><td>Text</td><td>43</td><td>208</td><td>47</td><td>13</td><td>3</td><td/><td>##IDS_SQLLOGIN_PSWD##</td><td>EdtPswd</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SQLLogin</td><td>lblSQLLoginIntro</td><td>Text</td><td>17</td><td>50</td><td>345</td><td>40</td><td>65539</td><td/><td>##IDS_SQLLOGIN_INTRO##</td><td>cboServers</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SQLLogin</td><td>lblServer</td><td>Text</td><td>18</td><td>92</td><td>88</td><td>14</td><td>3</td><td/><td>##IDS_SQLLOGIN_SERVER##</td><td>lblSQLLoginIntro</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupCompleteError</td><td>Back</td><td>PushButton</td><td>164</td><td>243</td><td>66</td><td>17</td><td>1</td><td/><td>##IDS_BACK##</td><td>Finish</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupCompleteError</td><td>Cancel</td><td>PushButton</td><td>301</td><td>243</td><td>66</td><td>17</td><td>1</td><td/><td>##IDS_CANCEL##</td><td>Back</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupCompleteError</td><td>DlgLine</td><td>Line</td><td>0</td><td>234</td><td>374</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupCompleteError</td><td>Finish</td><td>PushButton</td><td>230</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS__IsFatalError_Finish##</td><td>Cancel</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupCompleteError</td><td>FinishText1</td><td>Text</td><td>135</td><td>80</td><td>228</td><td>50</td><td>65539</td><td/><td>##IDS__IsFatalError_NotModified##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupCompleteError</td><td>FinishText2</td><td>Text</td><td>135</td><td>135</td><td>228</td><td>25</td><td>65539</td><td/><td>##IDS__IsFatalError_ClickFinish##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupCompleteError</td><td>Image</td><td>Bitmap</td><td>0</td><td>0</td><td>374</td><td>234</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary5</td></row>
+		<row><td>SetupCompleteError</td><td>RestContText1</td><td>Text</td><td>135</td><td>80</td><td>228</td><td>50</td><td>65539</td><td/><td>##IDS__IsFatalError_KeepOrRestore##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupCompleteError</td><td>RestContText2</td><td>Text</td><td>135</td><td>135</td><td>228</td><td>25</td><td>65539</td><td/><td>##IDS__IsFatalError_RestoreOrContinueLater##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupCompleteError</td><td>TextLine1</td><td>Text</td><td>135</td><td>8</td><td>225</td><td>45</td><td>65539</td><td/><td>##IDS__IsFatalError_WizardCompleted##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupCompleteError</td><td>TextLine2</td><td>Text</td><td>135</td><td>55</td><td>228</td><td>25</td><td>65539</td><td/><td>##IDS__IsFatalError_WizardInterrupted##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupCompleteSuccess</td><td>Back</td><td>PushButton</td><td>164</td><td>243</td><td>66</td><td>17</td><td>1</td><td/><td>##IDS_BACK##</td><td>OK</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupCompleteSuccess</td><td>Cancel</td><td>PushButton</td><td>301</td><td>243</td><td>66</td><td>17</td><td>1</td><td/><td>##IDS_CANCEL##</td><td>Image</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupCompleteSuccess</td><td>CheckBoxUpdates</td><td>CheckBox</td><td>135</td><td>164</td><td>10</td><td>9</td><td>2</td><td>ISCHECKFORPRODUCTUPDATES</td><td>CheckBox1</td><td>Back</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupCompleteSuccess</td><td>CheckForUpdatesText</td><td>Text</td><td>152</td><td>162</td><td>190</td><td>30</td><td>65538</td><td/><td>##IDS__IsExitDialog_Update_YesCheckForUpdates##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupCompleteSuccess</td><td>CheckLaunchProgram</td><td>CheckBox</td><td>151</td><td>114</td><td>10</td><td>9</td><td>2</td><td>LAUNCHPROGRAM</td><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupCompleteSuccess</td><td>CheckLaunchReadme</td><td>CheckBox</td><td>151</td><td>148</td><td>10</td><td>9</td><td>2</td><td>LAUNCHREADME</td><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupCompleteSuccess</td><td>DlgLine</td><td>Line</td><td>0</td><td>234</td><td>374</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupCompleteSuccess</td><td>Image</td><td>Bitmap</td><td>0</td><td>0</td><td>374</td><td>234</td><td>1</td><td/><td/><td>CheckBoxUpdates</td><td/><td>0</td><td/><td/><td>NewBinary5</td></row>
+		<row><td>SetupCompleteSuccess</td><td>LaunchProgramText</td><td>Text</td><td>164</td><td>112</td><td>198</td><td>15</td><td>65538</td><td/><td>Launch [ProductName]</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupCompleteSuccess</td><td>LaunchReadmeText</td><td>Text</td><td>164</td><td>148</td><td>198</td><td>13</td><td>65538</td><td/><td>##IDS__IsExitDialog_ShowReadMe##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupCompleteSuccess</td><td>OK</td><td>PushButton</td><td>230</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS__IsExitDialog_Finish##</td><td>Cancel</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupCompleteSuccess</td><td>TextLine1</td><td>Text</td><td>135</td><td>8</td><td>225</td><td>45</td><td>65539</td><td/><td>##IDS__IsExitDialog_WizardCompleted##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupCompleteSuccess</td><td>TextLine2</td><td>Text</td><td>135</td><td>55</td><td>228</td><td>45</td><td>65538</td><td/><td>##IDS__IsExitDialog_InstallSuccess##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupCompleteSuccess</td><td>TextLine3</td><td>Text</td><td>135</td><td>55</td><td>228</td><td>45</td><td>65538</td><td/><td>##IDS__IsExitDialog_UninstallSuccess##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupCompleteSuccess</td><td>UpdateTextLine1</td><td>Text</td><td>135</td><td>30</td><td>228</td><td>45</td><td>65538</td><td/><td>##IDS__IsExitDialog_Update_SetupFinished##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupCompleteSuccess</td><td>UpdateTextLine2</td><td>Text</td><td>135</td><td>80</td><td>228</td><td>45</td><td>65538</td><td/><td>##IDS__IsExitDialog_Update_PossibleUpdates##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupCompleteSuccess</td><td>UpdateTextLine3</td><td>Text</td><td>135</td><td>120</td><td>228</td><td>45</td><td>65538</td><td/><td>##IDS__IsExitDialog_Update_InternetConnection##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupError</td><td>A</td><td>PushButton</td><td>192</td><td>80</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS__IsErrorDlg_Abort##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupError</td><td>C</td><td>PushButton</td><td>192</td><td>80</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_CANCEL2##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupError</td><td>ErrorIcon</td><td>Icon</td><td>15</td><td>15</td><td>24</td><td>24</td><td>5242881</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary4</td></row>
+		<row><td>SetupError</td><td>ErrorText</td><td>Text</td><td>50</td><td>15</td><td>200</td><td>50</td><td>3</td><td/><td>##IDS__IsErrorDlg_ErrorText##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupError</td><td>I</td><td>PushButton</td><td>192</td><td>80</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS__IsErrorDlg_Ignore##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupError</td><td>N</td><td>PushButton</td><td>192</td><td>80</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS__IsErrorDlg_NO##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupError</td><td>O</td><td>PushButton</td><td>192</td><td>80</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS__IsErrorDlg_OK##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupError</td><td>R</td><td>PushButton</td><td>192</td><td>80</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS__IsErrorDlg_Retry##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupError</td><td>Y</td><td>PushButton</td><td>192</td><td>80</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS__IsErrorDlg_Yes##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupInitialization</td><td>ActionData</td><td>Text</td><td>135</td><td>125</td><td>228</td><td>12</td><td>65539</td><td/><td>##IDS__IsInitDlg_1##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupInitialization</td><td>ActionText</td><td>Text</td><td>135</td><td>109</td><td>220</td><td>36</td><td>65539</td><td/><td>##IDS__IsInitDlg_2##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupInitialization</td><td>Back</td><td>PushButton</td><td>164</td><td>243</td><td>66</td><td>17</td><td>1</td><td/><td>##IDS_BACK##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupInitialization</td><td>Cancel</td><td>PushButton</td><td>301</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_CANCEL##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupInitialization</td><td>DlgLine</td><td>Line</td><td>0</td><td>234</td><td>374</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupInitialization</td><td>Image</td><td>Bitmap</td><td>0</td><td>0</td><td>374</td><td>234</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary5</td></row>
+		<row><td>SetupInitialization</td><td>Next</td><td>PushButton</td><td>230</td><td>243</td><td>66</td><td>17</td><td>1</td><td/><td>##IDS_NEXT##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupInitialization</td><td>TextLine1</td><td>Text</td><td>135</td><td>8</td><td>225</td><td>45</td><td>65539</td><td/><td>##IDS__IsInitDlg_WelcomeWizard##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupInitialization</td><td>TextLine2</td><td>Text</td><td>135</td><td>55</td><td>228</td><td>30</td><td>65539</td><td/><td>##IDS__IsInitDlg_PreparingWizard##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupInterrupted</td><td>Back</td><td>PushButton</td><td>164</td><td>243</td><td>66</td><td>17</td><td>1</td><td/><td>##IDS_BACK##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupInterrupted</td><td>Cancel</td><td>PushButton</td><td>301</td><td>243</td><td>66</td><td>17</td><td>1</td><td/><td>##IDS_CANCEL##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupInterrupted</td><td>DlgLine</td><td>Line</td><td>0</td><td>234</td><td>374</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupInterrupted</td><td>Finish</td><td>PushButton</td><td>230</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS__IsUserExit_Finish##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupInterrupted</td><td>FinishText1</td><td>Text</td><td>135</td><td>80</td><td>228</td><td>50</td><td>65539</td><td/><td>##IDS__IsUserExit_NotModified##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupInterrupted</td><td>FinishText2</td><td>Text</td><td>135</td><td>135</td><td>228</td><td>25</td><td>65539</td><td/><td>##IDS__IsUserExit_ClickFinish##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupInterrupted</td><td>Image</td><td>Bitmap</td><td>0</td><td>0</td><td>374</td><td>234</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary5</td></row>
+		<row><td>SetupInterrupted</td><td>RestContText1</td><td>Text</td><td>135</td><td>80</td><td>228</td><td>50</td><td>65539</td><td/><td>##IDS__IsUserExit_KeepOrRestore##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupInterrupted</td><td>RestContText2</td><td>Text</td><td>135</td><td>135</td><td>228</td><td>25</td><td>65539</td><td/><td>##IDS__IsUserExit_RestoreOrContinue##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupInterrupted</td><td>TextLine1</td><td>Text</td><td>135</td><td>8</td><td>225</td><td>45</td><td>65539</td><td/><td>##IDS__IsUserExit_WizardCompleted##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupInterrupted</td><td>TextLine2</td><td>Text</td><td>135</td><td>55</td><td>228</td><td>25</td><td>65539</td><td/><td>##IDS__IsUserExit_WizardInterrupted##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupProgress</td><td>ActionProgress95</td><td>ProgressBar</td><td>59</td><td>113</td><td>275</td><td>12</td><td>65537</td><td/><td>##IDS__IsProgressDlg_ProgressDone##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupProgress</td><td>ActionText</td><td>Text</td><td>59</td><td>100</td><td>275</td><td>12</td><td>3</td><td/><td>##IDS__IsProgressDlg_2##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupProgress</td><td>Back</td><td>PushButton</td><td>164</td><td>243</td><td>66</td><td>17</td><td>1</td><td/><td>##IDS_BACK##</td><td>Next</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupProgress</td><td>Banner</td><td>Bitmap</td><td>0</td><td>0</td><td>374</td><td>44</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary1</td></row>
+		<row><td>SetupProgress</td><td>BannerLine</td><td>Line</td><td>0</td><td>44</td><td>374</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupProgress</td><td>Branding1</td><td>Text</td><td>4</td><td>229</td><td>50</td><td>13</td><td>3</td><td/><td>##IDS_INSTALLSHIELD_FORMATTED##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupProgress</td><td>Branding2</td><td>Text</td><td>3</td><td>228</td><td>50</td><td>13</td><td>65537</td><td/><td>##IDS_INSTALLSHIELD##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupProgress</td><td>Cancel</td><td>PushButton</td><td>301</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_CANCEL##</td><td>Back</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupProgress</td><td>DlgDesc</td><td>Text</td><td>21</td><td>23</td><td>292</td><td>25</td><td>65538</td><td/><td>##IDS__IsProgressDlg_UninstallingFeatures2##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupProgress</td><td>DlgDesc2</td><td>Text</td><td>21</td><td>23</td><td>292</td><td>25</td><td>65538</td><td/><td>##IDS__IsProgressDlg_UninstallingFeatures##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupProgress</td><td>DlgLine</td><td>Line</td><td>48</td><td>234</td><td>326</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupProgress</td><td>DlgText</td><td>Text</td><td>59</td><td>51</td><td>275</td><td>30</td><td>65538</td><td/><td>##IDS__IsProgressDlg_WaitUninstall2##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupProgress</td><td>DlgText2</td><td>Text</td><td>59</td><td>51</td><td>275</td><td>30</td><td>65538</td><td/><td>##IDS__IsProgressDlg_WaitUninstall##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupProgress</td><td>DlgTitle</td><td>Text</td><td>13</td><td>6</td><td>292</td><td>25</td><td>65538</td><td/><td>##IDS__IsProgressDlg_InstallingProductName##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupProgress</td><td>DlgTitle2</td><td>Text</td><td>13</td><td>6</td><td>292</td><td>25</td><td>65538</td><td/><td>##IDS__IsProgressDlg_Uninstalling##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupProgress</td><td>LbSec</td><td>Text</td><td>172</td><td>139</td><td>32</td><td>12</td><td>2</td><td/><td>##IDS__IsProgressDlg_SecHidden##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupProgress</td><td>LbStatus</td><td>Text</td><td>59</td><td>85</td><td>70</td><td>12</td><td>3</td><td/><td>##IDS__IsProgressDlg_Status##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupProgress</td><td>Next</td><td>PushButton</td><td>230</td><td>243</td><td>66</td><td>17</td><td>1</td><td/><td>##IDS_NEXT##</td><td>Cancel</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupProgress</td><td>SetupIcon</td><td>Icon</td><td>21</td><td>51</td><td>24</td><td>24</td><td>5242881</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary9</td></row>
+		<row><td>SetupProgress</td><td>ShowTime</td><td>Text</td><td>155</td><td>139</td><td>17</td><td>12</td><td>2</td><td/><td>##IDS__IsProgressDlg_Hidden##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupProgress</td><td>TextTime</td><td>Text</td><td>59</td><td>139</td><td>96</td><td>12</td><td>2</td><td/><td>##IDS__IsProgressDlg_HiddenTimeRemaining##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupResume</td><td>Back</td><td>PushButton</td><td>164</td><td>243</td><td>66</td><td>17</td><td>1</td><td/><td>##IDS_BACK##</td><td>Next</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupResume</td><td>Cancel</td><td>PushButton</td><td>301</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_CANCEL##</td><td>Back</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupResume</td><td>DlgLine</td><td>Line</td><td>0</td><td>234</td><td>374</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupResume</td><td>Image</td><td>Bitmap</td><td>0</td><td>0</td><td>374</td><td>234</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary5</td></row>
+		<row><td>SetupResume</td><td>Next</td><td>PushButton</td><td>230</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_NEXT##</td><td>Cancel</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupResume</td><td>PreselectedText</td><td>Text</td><td>135</td><td>55</td><td>228</td><td>45</td><td>65539</td><td/><td>##IDS__IsResumeDlg_WizardResume##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupResume</td><td>ResumeText</td><td>Text</td><td>135</td><td>46</td><td>228</td><td>45</td><td>65539</td><td/><td>##IDS__IsResumeDlg_ResumeSuspended##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupResume</td><td>TextLine1</td><td>Text</td><td>135</td><td>8</td><td>225</td><td>45</td><td>65539</td><td/><td>##IDS__IsResumeDlg_Resuming##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupType</td><td>Back</td><td>PushButton</td><td>164</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_BACK##</td><td>Next</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupType</td><td>Banner</td><td>Bitmap</td><td>0</td><td>0</td><td>374</td><td>44</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary1</td></row>
+		<row><td>SetupType</td><td>BannerLine</td><td>Line</td><td>0</td><td>44</td><td>374</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupType</td><td>Branding1</td><td>Text</td><td>4</td><td>229</td><td>50</td><td>13</td><td>3</td><td/><td>##IDS_INSTALLSHIELD_FORMATTED##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupType</td><td>Branding2</td><td>Text</td><td>3</td><td>228</td><td>50</td><td>13</td><td>65537</td><td/><td>##IDS_INSTALLSHIELD##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupType</td><td>Cancel</td><td>PushButton</td><td>301</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_CANCEL##</td><td>RadioGroup</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupType</td><td>CompText</td><td>Text</td><td>80</td><td>94</td><td>246</td><td>35</td><td>3</td><td/><td>##IDS__IsSetupTypeMinDlg_AllFeatures##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupType</td><td>CompleteIco</td><td>Icon</td><td>34</td><td>94</td><td>24</td><td>24</td><td>5242881</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary10</td></row>
+		<row><td>SetupType</td><td>CustText</td><td>Text</td><td>80</td><td>154</td><td>246</td><td>35</td><td>3</td><td/><td>##IDS__IsSetupTypeMinDlg_ChooseFeatures##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupType</td><td>CustomIco</td><td>Icon</td><td>34</td><td>154</td><td>24</td><td>24</td><td>5242881</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary6</td></row>
+		<row><td>SetupType</td><td>DlgDesc</td><td>Text</td><td>21</td><td>23</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS__IsSetupTypeMinDlg_ChooseSetupType##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupType</td><td>DlgLine</td><td>Line</td><td>48</td><td>234</td><td>326</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupType</td><td>DlgText</td><td>Text</td><td>21</td><td>51</td><td>326</td><td>10</td><td>3</td><td/><td>##IDS__IsSetupTypeMinDlg_SelectSetupType##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupType</td><td>DlgTitle</td><td>Text</td><td>13</td><td>6</td><td>292</td><td>25</td><td>65539</td><td/><td>##IDS__IsSetupTypeMinDlg_SetupType##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupType</td><td>Next</td><td>PushButton</td><td>230</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_NEXT##</td><td>Cancel</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SetupType</td><td>RadioGroup</td><td>RadioButtonGroup</td><td>21</td><td>79</td><td>264</td><td>120</td><td>3</td><td>_IsSetupTypeMin</td><td>##IDS__IsSetupTypeMinDlg_13##</td><td>Back</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SplashBitmap</td><td>Back</td><td>PushButton</td><td>164</td><td>243</td><td>66</td><td>17</td><td>1</td><td/><td>##IDS_BACK##</td><td>Next</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SplashBitmap</td><td>Branding1</td><td>Text</td><td>4</td><td>229</td><td>50</td><td>13</td><td>3</td><td/><td>##IDS_INSTALLSHIELD_FORMATTED##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SplashBitmap</td><td>Branding2</td><td>Text</td><td>3</td><td>228</td><td>50</td><td>13</td><td>65537</td><td/><td>##IDS_INSTALLSHIELD##</td><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SplashBitmap</td><td>Cancel</td><td>PushButton</td><td>301</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_CANCEL##</td><td>Back</td><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SplashBitmap</td><td>DlgLine</td><td>Line</td><td>48</td><td>234</td><td>374</td><td>0</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td/></row>
+		<row><td>SplashBitmap</td><td>Image</td><td>Bitmap</td><td>13</td><td>12</td><td>349</td><td>211</td><td>1</td><td/><td/><td/><td/><td>0</td><td/><td/><td>NewBinary5</td></row>
+		<row><td>SplashBitmap</td><td>Next</td><td>PushButton</td><td>230</td><td>243</td><td>66</td><td>17</td><td>3</td><td/><td>##IDS_NEXT##</td><td>Cancel</td><td/><td>0</td><td/><td/><td/></row>
+	</table>
+
+	<table name="ControlCondition">
+		<col key="yes" def="s72">Dialog_</col>
+		<col key="yes" def="s50">Control_</col>
+		<col key="yes" def="s50">Action</col>
+		<col key="yes" def="s255">Condition</col>
+		<row><td>CustomSetup</td><td>ChangeFolder</td><td>Hide</td><td>Installed</td></row>
+		<row><td>CustomSetup</td><td>Details</td><td>Hide</td><td>Installed</td></row>
+		<row><td>CustomSetup</td><td>InstallLabel</td><td>Hide</td><td>Installed</td></row>
+		<row><td>CustomerInformation</td><td>DlgRadioGroupText</td><td>Hide</td><td>NOT Privileged</td></row>
+		<row><td>CustomerInformation</td><td>DlgRadioGroupText</td><td>Hide</td><td>ProductState &gt; 0</td></row>
+		<row><td>CustomerInformation</td><td>DlgRadioGroupText</td><td>Hide</td><td>Version9X</td></row>
+		<row><td>CustomerInformation</td><td>RadioGroup</td><td>Hide</td><td>NOT Privileged</td></row>
+		<row><td>CustomerInformation</td><td>RadioGroup</td><td>Hide</td><td>ProductState &gt; 0</td></row>
+		<row><td>CustomerInformation</td><td>RadioGroup</td><td>Hide</td><td>Version9X</td></row>
+		<row><td>CustomerInformation</td><td>SerialLabel</td><td>Show</td><td>SERIALNUMSHOW</td></row>
+		<row><td>CustomerInformation</td><td>SerialNumber</td><td>Show</td><td>SERIALNUMSHOW</td></row>
+		<row><td>InstallWelcome</td><td>Copyright</td><td>Hide</td><td>SHOWCOPYRIGHT="No"</td></row>
+		<row><td>InstallWelcome</td><td>Copyright</td><td>Show</td><td>SHOWCOPYRIGHT="Yes"</td></row>
+		<row><td>LicenseAgreement</td><td>Next</td><td>Disable</td><td>AgreeToLicense &lt;&gt; "Yes"</td></row>
+		<row><td>LicenseAgreement</td><td>Next</td><td>Enable</td><td>AgreeToLicense = "Yes"</td></row>
+		<row><td>ReadyToInstall</td><td>DlgTitle</td><td>Show</td><td>ProgressType0="Modify"</td></row>
+		<row><td>ReadyToInstall</td><td>DlgTitle2</td><td>Show</td><td>ProgressType0="Repair"</td></row>
+		<row><td>ReadyToInstall</td><td>DlgTitle3</td><td>Show</td><td>ProgressType0="install"</td></row>
+		<row><td>SQLLogin</td><td>EdtLogin</td><td>Disable</td><td>IS_SQLSERVER_AUTHENTICATION=0</td></row>
+		<row><td>SQLLogin</td><td>EdtLogin</td><td>Enable</td><td>IS_SQLSERVER_AUTHENTICATION=1</td></row>
+		<row><td>SQLLogin</td><td>EdtPswd</td><td>Disable</td><td>IS_SQLSERVER_AUTHENTICATION=0</td></row>
+		<row><td>SQLLogin</td><td>EdtPswd</td><td>Enable</td><td>IS_SQLSERVER_AUTHENTICATION=1</td></row>
+		<row><td>SQLLogin</td><td>lblLoginID</td><td>Disable</td><td>IS_SQLSERVER_AUTHENTICATION=0</td></row>
+		<row><td>SQLLogin</td><td>lblLoginID</td><td>Enable</td><td>IS_SQLSERVER_AUTHENTICATION=1</td></row>
+		<row><td>SQLLogin</td><td>lblPswd</td><td>Disable</td><td>IS_SQLSERVER_AUTHENTICATION=0</td></row>
+		<row><td>SQLLogin</td><td>lblPswd</td><td>Enable</td><td>IS_SQLSERVER_AUTHENTICATION=1</td></row>
+		<row><td>SetupCompleteError</td><td>Back</td><td>Default</td><td>UpdateStarted</td></row>
+		<row><td>SetupCompleteError</td><td>Back</td><td>Disable</td><td>NOT UpdateStarted</td></row>
+		<row><td>SetupCompleteError</td><td>Back</td><td>Enable</td><td>UpdateStarted</td></row>
+		<row><td>SetupCompleteError</td><td>Cancel</td><td>Disable</td><td>NOT UpdateStarted</td></row>
+		<row><td>SetupCompleteError</td><td>Cancel</td><td>Enable</td><td>UpdateStarted</td></row>
+		<row><td>SetupCompleteError</td><td>Finish</td><td>Default</td><td>NOT UpdateStarted</td></row>
+		<row><td>SetupCompleteError</td><td>FinishText1</td><td>Hide</td><td>UpdateStarted</td></row>
+		<row><td>SetupCompleteError</td><td>FinishText1</td><td>Show</td><td>NOT UpdateStarted</td></row>
+		<row><td>SetupCompleteError</td><td>FinishText2</td><td>Hide</td><td>UpdateStarted</td></row>
+		<row><td>SetupCompleteError</td><td>FinishText2</td><td>Show</td><td>NOT UpdateStarted</td></row>
+		<row><td>SetupCompleteError</td><td>RestContText1</td><td>Hide</td><td>NOT UpdateStarted</td></row>
+		<row><td>SetupCompleteError</td><td>RestContText1</td><td>Show</td><td>UpdateStarted</td></row>
+		<row><td>SetupCompleteError</td><td>RestContText2</td><td>Hide</td><td>NOT UpdateStarted</td></row>
+		<row><td>SetupCompleteError</td><td>RestContText2</td><td>Show</td><td>UpdateStarted</td></row>
+		<row><td>SetupCompleteSuccess</td><td>CheckBoxUpdates</td><td>Show</td><td>ISENABLEDWUSFINISHDIALOG And NOT Installed And ACTION="INSTALL"</td></row>
+		<row><td>SetupCompleteSuccess</td><td>CheckForUpdatesText</td><td>Show</td><td>ISENABLEDWUSFINISHDIALOG And NOT Installed And ACTION="INSTALL"</td></row>
+		<row><td>SetupCompleteSuccess</td><td>CheckLaunchProgram</td><td>Show</td><td>SHOWLAUNCHPROGRAM="-1" And PROGRAMFILETOLAUNCHATEND &lt;&gt; "" And NOT Installed And NOT ISENABLEDWUSFINISHDIALOG</td></row>
+		<row><td>SetupCompleteSuccess</td><td>CheckLaunchReadme</td><td>Show</td><td>SHOWLAUNCHREADME="-1"  And READMEFILETOLAUNCHATEND &lt;&gt; "" And NOT Installed And NOT ISENABLEDWUSFINISHDIALOG</td></row>
+		<row><td>SetupCompleteSuccess</td><td>LaunchProgramText</td><td>Show</td><td>SHOWLAUNCHPROGRAM="-1" And PROGRAMFILETOLAUNCHATEND &lt;&gt; "" And NOT Installed And NOT ISENABLEDWUSFINISHDIALOG</td></row>
+		<row><td>SetupCompleteSuccess</td><td>LaunchReadmeText</td><td>Show</td><td>SHOWLAUNCHREADME="-1"  And READMEFILETOLAUNCHATEND &lt;&gt; "" And NOT Installed And NOT ISENABLEDWUSFINISHDIALOG</td></row>
+		<row><td>SetupCompleteSuccess</td><td>TextLine2</td><td>Show</td><td>ProgressType2="installed" And ((ACTION&lt;&gt;"INSTALL") OR (NOT ISENABLEDWUSFINISHDIALOG) OR (ISENABLEDWUSFINISHDIALOG And Installed))</td></row>
+		<row><td>SetupCompleteSuccess</td><td>TextLine3</td><td>Show</td><td>ProgressType2="uninstalled" And ((ACTION&lt;&gt;"INSTALL") OR (NOT ISENABLEDWUSFINISHDIALOG) OR (ISENABLEDWUSFINISHDIALOG And Installed))</td></row>
+		<row><td>SetupCompleteSuccess</td><td>UpdateTextLine1</td><td>Show</td><td>ISENABLEDWUSFINISHDIALOG And NOT Installed And ACTION="INSTALL"</td></row>
+		<row><td>SetupCompleteSuccess</td><td>UpdateTextLine2</td><td>Show</td><td>ISENABLEDWUSFINISHDIALOG And NOT Installed And ACTION="INSTALL"</td></row>
+		<row><td>SetupCompleteSuccess</td><td>UpdateTextLine3</td><td>Show</td><td>ISENABLEDWUSFINISHDIALOG And NOT Installed And ACTION="INSTALL"</td></row>
+		<row><td>SetupInterrupted</td><td>Back</td><td>Default</td><td>UpdateStarted</td></row>
+		<row><td>SetupInterrupted</td><td>Back</td><td>Disable</td><td>NOT UpdateStarted</td></row>
+		<row><td>SetupInterrupted</td><td>Back</td><td>Enable</td><td>UpdateStarted</td></row>
+		<row><td>SetupInterrupted</td><td>Cancel</td><td>Disable</td><td>NOT UpdateStarted</td></row>
+		<row><td>SetupInterrupted</td><td>Cancel</td><td>Enable</td><td>UpdateStarted</td></row>
+		<row><td>SetupInterrupted</td><td>Finish</td><td>Default</td><td>NOT UpdateStarted</td></row>
+		<row><td>SetupInterrupted</td><td>FinishText1</td><td>Hide</td><td>UpdateStarted</td></row>
+		<row><td>SetupInterrupted</td><td>FinishText1</td><td>Show</td><td>NOT UpdateStarted</td></row>
+		<row><td>SetupInterrupted</td><td>FinishText2</td><td>Hide</td><td>UpdateStarted</td></row>
+		<row><td>SetupInterrupted</td><td>FinishText2</td><td>Show</td><td>NOT UpdateStarted</td></row>
+		<row><td>SetupInterrupted</td><td>RestContText1</td><td>Hide</td><td>NOT UpdateStarted</td></row>
+		<row><td>SetupInterrupted</td><td>RestContText1</td><td>Show</td><td>UpdateStarted</td></row>
+		<row><td>SetupInterrupted</td><td>RestContText2</td><td>Hide</td><td>NOT UpdateStarted</td></row>
+		<row><td>SetupInterrupted</td><td>RestContText2</td><td>Show</td><td>UpdateStarted</td></row>
+		<row><td>SetupProgress</td><td>DlgDesc</td><td>Show</td><td>ProgressType2="installed"</td></row>
+		<row><td>SetupProgress</td><td>DlgDesc2</td><td>Show</td><td>ProgressType2="uninstalled"</td></row>
+		<row><td>SetupProgress</td><td>DlgText</td><td>Show</td><td>ProgressType3="installs"</td></row>
+		<row><td>SetupProgress</td><td>DlgText2</td><td>Show</td><td>ProgressType3="uninstalls"</td></row>
+		<row><td>SetupProgress</td><td>DlgTitle</td><td>Show</td><td>ProgressType1="Installing"</td></row>
+		<row><td>SetupProgress</td><td>DlgTitle2</td><td>Show</td><td>ProgressType1="Uninstalling"</td></row>
+		<row><td>SetupResume</td><td>PreselectedText</td><td>Hide</td><td>RESUME</td></row>
+		<row><td>SetupResume</td><td>PreselectedText</td><td>Show</td><td>NOT RESUME</td></row>
+		<row><td>SetupResume</td><td>ResumeText</td><td>Hide</td><td>NOT RESUME</td></row>
+		<row><td>SetupResume</td><td>ResumeText</td><td>Show</td><td>RESUME</td></row>
+	</table>
+
+	<table name="ControlEvent">
+		<col key="yes" def="s72">Dialog_</col>
+		<col key="yes" def="s50">Control_</col>
+		<col key="yes" def="s50">Event</col>
+		<col key="yes" def="s255">Argument</col>
+		<col key="yes" def="S255">Condition</col>
+		<col def="I2">Ordering</col>
+		<row><td>AdminChangeFolder</td><td>Cancel</td><td>EndDialog</td><td>Return</td><td>1</td><td>2</td></row>
+		<row><td>AdminChangeFolder</td><td>Cancel</td><td>Reset</td><td>0</td><td>1</td><td>1</td></row>
+		<row><td>AdminChangeFolder</td><td>NewFolder</td><td>DirectoryListNew</td><td>0</td><td>1</td><td>0</td></row>
+		<row><td>AdminChangeFolder</td><td>OK</td><td>EndDialog</td><td>Return</td><td>1</td><td>0</td></row>
+		<row><td>AdminChangeFolder</td><td>OK</td><td>SetTargetPath</td><td>TARGETDIR</td><td>1</td><td>1</td></row>
+		<row><td>AdminChangeFolder</td><td>Up</td><td>DirectoryListUp</td><td>0</td><td>1</td><td>0</td></row>
+		<row><td>AdminNetworkLocation</td><td>Back</td><td>NewDialog</td><td>AdminWelcome</td><td>1</td><td>0</td></row>
+		<row><td>AdminNetworkLocation</td><td>Browse</td><td>SpawnDialog</td><td>AdminChangeFolder</td><td>1</td><td>0</td></row>
+		<row><td>AdminNetworkLocation</td><td>Cancel</td><td>SpawnDialog</td><td>CancelSetup</td><td>1</td><td>0</td></row>
+		<row><td>AdminNetworkLocation</td><td>InstallNow</td><td>EndDialog</td><td>Return</td><td>OutOfNoRbDiskSpace &lt;&gt; 1</td><td>3</td></row>
+		<row><td>AdminNetworkLocation</td><td>InstallNow</td><td>NewDialog</td><td>OutOfSpace</td><td>OutOfNoRbDiskSpace = 1</td><td>2</td></row>
+		<row><td>AdminNetworkLocation</td><td>InstallNow</td><td>SetTargetPath</td><td>TARGETDIR</td><td>1</td><td>1</td></row>
+		<row><td>AdminWelcome</td><td>Cancel</td><td>SpawnDialog</td><td>CancelSetup</td><td>1</td><td>0</td></row>
+		<row><td>AdminWelcome</td><td>Next</td><td>NewDialog</td><td>AdminNetworkLocation</td><td>1</td><td>0</td></row>
+		<row><td>CancelSetup</td><td>No</td><td>EndDialog</td><td>Return</td><td>1</td><td>0</td></row>
+		<row><td>CancelSetup</td><td>Yes</td><td>EndDialog</td><td>Exit</td><td>1</td><td>2</td></row>
+		<row><td>CustomSetup</td><td>Back</td><td>NewDialog</td><td>DestinationFolder</td><td>Installed</td><td>0</td></row>
+		<row><td>CustomSetup</td><td>Back</td><td>NewDialog</td><td>SetupType</td><td>NOT Installed</td><td>0</td></row>
+		<row><td>CustomSetup</td><td>Cancel</td><td>SpawnDialog</td><td>CancelSetup</td><td>1</td><td>0</td></row>
+		<row><td>CustomSetup</td><td>ChangeFolder</td><td>SelectionBrowse</td><td>InstallChangeFolder</td><td>1</td><td>0</td></row>
+		<row><td>CustomSetup</td><td>Details</td><td>SelectionBrowse</td><td>DiskSpaceRequirements</td><td>1</td><td>1</td></row>
+		<row><td>CustomSetup</td><td>Help</td><td>SpawnDialog</td><td>CustomSetupTips</td><td>1</td><td>1</td></row>
+		<row><td>CustomSetup</td><td>Next</td><td>NewDialog</td><td>OutOfSpace</td><td>OutOfNoRbDiskSpace = 1</td><td>0</td></row>
+		<row><td>CustomSetup</td><td>Next</td><td>NewDialog</td><td>ReadyToInstall</td><td>OutOfNoRbDiskSpace &lt;&gt; 1</td><td>0</td></row>
+		<row><td>CustomSetup</td><td>Next</td><td>[_IsSetupTypeMin]</td><td>Custom</td><td>1</td><td>0</td></row>
+		<row><td>CustomSetupTips</td><td>OK</td><td>EndDialog</td><td>Return</td><td>1</td><td>1</td></row>
+		<row><td>CustomerInformation</td><td>Back</td><td>NewDialog</td><td>LicenseAgreement</td><td>1</td><td>1</td></row>
+		<row><td>CustomerInformation</td><td>Cancel</td><td>SpawnDialog</td><td>CancelSetup</td><td>1</td><td>0</td></row>
+		<row><td>CustomerInformation</td><td>Next</td><td>EndDialog</td><td>Exit</td><td>(SERIALNUMVALRETRYLIMIT) And (SERIALNUMVALRETRYLIMIT&lt;0) And (SERIALNUMVALRETURN&lt;&gt;SERIALNUMVALSUCCESSRETVAL)</td><td>3</td></row>
+		<row><td>CustomerInformation</td><td>Next</td><td>NewDialog</td><td>SetupType</td><td>(Not SERIALNUMVALRETURN) OR (SERIALNUMVALRETURN=SERIALNUMVALSUCCESSRETVAL)</td><td>4</td></row>
+		<row><td>CustomerInformation</td><td>Next</td><td>[ALLUSERS]</td><td>1</td><td>ApplicationUsers = "AllUsers" And Privileged</td><td>1</td></row>
+		<row><td>CustomerInformation</td><td>Next</td><td>[ALLUSERS]</td><td>{}</td><td>ApplicationUsers = "OnlyCurrentUser" And Privileged</td><td>2</td></row>
+		<row><td>DatabaseFolder</td><td>Back</td><td>NewDialog</td><td>CustomerInformation</td><td>1</td><td>1</td></row>
+		<row><td>DatabaseFolder</td><td>Cancel</td><td>SpawnDialog</td><td>CancelSetup</td><td>1</td><td>1</td></row>
+		<row><td>DatabaseFolder</td><td>ChangeFolder</td><td>SpawnDialog</td><td>InstallChangeFolder</td><td>1</td><td>1</td></row>
+		<row><td>DatabaseFolder</td><td>ChangeFolder</td><td>[_BrowseProperty]</td><td>DATABASEDIR</td><td>1</td><td>2</td></row>
+		<row><td>DatabaseFolder</td><td>Next</td><td>NewDialog</td><td>SetupType</td><td>1</td><td>1</td></row>
+		<row><td>DestinationFolder</td><td>Back</td><td>NewDialog</td><td>LicenseAgreement</td><td>1</td><td>0</td></row>
+		<row><td>DestinationFolder</td><td>Cancel</td><td>SpawnDialog</td><td>CancelSetup</td><td>1</td><td>1</td></row>
+		<row><td>DestinationFolder</td><td>ChangeFolder</td><td>SpawnDialog</td><td>InstallChangeFolder</td><td>1</td><td>1</td></row>
+		<row><td>DestinationFolder</td><td>ChangeFolder</td><td>[_BrowseProperty]</td><td>INSTALLDIR</td><td>1</td><td>2</td></row>
+		<row><td>DestinationFolder</td><td>Next</td><td>NewDialog</td><td>ReadyToInstall</td><td>1</td><td>0</td></row>
+		<row><td>DiskSpaceRequirements</td><td>OK</td><td>EndDialog</td><td>Return</td><td>1</td><td>0</td></row>
+		<row><td>FilesInUse</td><td>Exit</td><td>EndDialog</td><td>Exit</td><td>1</td><td>0</td></row>
+		<row><td>FilesInUse</td><td>Ignore</td><td>EndDialog</td><td>Ignore</td><td>1</td><td>0</td></row>
+		<row><td>FilesInUse</td><td>Retry</td><td>EndDialog</td><td>Retry</td><td>1</td><td>0</td></row>
+		<row><td>InstallChangeFolder</td><td>Cancel</td><td>EndDialog</td><td>Return</td><td>1</td><td>2</td></row>
+		<row><td>InstallChangeFolder</td><td>Cancel</td><td>Reset</td><td>0</td><td>1</td><td>1</td></row>
+		<row><td>InstallChangeFolder</td><td>NewFolder</td><td>DirectoryListNew</td><td>0</td><td>1</td><td>0</td></row>
+		<row><td>InstallChangeFolder</td><td>OK</td><td>EndDialog</td><td>Return</td><td>1</td><td>3</td></row>
+		<row><td>InstallChangeFolder</td><td>OK</td><td>SetTargetPath</td><td>[_BrowseProperty]</td><td>1</td><td>2</td></row>
+		<row><td>InstallChangeFolder</td><td>Up</td><td>DirectoryListUp</td><td>0</td><td>1</td><td>0</td></row>
+		<row><td>InstallWelcome</td><td>Back</td><td>NewDialog</td><td>SplashBitmap</td><td>Display_IsBitmapDlg</td><td>0</td></row>
+		<row><td>InstallWelcome</td><td>Cancel</td><td>SpawnDialog</td><td>CancelSetup</td><td>1</td><td>0</td></row>
+		<row><td>InstallWelcome</td><td>Next</td><td>NewDialog</td><td>LicenseAgreement</td><td>1</td><td>0</td></row>
+		<row><td>LicenseAgreement</td><td>Back</td><td>NewDialog</td><td>InstallWelcome</td><td>1</td><td>0</td></row>
+		<row><td>LicenseAgreement</td><td>Cancel</td><td>SpawnDialog</td><td>CancelSetup</td><td>1</td><td>0</td></row>
+		<row><td>LicenseAgreement</td><td>ISPrintButton</td><td>DoAction</td><td>ISPrint</td><td>1</td><td>0</td></row>
+		<row><td>LicenseAgreement</td><td>Next</td><td>NewDialog</td><td>DestinationFolder</td><td>AgreeToLicense = "Yes"</td><td>0</td></row>
+		<row><td>MaintenanceType</td><td>Back</td><td>NewDialog</td><td>MaintenanceWelcome</td><td>1</td><td>0</td></row>
+		<row><td>MaintenanceType</td><td>Cancel</td><td>SpawnDialog</td><td>CancelSetup</td><td>1</td><td>0</td></row>
+		<row><td>MaintenanceType</td><td>Next</td><td>NewDialog</td><td>CustomSetup</td><td>_IsMaintenance = "Change"</td><td>12</td></row>
+		<row><td>MaintenanceType</td><td>Next</td><td>NewDialog</td><td>ReadyToInstall</td><td>_IsMaintenance = "Reinstall"</td><td>13</td></row>
+		<row><td>MaintenanceType</td><td>Next</td><td>NewDialog</td><td>ReadyToRemove</td><td>_IsMaintenance = "Remove"</td><td>11</td></row>
+		<row><td>MaintenanceType</td><td>Next</td><td>Reinstall</td><td>ALL</td><td>_IsMaintenance = "Reinstall"</td><td>10</td></row>
+		<row><td>MaintenanceType</td><td>Next</td><td>ReinstallMode</td><td>[ReinstallModeText]</td><td>_IsMaintenance = "Reinstall"</td><td>9</td></row>
+		<row><td>MaintenanceType</td><td>Next</td><td>[ProgressType0]</td><td>Modify</td><td>_IsMaintenance = "Change"</td><td>2</td></row>
+		<row><td>MaintenanceType</td><td>Next</td><td>[ProgressType0]</td><td>Repair</td><td>_IsMaintenance = "Reinstall"</td><td>1</td></row>
+		<row><td>MaintenanceType</td><td>Next</td><td>[ProgressType1]</td><td>Modifying</td><td>_IsMaintenance = "Change"</td><td>3</td></row>
+		<row><td>MaintenanceType</td><td>Next</td><td>[ProgressType1]</td><td>Repairing</td><td>_IsMaintenance = "Reinstall"</td><td>4</td></row>
+		<row><td>MaintenanceType</td><td>Next</td><td>[ProgressType2]</td><td>modified</td><td>_IsMaintenance = "Change"</td><td>6</td></row>
+		<row><td>MaintenanceType</td><td>Next</td><td>[ProgressType2]</td><td>repairs</td><td>_IsMaintenance = "Reinstall"</td><td>5</td></row>
+		<row><td>MaintenanceType</td><td>Next</td><td>[ProgressType3]</td><td>modifies</td><td>_IsMaintenance = "Change"</td><td>7</td></row>
+		<row><td>MaintenanceType</td><td>Next</td><td>[ProgressType3]</td><td>repairs</td><td>_IsMaintenance = "Reinstall"</td><td>8</td></row>
+		<row><td>MaintenanceWelcome</td><td>Cancel</td><td>SpawnDialog</td><td>CancelSetup</td><td>1</td><td>0</td></row>
+		<row><td>MaintenanceWelcome</td><td>Next</td><td>NewDialog</td><td>MaintenanceType</td><td>1</td><td>0</td></row>
+		<row><td>OutOfSpace</td><td>Resume</td><td>NewDialog</td><td>AdminNetworkLocation</td><td>ACTION = "ADMIN"</td><td>0</td></row>
+		<row><td>OutOfSpace</td><td>Resume</td><td>NewDialog</td><td>CustomSetup</td><td>ACTION &lt;&gt; "ADMIN"</td><td>0</td></row>
+		<row><td>PatchWelcome</td><td>Back</td><td>NewDialog</td><td>SplashBitmap</td><td>Display_IsBitmapDlg</td><td>0</td></row>
+		<row><td>PatchWelcome</td><td>Cancel</td><td>SpawnDialog</td><td>CancelSetup</td><td>1</td><td>1</td></row>
+		<row><td>PatchWelcome</td><td>Next</td><td>EndDialog</td><td>Return</td><td>1</td><td>3</td></row>
+		<row><td>PatchWelcome</td><td>Next</td><td>Reinstall</td><td>ALL</td><td>PATCH And REINSTALL=""</td><td>1</td></row>
+		<row><td>PatchWelcome</td><td>Next</td><td>ReinstallMode</td><td>omus</td><td>PATCH And REINSTALLMODE=""</td><td>2</td></row>
+		<row><td>ReadyToInstall</td><td>Back</td><td>NewDialog</td><td>CustomSetup</td><td>Installed OR _IsSetupTypeMin = "Custom"</td><td>2</td></row>
+		<row><td>ReadyToInstall</td><td>Back</td><td>NewDialog</td><td>DestinationFolder</td><td>NOT Installed AND _IsSetupTypeMin &lt;&gt; "Custom"</td><td>1</td></row>
+		<row><td>ReadyToInstall</td><td>Back</td><td>NewDialog</td><td>MaintenanceType</td><td>Installed AND _IsMaintenance = "Reinstall"</td><td>3</td></row>
+		<row><td>ReadyToInstall</td><td>Cancel</td><td>SpawnDialog</td><td>CancelSetup</td><td>1</td><td>0</td></row>
+		<row><td>ReadyToInstall</td><td>InstallNow</td><td>EndDialog</td><td>Return</td><td>OutOfNoRbDiskSpace &lt;&gt; 1</td><td>0</td></row>
+		<row><td>ReadyToInstall</td><td>InstallNow</td><td>NewDialog</td><td>OutOfSpace</td><td>OutOfNoRbDiskSpace = 1</td><td>0</td></row>
+		<row><td>ReadyToInstall</td><td>InstallNow</td><td>[ProgressType1]</td><td>Installing</td><td>1</td><td>0</td></row>
+		<row><td>ReadyToInstall</td><td>InstallNow</td><td>[ProgressType2]</td><td>installed</td><td>1</td><td>0</td></row>
+		<row><td>ReadyToInstall</td><td>InstallNow</td><td>[ProgressType3]</td><td>installs</td><td>1</td><td>0</td></row>
+		<row><td>ReadyToRemove</td><td>Back</td><td>NewDialog</td><td>MaintenanceType</td><td>1</td><td>0</td></row>
+		<row><td>ReadyToRemove</td><td>Cancel</td><td>SpawnDialog</td><td>CancelSetup</td><td>1</td><td>0</td></row>
+		<row><td>ReadyToRemove</td><td>RemoveNow</td><td>EndDialog</td><td>Return</td><td>OutOfNoRbDiskSpace &lt;&gt; 1</td><td>2</td></row>
+		<row><td>ReadyToRemove</td><td>RemoveNow</td><td>NewDialog</td><td>OutOfSpace</td><td>OutOfNoRbDiskSpace = 1</td><td>2</td></row>
+		<row><td>ReadyToRemove</td><td>RemoveNow</td><td>Remove</td><td>ALL</td><td>1</td><td>1</td></row>
+		<row><td>ReadyToRemove</td><td>RemoveNow</td><td>[ProgressType1]</td><td>Uninstalling</td><td>1</td><td>0</td></row>
+		<row><td>ReadyToRemove</td><td>RemoveNow</td><td>[ProgressType2]</td><td>uninstalled</td><td>1</td><td>0</td></row>
+		<row><td>ReadyToRemove</td><td>RemoveNow</td><td>[ProgressType3]</td><td>uninstalls</td><td>1</td><td>0</td></row>
+		<row><td>SQLBrowse</td><td>Cancel</td><td>EndDialog</td><td>Return</td><td>1</td><td>1</td></row>
+		<row><td>SQLBrowse</td><td>OK</td><td>EndDialog</td><td>Return</td><td>1</td><td>2</td></row>
+		<row><td>SQLBrowse</td><td>OK</td><td>[IS_SQLSERVER_SERVER]</td><td>[IS_SQLSERVER_LIST]</td><td>1</td><td>1</td></row>
+		<row><td>SQLLogin</td><td>Back</td><td>NewDialog</td><td>CustomerInformation</td><td>1</td><td>1</td></row>
+		<row><td>SQLLogin</td><td>BtnSQLBrowse</td><td>DoAction</td><td>ISSQLServerList</td><td>1</td><td>2</td></row>
+		<row><td>SQLLogin</td><td>BtnSQLBrowse</td><td>SpawnDialog</td><td>SQLBrowse</td><td>1</td><td>3</td></row>
+		<row><td>SQLLogin</td><td>BtnSQLBrowse</td><td>[IS_SQLSERVER_LIST]</td><td>[IS_SQLSERVER_SERVER]</td><td>1</td><td>1</td></row>
+		<row><td>SQLLogin</td><td>Cancel</td><td>SpawnDialog</td><td>CancelSetup</td><td>1</td><td>1</td></row>
+		<row><td>SQLLogin</td><td>Next</td><td>DoAction</td><td>ISSQLServerValidate</td><td>1</td><td>3</td></row>
+		<row><td>SQLLogin</td><td>Next</td><td>NewDialog</td><td>SetupType</td><td>IS_SQLSERVER_STATUS=0</td><td>4</td></row>
+		<row><td>SetupCompleteError</td><td>Back</td><td>EndDialog</td><td>Return</td><td>1</td><td>2</td></row>
+		<row><td>SetupCompleteError</td><td>Back</td><td>[Suspend]</td><td>{}</td><td>1</td><td>1</td></row>
+		<row><td>SetupCompleteError</td><td>Cancel</td><td>EndDialog</td><td>Return</td><td>1</td><td>2</td></row>
+		<row><td>SetupCompleteError</td><td>Cancel</td><td>[Suspend]</td><td>1</td><td>1</td><td>1</td></row>
+		<row><td>SetupCompleteError</td><td>Finish</td><td>EndDialog</td><td>Exit</td><td>1</td><td>2</td></row>
+		<row><td>SetupCompleteSuccess</td><td>OK</td><td>DoAction</td><td>CheckForProductUpdates</td><td>ISCHECKFORPRODUCTUPDATES="1" And ISENABLEDWUSFINISHDIALOG And NOT ISREBOOTREQUIRED And NOT Installed And ACTION="INSTALL"</td><td>4</td></row>
+		<row><td>SetupCompleteSuccess</td><td>OK</td><td>DoAction</td><td>CheckForProductUpdatesOnReboot</td><td>ISCHECKFORPRODUCTUPDATES="1" And ISENABLEDWUSFINISHDIALOG And ISREBOOTREQUIRED And NOT Installed And ACTION="INSTALL"</td><td>5</td></row>
+		<row><td>SetupCompleteSuccess</td><td>OK</td><td>DoAction</td><td>IS_LAUNCH_MY_PROGRAM_PLEASE</td><td>LAUNCHPROGRAM</td><td/></row>
+		<row><td>SetupCompleteSuccess</td><td>OK</td><td>EndDialog</td><td>Exit</td><td>1</td><td>2</td></row>
+		<row><td>SetupError</td><td>A</td><td>EndDialog</td><td>ErrorAbort</td><td>1</td><td>0</td></row>
+		<row><td>SetupError</td><td>C</td><td>EndDialog</td><td>ErrorCancel</td><td>1</td><td>0</td></row>
+		<row><td>SetupError</td><td>I</td><td>EndDialog</td><td>ErrorIgnore</td><td>1</td><td>0</td></row>
+		<row><td>SetupError</td><td>N</td><td>EndDialog</td><td>ErrorNo</td><td>1</td><td>0</td></row>
+		<row><td>SetupError</td><td>O</td><td>EndDialog</td><td>ErrorOk</td><td>1</td><td>0</td></row>
+		<row><td>SetupError</td><td>R</td><td>EndDialog</td><td>ErrorRetry</td><td>1</td><td>0</td></row>
+		<row><td>SetupError</td><td>Y</td><td>EndDialog</td><td>ErrorYes</td><td>1</td><td>0</td></row>
+		<row><td>SetupInitialization</td><td>Cancel</td><td>SpawnDialog</td><td>CancelSetup</td><td>1</td><td>0</td></row>
+		<row><td>SetupInterrupted</td><td>Back</td><td>EndDialog</td><td>Exit</td><td>1</td><td>2</td></row>
+		<row><td>SetupInterrupted</td><td>Back</td><td>[Suspend]</td><td>{}</td><td>1</td><td>1</td></row>
+		<row><td>SetupInterrupted</td><td>Cancel</td><td>EndDialog</td><td>Exit</td><td>1</td><td>2</td></row>
+		<row><td>SetupInterrupted</td><td>Cancel</td><td>[Suspend]</td><td>1</td><td>1</td><td>1</td></row>
+		<row><td>SetupInterrupted</td><td>Finish</td><td>EndDialog</td><td>Exit</td><td>1</td><td>2</td></row>
+		<row><td>SetupProgress</td><td>Cancel</td><td>SpawnDialog</td><td>CancelSetup</td><td>1</td><td>0</td></row>
+		<row><td>SetupResume</td><td>Cancel</td><td>SpawnDialog</td><td>CancelSetup</td><td>1</td><td>0</td></row>
+		<row><td>SetupResume</td><td>Next</td><td>EndDialog</td><td>Return</td><td>OutOfNoRbDiskSpace &lt;&gt; 1</td><td>0</td></row>
+		<row><td>SetupResume</td><td>Next</td><td>NewDialog</td><td>OutOfSpace</td><td>OutOfNoRbDiskSpace = 1</td><td>0</td></row>
+		<row><td>SetupType</td><td>Back</td><td>NewDialog</td><td>DestinationFolder</td><td>1</td><td>0</td></row>
+		<row><td>SetupType</td><td>Cancel</td><td>SpawnDialog</td><td>CancelSetup</td><td>1</td><td>0</td></row>
+		<row><td>SetupType</td><td>Next</td><td>AddLocal</td><td>ALL</td><td>_IsSetupTypeMin = "Typical"</td><td>0</td></row>
+		<row><td>SetupType</td><td>Next</td><td>NewDialog</td><td>CustomSetup</td><td>_IsSetupTypeMin = "Custom"</td><td>0</td></row>
+		<row><td>SetupType</td><td>Next</td><td>NewDialog</td><td>ReadyToInstall</td><td>_IsSetupTypeMin &lt;&gt; "Custom"</td><td>0</td></row>
+		<row><td>SplashBitmap</td><td>Cancel</td><td>SpawnDialog</td><td>CancelSetup</td><td>1</td><td>0</td></row>
+		<row><td>SplashBitmap</td><td>Next</td><td>NewDialog</td><td>InstallWelcome</td><td>1</td><td>0</td></row>
+	</table>
+
+	<table name="CreateFolder">
+		<col key="yes" def="s72">Directory_</col>
+		<col key="yes" def="s72">Component_</col>
+		<row><td>BIN</td><td>AllOtherFiles10</td></row>
+		<row><td>BIN</td><td>AllOtherFiles11</td></row>
+		<row><td>BIN</td><td>AllOtherFiles8</td></row>
+		<row><td>CONF</td><td>AllOtherFiles5</td></row>
+		<row><td>CONF</td><td>AllOtherFiles9</td></row>
+		<row><td>INSTALLDIR</td><td>AllOtherFiles4</td></row>
+		<row><td>INSTALLDIR</td><td>AllOtherFiles5</td></row>
+		<row><td>INSTALLDIR</td><td>AllOtherFiles6</td></row>
+		<row><td>INSTALLDIR</td><td>AllOtherFiles7</td></row>
+		<row><td>INSTALLDIR</td><td>ISRegistryComponent</td></row>
+		<row><td>INSTALLDIR</td><td>ISRegistryComponent1</td></row>
+		<row><td>INSTALLDIR</td><td>VirtualDirComponent</td></row>
+		<row><td>LOG</td><td>AllOtherFiles12</td></row>
+	</table>
+
+	<table name="CustomAction">
+		<col key="yes" def="s72">Action</col>
+		<col def="i2">Type</col>
+		<col def="S64">Source</col>
+		<col def="S0">Target</col>
+		<col def="S255">ISComments</col>
+		<row><td>CheckForProductUpdates</td><td>226</td><td>ISUpdateServiceFolder</td><td>[ISUpdateServiceFolder]agent.exe "/au[ProductCode] /EndOfInstall"</td><td>Checks for product updates</td></row>
+		<row><td>CheckForProductUpdatesOnReboot</td><td>226</td><td>ISUpdateServiceFolder</td><td>[ISUpdateServiceFolder]agent.exe "/au[ProductCode] /EndOfInstall /Reboot"</td><td>Checks for product updates on reboot</td></row>
+		<row><td>ISPrint</td><td>1</td><td>SetAllUsers.dll</td><td>PrintScrollableText</td><td>Prints the contents of a ScrollableText control on a dialog.</td></row>
+		<row><td>ISSelfRegisterCosting</td><td>1</td><td>ISSELFREG.DLL</td><td>ISSelfRegisterCosting</td><td/></row>
+		<row><td>ISSelfRegisterFiles</td><td>1025</td><td>ISSELFREG.DLL</td><td>ISSelfRegisterFiles</td><td/></row>
+		<row><td>ISSelfRegisterFinalize</td><td>1</td><td>ISSELFREG.DLL</td><td>ISSelfRegisterFinalize</td><td/></row>
+		<row><td>ISUnSelfRegisterFiles</td><td>1025</td><td>ISSELFREG.DLL</td><td>ISUnSelfRegisterFiles</td><td/></row>
+		<row><td>InstallFilter</td><td>70</td><td>NewBinary19</td><td/><td/></row>
+		<row><td>SetARPINSTALLLOCATION</td><td>51</td><td>ARPINSTALLLOCATION</td><td>[INSTALLDIR]</td><td/></row>
+		<row><td>SetAllUsersProfileNT</td><td>51</td><td>ALLUSERSPROFILE</td><td>[%SystemRoot]\Profiles\All Users</td><td/></row>
+		<row><td>caCreateVRoots</td><td>1025</td><td>binIISHelper</td><td>CreateIISVRoots</td><td/></row>
+		<row><td>caExtractIISSuppFiles</td><td>1</td><td>binIISHelper</td><td>ExtractIISTables</td><td/></row>
+		<row><td>caIISCleanup</td><td>1</td><td>binIISHelper</td><td>CleanupVRoots</td><td/></row>
+		<row><td>caRemoveVRoots</td><td>1025</td><td>binIISHelper</td><td>RemoveIISVRoots</td><td/></row>
+		<row><td>caRlbackVRoots</td><td>1281</td><td>binIISHelper</td><td>RlBackRemoveIISVRoots</td><td/></row>
+		<row><td>setAllUsersProfile2K</td><td>51</td><td>ALLUSERSPROFILE</td><td>[%ALLUSERSPROFILE]</td><td/></row>
+		<row><td>setUserProfileNT</td><td>51</td><td>USERPROFILE</td><td>[%USERPROFILE]</td><td/></row>
+	</table>
+
+	<table name="Dialog">
+		<col key="yes" def="s72">Dialog</col>
+		<col def="i2">HCentering</col>
+		<col def="i2">VCentering</col>
+		<col def="i2">Width</col>
+		<col def="i2">Height</col>
+		<col def="I4">Attributes</col>
+		<col def="L128">Title</col>
+		<col def="s50">Control_First</col>
+		<col def="S50">Control_Default</col>
+		<col def="S50">Control_Cancel</col>
+		<col def="S255">ISComments</col>
+		<col def="S72">TextStyle_</col>
+		<col def="I4">ISWindowStyle</col>
+		<col def="I4">ISResourceId</col>
+		<row><td>AdminChangeFolder</td><td>50</td><td>50</td><td>374</td><td>266</td><td>3</td><td>##IDS_PRODUCTNAME_INSTALLSHIELD##</td><td>Tail</td><td>OK</td><td>Cancel</td><td>Install Point Browse</td><td/><td>0</td><td/></row>
+		<row><td>AdminNetworkLocation</td><td>50</td><td>50</td><td>374</td><td>266</td><td>3</td><td>##IDS_PRODUCTNAME_INSTALLSHIELD##</td><td>InstallNow</td><td>InstallNow</td><td>Cancel</td><td>Network Location</td><td/><td>0</td><td/></row>
+		<row><td>AdminWelcome</td><td>50</td><td>50</td><td>374</td><td>266</td><td>3</td><td>##IDS_PRODUCTNAME_INSTALLSHIELD##</td><td>Next</td><td>Next</td><td>Cancel</td><td>Administration Welcome</td><td/><td>0</td><td/></row>
+		<row><td>CancelSetup</td><td>50</td><td>50</td><td>260</td><td>85</td><td>3</td><td>##IDS_PRODUCTNAME_INSTALLSHIELD##</td><td>No</td><td>No</td><td>No</td><td>Cancel</td><td/><td>0</td><td/></row>
+		<row><td>CustomSetup</td><td>50</td><td>50</td><td>374</td><td>266</td><td>35</td><td>##IDS_PRODUCTNAME_INSTALLSHIELD##</td><td>Tree</td><td>Next</td><td>Cancel</td><td>Custom Selection</td><td/><td>0</td><td/></row>
+		<row><td>CustomSetupTips</td><td>50</td><td>50</td><td>374</td><td>266</td><td>3</td><td>##IDS_PRODUCTNAME_INSTALLSHIELD##</td><td>OK</td><td>OK</td><td>OK</td><td>Custom Setup Tips</td><td/><td>0</td><td/></row>
+		<row><td>CustomerInformation</td><td>50</td><td>50</td><td>374</td><td>266</td><td>3</td><td>##IDS_PRODUCTNAME_INSTALLSHIELD##</td><td>NameEdit</td><td>Next</td><td>Cancel</td><td>Identification</td><td/><td>0</td><td/></row>
+		<row><td>DatabaseFolder</td><td>50</td><td>50</td><td>374</td><td>266</td><td>3</td><td>##IDS_PRODUCTNAME_INSTALLSHIELD##</td><td>Next</td><td>Next</td><td>Cancel</td><td>Database Folder</td><td/><td>0</td><td/></row>
+		<row><td>DestinationFolder</td><td>50</td><td>50</td><td>374</td><td>266</td><td>3</td><td>##IDS_PRODUCTNAME_INSTALLSHIELD##</td><td>Next</td><td>Next</td><td>Cancel</td><td>Destination Folder</td><td/><td>0</td><td/></row>
+		<row><td>DiskSpaceRequirements</td><td>50</td><td>50</td><td>374</td><td>266</td><td>3</td><td>##IDS_PRODUCTNAME_INSTALLSHIELD##</td><td>OK</td><td>OK</td><td>OK</td><td>Feature Details</td><td/><td>0</td><td/></row>
+		<row><td>FilesInUse</td><td>50</td><td>50</td><td>374</td><td>266</td><td>19</td><td>##IDS_PRODUCTNAME_INSTALLSHIELD##</td><td>Retry</td><td>Retry</td><td>Exit</td><td>Files in Use</td><td/><td>0</td><td/></row>
+		<row><td>InstallChangeFolder</td><td>50</td><td>50</td><td>374</td><td>266</td><td>3</td><td>##IDS_PRODUCTNAME_INSTALLSHIELD##</td><td>Tail</td><td>OK</td><td>Cancel</td><td>Browse</td><td/><td>0</td><td/></row>
+		<row><td>InstallWelcome</td><td>50</td><td>50</td><td>374</td><td>266</td><td>3</td><td>##IDS_PRODUCTNAME_INSTALLSHIELD##</td><td>Next</td><td>Next</td><td>Cancel</td><td>Welcome Panel</td><td/><td>0</td><td/></row>
+		<row><td>LicenseAgreement</td><td>50</td><td>50</td><td>374</td><td>266</td><td>2</td><td>##IDS_PRODUCTNAME_INSTALLSHIELD##</td><td>Agree</td><td>Next</td><td>Cancel</td><td>License Agreement</td><td/><td>0</td><td/></row>
+		<row><td>MaintenanceType</td><td>50</td><td>50</td><td>374</td><td>266</td><td>3</td><td>##IDS_PRODUCTNAME_INSTALLSHIELD##</td><td>RadioGroup</td><td>Next</td><td>Cancel</td><td>Change, Reinstall, Remove</td><td/><td>0</td><td/></row>
+		<row><td>MaintenanceWelcome</td><td>50</td><td>50</td><td>374</td><td>266</td><td>3</td><td>##IDS_PRODUCTNAME_INSTALLSHIELD##</td><td>Next</td><td>Next</td><td>Cancel</td><td>Maintenance Welcome</td><td/><td>0</td><td/></row>
+		<row><td>OutOfSpace</td><td>50</td><td>50</td><td>374</td><td>266</td><td>3</td><td>##IDS_PRODUCTNAME_INSTALLSHIELD##</td><td>Resume</td><td>Resume</td><td>Resume</td><td>Out Of Disk Space</td><td/><td>0</td><td/></row>
+		<row><td>PatchWelcome</td><td>50</td><td>50</td><td>374</td><td>266</td><td>3</td><td>##IDS__IsPatchDlg_PatchWizard##</td><td>Next</td><td>Next</td><td>Cancel</td><td>Patch Panel</td><td/><td>0</td><td/></row>
+		<row><td>ReadyToInstall</td><td>50</td><td>50</td><td>374</td><td>266</td><td>35</td><td>##IDS_PRODUCTNAME_INSTALLSHIELD##</td><td>InstallNow</td><td>InstallNow</td><td>Cancel</td><td>Ready to Install</td><td/><td>0</td><td/></row>
+		<row><td>ReadyToRemove</td><td>50</td><td>50</td><td>374</td><td>266</td><td>3</td><td>##IDS_PRODUCTNAME_INSTALLSHIELD##</td><td>RemoveNow</td><td>RemoveNow</td><td>Cancel</td><td>Verify Remove</td><td/><td>0</td><td/></row>
+		<row><td>SQLBrowse</td><td>50</td><td>50</td><td>272</td><td>265</td><td>3</td><td>##IDS_PRODUCTNAME_INSTALLSHIELD##</td><td>lstSQLServer</td><td>OK</td><td>Cancel</td><td>SQL Server List Browse</td><td/><td>0</td><td/></row>
+		<row><td>SQLLogin</td><td>50</td><td>50</td><td>374</td><td>266</td><td>3</td><td>##IDS_PRODUCTNAME_INSTALLSHIELD##</td><td>cboServers</td><td>Next</td><td>Cancel</td><td>SQL Server Login</td><td/><td>0</td><td/></row>
+		<row><td>SetupCompleteError</td><td>50</td><td>50</td><td>374</td><td>266</td><td>3</td><td>##IDS_PRODUCTNAME_INSTALLSHIELD##</td><td>Finish</td><td>Finish</td><td>Finish</td><td>Fatal Error</td><td/><td>0</td><td/></row>
+		<row><td>SetupCompleteSuccess</td><td>50</td><td>50</td><td>374</td><td>266</td><td>3</td><td>##IDS_PRODUCTNAME_INSTALLSHIELD##</td><td>OK</td><td>OK</td><td>OK</td><td>Exit</td><td/><td>0</td><td/></row>
+		<row><td>SetupError</td><td>50</td><td>50</td><td>270</td><td>110</td><td>65543</td><td>##IDS__IsErrorDlg_InstallerInfo##</td><td>ErrorText</td><td>O</td><td>C</td><td>Error</td><td/><td>0</td><td/></row>
+		<row><td>SetupInitialization</td><td>50</td><td>50</td><td>374</td><td>266</td><td>5</td><td>##IDS_PRODUCTNAME_INSTALLSHIELD##</td><td>Cancel</td><td>Cancel</td><td>Cancel</td><td>Setup Initialization</td><td/><td>0</td><td/></row>
+		<row><td>SetupInterrupted</td><td>50</td><td>50</td><td>374</td><td>266</td><td>3</td><td>##IDS_PRODUCTNAME_INSTALLSHIELD##</td><td>Finish</td><td>Finish</td><td>Finish</td><td>User Exit</td><td/><td>0</td><td/></row>
+		<row><td>SetupProgress</td><td>50</td><td>50</td><td>374</td><td>266</td><td>5</td><td>##IDS_PRODUCTNAME_INSTALLSHIELD##</td><td>Cancel</td><td>Cancel</td><td>Cancel</td><td>Progress</td><td/><td>0</td><td/></row>
+		<row><td>SetupResume</td><td>50</td><td>50</td><td>374</td><td>266</td><td>3</td><td>##IDS_PRODUCTNAME_INSTALLSHIELD##</td><td>Next</td><td>Next</td><td>Cancel</td><td>Resume</td><td/><td>0</td><td/></row>
+		<row><td>SetupType</td><td>50</td><td>50</td><td>374</td><td>266</td><td>3</td><td>##IDS_PRODUCTNAME_INSTALLSHIELD##</td><td>RadioGroup</td><td>Next</td><td>Cancel</td><td>Setup Type</td><td/><td>0</td><td/></row>
+		<row><td>SplashBitmap</td><td>50</td><td>50</td><td>374</td><td>266</td><td>3</td><td>##IDS_PRODUCTNAME_INSTALLSHIELD##</td><td>Next</td><td>Next</td><td>Cancel</td><td>Welcome Bitmap</td><td/><td>0</td><td/></row>
+	</table>
+
+	<table name="Directory">
+		<col key="yes" def="s72">Directory</col>
+		<col def="S72">Directory_Parent</col>
+		<col def="l255">DefaultDir</col>
+		<col def="S255">ISDescription</col>
+		<col def="I4">ISAttributes</col>
+		<col def="S255">ISFolderName</col>
+		<row><td>ALLUSERSPROFILE</td><td>TARGETDIR</td><td>.:ALLUSE~1|All Users</td><td/><td>0</td><td/></row>
+		<row><td>APACHE_SOFTWARE_FOUNDATION</td><td>ProgramFilesFolder</td><td>APACHE~1|Apache Software Foundation</td><td/><td>0</td><td/></row>
+		<row><td>AdminToolsFolder</td><td>TARGETDIR</td><td>.:Admint~1|AdminTools</td><td/><td>0</td><td/></row>
+		<row><td>AppDataFolder</td><td>TARGETDIR</td><td>.:APPLIC~1|Application Data</td><td/><td>0</td><td/></row>
+		<row><td>BIN</td><td>INSTALLDIR</td><td>bin</td><td/><td>0</td><td/></row>
+		<row><td>CONF</td><td>INSTALLDIR</td><td>conf</td><td/><td>0</td><td/></row>
+		<row><td>CommonAppDataFolder</td><td>TARGETDIR</td><td>.:Common~1|CommonAppData</td><td/><td>0</td><td/></row>
+		<row><td>CommonFiles64Folder</td><td>TARGETDIR</td><td>.:Common64</td><td/><td>0</td><td/></row>
+		<row><td>CommonFilesFolder</td><td>TARGETDIR</td><td>.:Common</td><td/><td>0</td><td/></row>
+		<row><td>DATABASEDIR</td><td>ISYourDataBaseDir</td><td>.</td><td/><td>0</td><td/></row>
+		<row><td>DesktopFolder</td><td>TARGETDIR</td><td>.:Desktop</td><td/><td>3</td><td/></row>
+		<row><td>FavoritesFolder</td><td>TARGETDIR</td><td>.:FAVORI~1|Favorites</td><td/><td>0</td><td/></row>
+		<row><td>FontsFolder</td><td>TARGETDIR</td><td>.:Fonts</td><td/><td>0</td><td/></row>
+		<row><td>GlobalAssemblyCache</td><td>TARGETDIR</td><td>.:Global~1|GlobalAssemblyCache</td><td/><td>0</td><td/></row>
+		<row><td>IISROOTFOLDER</td><td>TARGETDIR</td><td>.:IISRoo~1|IISRootFolder</td><td/><td>0</td><td/></row>
+		<row><td>INSTALLDIR</td><td>JAKARTA_ISAPI_REDIRECTOR</td><td>.</td><td/><td>0</td><td/></row>
+		<row><td>ISCommonFilesFolder</td><td>CommonFilesFolder</td><td>Instal~1|InstallShield</td><td/><td>0</td><td/></row>
+		<row><td>ISMyCompanyDir</td><td>ProgramFilesFolder</td><td>MYCOMP~1|My Company Name</td><td/><td>0</td><td/></row>
+		<row><td>ISMyProductDir</td><td>ISMyCompanyDir</td><td>MYPROD~1|My Product Name</td><td/><td>0</td><td/></row>
+		<row><td>ISUpdateServiceFolder</td><td>ISCommonFilesFolder</td><td>UPDATE~1|UpdateService</td><td/><td>0</td><td/></row>
+		<row><td>ISYourDataBaseDir</td><td>INSTALLDIR</td><td>Database</td><td/><td>0</td><td/></row>
+		<row><td>JAKARTA_ISAPI_REDIRECTOR</td><td>APACHE_SOFTWARE_FOUNDATION</td><td>JAKART~1|Tomcat Isapi Redirector</td><td/><td>0</td><td/></row>
+		<row><td>JAKARTA_ISAPU_REDIRECTOR</td><td>APACHE_SOFTWARE_FOUNDATION</td><td>JAKART~1|Jakarta Isapu Redirector</td><td/><td>0</td><td/></row>
+		<row><td>JBOSS_EUROPE_SARL</td><td>ProgramFilesFolder</td><td>JBOSSE~1|JBoss Europe SaRL</td><td/><td>0</td><td/></row>
+		<row><td>LOG</td><td>INSTALLDIR</td><td>log</td><td/><td>0</td><td/></row>
+		<row><td>LocalAppDataFolder</td><td>TARGETDIR</td><td>.:LocalA~1|LocalAppData</td><td/><td>0</td><td/></row>
+		<row><td>MY_PRODUCT_NAME</td><td>JBOSS_EUROPE_SARL</td><td>MYPROD~1|My Product Name</td><td/><td>0</td><td/></row>
+		<row><td>MY_PRODUCT_NAME1</td><td>APACHE_SOFTWARE_FOUNDATION</td><td>MYPROD~1|My Product Name</td><td/><td>0</td><td/></row>
+		<row><td>MyPicturesFolder</td><td>TARGETDIR</td><td>.:MyPict~1|MyPictures</td><td/><td>0</td><td/></row>
+		<row><td>PersonalFolder</td><td>TARGETDIR</td><td>.:Personal</td><td/><td>0</td><td/></row>
+		<row><td>PrimaryVolumePath</td><td>TARGETDIR</td><td>.:Primar~1|PrimaryVolumePath</td><td/><td>0</td><td/></row>
+		<row><td>ProgramFiles64Folder</td><td>TARGETDIR</td><td>.:Prog64~1|Program Files 64</td><td/><td>0</td><td/></row>
+		<row><td>ProgramFilesFolder</td><td>TARGETDIR</td><td>.:PROGRA~1|program files</td><td/><td>0</td><td/></row>
+		<row><td>ProgramMenuFolder</td><td>TARGETDIR</td><td>.:Programs</td><td/><td>3</td><td/></row>
+		<row><td>SendToFolder</td><td>TARGETDIR</td><td>.:SendTo</td><td/><td>3</td><td/></row>
+		<row><td>StartMenuFolder</td><td>TARGETDIR</td><td>.:STARTM~1|Start Menu</td><td/><td>3</td><td/></row>
+		<row><td>StartupFolder</td><td>TARGETDIR</td><td>.:StartUp</td><td/><td>3</td><td/></row>
+		<row><td>System16Folder</td><td>TARGETDIR</td><td>.:System</td><td/><td>0</td><td/></row>
+		<row><td>System64Folder</td><td>TARGETDIR</td><td>.:System64</td><td/><td>0</td><td/></row>
+		<row><td>SystemFolder</td><td>TARGETDIR</td><td>.:System32</td><td/><td>0</td><td/></row>
+		<row><td>TARGETDIR</td><td/><td>SourceDir</td><td/><td>0</td><td/></row>
+		<row><td>TempFolder</td><td>TARGETDIR</td><td>.:Temp</td><td/><td>0</td><td/></row>
+		<row><td>TemplateFolder</td><td>TARGETDIR</td><td>.:ShellNew</td><td/><td>0</td><td/></row>
+		<row><td>USERPROFILE</td><td>TARGETDIR</td><td>.:USERPR~1|UserProfile</td><td/><td>0</td><td/></row>
+		<row><td>WindowsFolder</td><td>TARGETDIR</td><td>.:Windows</td><td/><td>0</td><td/></row>
+		<row><td>WindowsVolume</td><td>TARGETDIR</td><td>.:WinRoot</td><td/><td>0</td><td/></row>
+	</table>
+
+	<table name="DrLocator">
+		<col key="yes" def="s72">Signature_</col>
+		<col key="yes" def="S72">Parent</col>
+		<col key="yes" def="S255">Path</col>
+		<col def="I2">Depth</col>
+	</table>
+
+	<table name="DuplicateFile">
+		<col key="yes" def="s72">FileKey</col>
+		<col def="s72">Component_</col>
+		<col def="s72">File_</col>
+		<col def="L255">DestName</col>
+		<col def="S72">DestFolder</col>
+	</table>
+
+	<table name="Environment">
+		<col key="yes" def="s72">Environment</col>
+		<col def="l255">Name</col>
+		<col def="L255">Value</col>
+		<col def="s72">Component_</col>
+	</table>
+
+	<table name="Error">
+		<col key="yes" def="i2">Error</col>
+		<col def="L255">Message</col>
+		<row><td>0</td><td>##IDS_ERROR_0##</td></row>
+		<row><td>1</td><td>##IDS_ERROR_1##</td></row>
+		<row><td>10</td><td>##IDS_ERROR_8##</td></row>
+		<row><td>11</td><td>##IDS_ERROR_9##</td></row>
+		<row><td>1101</td><td>##IDS_ERROR_22##</td></row>
+		<row><td>12</td><td>##IDS_ERROR_10##</td></row>
+		<row><td>13</td><td>##IDS_ERROR_11##</td></row>
+		<row><td>1301</td><td>##IDS_ERROR_23##</td></row>
+		<row><td>1302</td><td>##IDS_ERROR_24##</td></row>
+		<row><td>1303</td><td>##IDS_ERROR_25##</td></row>
+		<row><td>1304</td><td>##IDS_ERROR_26##</td></row>
+		<row><td>1305</td><td>##IDS_ERROR_27##</td></row>
+		<row><td>1306</td><td>##IDS_ERROR_28##</td></row>
+		<row><td>1307</td><td>##IDS_ERROR_29##</td></row>
+		<row><td>1308</td><td>##IDS_ERROR_30##</td></row>
+		<row><td>1309</td><td>##IDS_ERROR_31##</td></row>
+		<row><td>1310</td><td>##IDS_ERROR_32##</td></row>
+		<row><td>1311</td><td>##IDS_ERROR_33##</td></row>
+		<row><td>1312</td><td>##IDS_ERROR_34##</td></row>
+		<row><td>1313</td><td>##IDS_ERROR_35##</td></row>
+		<row><td>1314</td><td>##IDS_ERROR_36##</td></row>
+		<row><td>1315</td><td>##IDS_ERROR_37##</td></row>
+		<row><td>1316</td><td>##IDS_ERROR_38##</td></row>
+		<row><td>1317</td><td>##IDS_ERROR_39##</td></row>
+		<row><td>1318</td><td>##IDS_ERROR_40##</td></row>
+		<row><td>1319</td><td>##IDS_ERROR_41##</td></row>
+		<row><td>1320</td><td>##IDS_ERROR_42##</td></row>
+		<row><td>1321</td><td>##IDS_ERROR_43##</td></row>
+		<row><td>1322</td><td>##IDS_ERROR_44##</td></row>
+		<row><td>1323</td><td>##IDS_ERROR_45##</td></row>
+		<row><td>1324</td><td>##IDS_ERROR_46##</td></row>
+		<row><td>1325</td><td>##IDS_ERROR_47##</td></row>
+		<row><td>1326</td><td>##IDS_ERROR_48##</td></row>
+		<row><td>1327</td><td>##IDS_ERROR_49##</td></row>
+		<row><td>1328</td><td>##IDS_ERROR_122##</td></row>
+		<row><td>14</td><td>##IDS_ERROR_12##</td></row>
+		<row><td>1401</td><td>##IDS_ERROR_50##</td></row>
+		<row><td>1402</td><td>##IDS_ERROR_51##</td></row>
+		<row><td>1403</td><td>##IDS_ERROR_52##</td></row>
+		<row><td>1404</td><td>##IDS_ERROR_53##</td></row>
+		<row><td>1405</td><td>##IDS_ERROR_54##</td></row>
+		<row><td>1406</td><td>##IDS_ERROR_55##</td></row>
+		<row><td>1407</td><td>##IDS_ERROR_56##</td></row>
+		<row><td>1408</td><td>##IDS_ERROR_57##</td></row>
+		<row><td>1409</td><td>##IDS_ERROR_58##</td></row>
+		<row><td>1410</td><td>##IDS_ERROR_59##</td></row>
+		<row><td>15</td><td>##IDS_ERROR_13##</td></row>
+		<row><td>1500</td><td>##IDS_ERROR_60##</td></row>
+		<row><td>1501</td><td>##IDS_ERROR_61##</td></row>
+		<row><td>1502</td><td>##IDS_ERROR_62##</td></row>
+		<row><td>1503</td><td>##IDS_ERROR_63##</td></row>
+		<row><td>16</td><td>##IDS_ERROR_14##</td></row>
+		<row><td>1601</td><td>##IDS_ERROR_64##</td></row>
+		<row><td>1602</td><td>##IDS_ERROR_65##</td></row>
+		<row><td>1603</td><td>##IDS_ERROR_66##</td></row>
+		<row><td>1604</td><td>##IDS_ERROR_67##</td></row>
+		<row><td>1605</td><td>##IDS_ERROR_68##</td></row>
+		<row><td>1606</td><td>##IDS_ERROR_69##</td></row>
+		<row><td>1607</td><td>##IDS_ERROR_70##</td></row>
+		<row><td>1608</td><td>##IDS_ERROR_71##</td></row>
+		<row><td>17</td><td>##IDS_ERROR_15##</td></row>
+		<row><td>1701</td><td>##IDS_ERROR_72##</td></row>
+		<row><td>1702</td><td>##IDS_ERROR_73##</td></row>
+		<row><td>1703</td><td>##IDS_ERROR_74##</td></row>
+		<row><td>1704</td><td>##IDS_ERROR_75##</td></row>
+		<row><td>1705</td><td>##IDS_ERROR_76##</td></row>
+		<row><td>1706</td><td>##IDS_ERROR_77##</td></row>
+		<row><td>1707</td><td>##IDS_ERROR_78##</td></row>
+		<row><td>1708</td><td>##IDS_ERROR_79##</td></row>
+		<row><td>1709</td><td>##IDS_ERROR_80##</td></row>
+		<row><td>1710</td><td>##IDS_ERROR_81##</td></row>
+		<row><td>1711</td><td>##IDS_ERROR_82##</td></row>
+		<row><td>1712</td><td>##IDS_ERROR_83##</td></row>
+		<row><td>1713</td><td>##IDS_ERROR_123##</td></row>
+		<row><td>1714</td><td>##IDS_ERROR_124##</td></row>
+		<row><td>18</td><td>##IDS_ERROR_16##</td></row>
+		<row><td>1801</td><td>##IDS_ERROR_84##</td></row>
+		<row><td>1802</td><td>##IDS_ERROR_85##</td></row>
+		<row><td>1803</td><td>##IDS_ERROR_86##</td></row>
+		<row><td>1804</td><td>##IDS_ERROR_87##</td></row>
+		<row><td>1805</td><td>##IDS_ERROR_88##</td></row>
+		<row><td>1806</td><td>##IDS_ERROR_89##</td></row>
+		<row><td>1807</td><td>##IDS_ERROR_90##</td></row>
+		<row><td>19</td><td>##IDS_ERROR_17##</td></row>
+		<row><td>1901</td><td>##IDS_ERROR_91##</td></row>
+		<row><td>1902</td><td>##IDS_ERROR_92##</td></row>
+		<row><td>1903</td><td>##IDS_ERROR_93##</td></row>
+		<row><td>1904</td><td>##IDS_ERROR_94##</td></row>
+		<row><td>1905</td><td>##IDS_ERROR_95##</td></row>
+		<row><td>1906</td><td>##IDS_ERROR_96##</td></row>
+		<row><td>1907</td><td>##IDS_ERROR_97##</td></row>
+		<row><td>1908</td><td>##IDS_ERROR_98##</td></row>
+		<row><td>1909</td><td>##IDS_ERROR_99##</td></row>
+		<row><td>1910</td><td>##IDS_ERROR_100##</td></row>
+		<row><td>1911</td><td>##IDS_ERROR_101##</td></row>
+		<row><td>1912</td><td>##IDS_ERROR_102##</td></row>
+		<row><td>1913</td><td>##IDS_ERROR_103##</td></row>
+		<row><td>1914</td><td>##IDS_ERROR_104##</td></row>
+		<row><td>1915</td><td>##IDS_ERROR_105##</td></row>
+		<row><td>1916</td><td>##IDS_ERROR_106##</td></row>
+		<row><td>1917</td><td>##IDS_ERROR_107##</td></row>
+		<row><td>1918</td><td>##IDS_ERROR_108##</td></row>
+		<row><td>1919</td><td>##IDS_ERROR_109##</td></row>
+		<row><td>1920</td><td>##IDS_ERROR_110##</td></row>
+		<row><td>1921</td><td>##IDS_ERROR_111##</td></row>
+		<row><td>1922</td><td>##IDS_ERROR_112##</td></row>
+		<row><td>1923</td><td>##IDS_ERROR_113##</td></row>
+		<row><td>1924</td><td>##IDS_ERROR_114##</td></row>
+		<row><td>1925</td><td>##IDS_ERROR_115##</td></row>
+		<row><td>1926</td><td>##IDS_ERROR_116##</td></row>
+		<row><td>1927</td><td>##IDS_ERROR_117##</td></row>
+		<row><td>1928</td><td>##IDS_ERROR_118##</td></row>
+		<row><td>1929</td><td>##IDS_ERROR_119##</td></row>
+		<row><td>1930</td><td>##IDS_ERROR_125##</td></row>
+		<row><td>1931</td><td>##IDS_ERROR_126##</td></row>
+		<row><td>1932</td><td>##IDS_ERROR_127##</td></row>
+		<row><td>1933</td><td>##IDS_ERROR_128##</td></row>
+		<row><td>1934</td><td>##IDS_ERROR_129##</td></row>
+		<row><td>2</td><td>##IDS_ERROR_2##</td></row>
+		<row><td>20</td><td>##IDS_ERROR_18##</td></row>
+		<row><td>21</td><td>##IDS_ERROR_19##</td></row>
+		<row><td>22</td><td>##IDS_ERROR_120##</td></row>
+		<row><td>23</td><td>##IDS_ERROR_121##</td></row>
+		<row><td>2331</td><td>##IDS_ERROR_2331##</td></row>
+		<row><td>27500</td><td>##IDS_ERROR_130##</td></row>
+		<row><td>27501</td><td>##IDS_ERROR_131##</td></row>
+		<row><td>27502</td><td>##IDS_ERROR_27502##</td></row>
+		<row><td>27503</td><td>##IDS_ERROR_27503##</td></row>
+		<row><td>27504</td><td>##IDS_ERROR_27504##</td></row>
+		<row><td>27505</td><td>##IDS_ERROR_27505##</td></row>
+		<row><td>27506</td><td>##IDS_ERROR_27506##</td></row>
+		<row><td>27507</td><td>##IDS_ERROR_27507##</td></row>
+		<row><td>27508</td><td>##IDS_ERROR_27508##</td></row>
+		<row><td>27509</td><td>##IDS_ERROR_27509##</td></row>
+		<row><td>27510</td><td>##IDS_ERROR_27510##</td></row>
+		<row><td>27511</td><td>##IDS_ERROR_27511##</td></row>
+		<row><td>27512</td><td>##IDS_ERROR_27512##</td></row>
+		<row><td>27513</td><td>##IDS_ERROR_27513##</td></row>
+		<row><td>27514</td><td>##IDS_ERROR_27514##</td></row>
+		<row><td>27515</td><td>##IDS_ERROR_27515##</td></row>
+		<row><td>27516</td><td>##IDS_ERROR_27516##</td></row>
+		<row><td>27517</td><td>##IDS_ERROR_27517##</td></row>
+		<row><td>27518</td><td>##IDS_ERROR_27518##</td></row>
+		<row><td>27519</td><td>##IDS_ERROR_27519##</td></row>
+		<row><td>27520</td><td>##IDS_ERROR_27520##</td></row>
+		<row><td>27521</td><td>##IDS_ERROR_27521##</td></row>
+		<row><td>27522</td><td>##IDS_ERROR_27522##</td></row>
+		<row><td>27523</td><td>##IDS_ERROR_27523##</td></row>
+		<row><td>27524</td><td>##IDS_ERROR_27524##</td></row>
+		<row><td>27525</td><td>##IDS_ERROR_27525##</td></row>
+		<row><td>27526</td><td>##IDS_ERROR_27526##</td></row>
+		<row><td>27527</td><td>##IDS_ERROR_27527##</td></row>
+		<row><td>27528</td><td>##IDS_ERROR_27528##</td></row>
+		<row><td>27529</td><td>##IDS_ERROR_27529##</td></row>
+		<row><td>27530</td><td>##IDS_ERROR_27530##</td></row>
+		<row><td>27531</td><td>##IDS_ERROR_27531##</td></row>
+		<row><td>27532</td><td>##IDS_ERROR_27532##</td></row>
+		<row><td>27533</td><td>##IDS_ERROR_27533##</td></row>
+		<row><td>27534</td><td>##IDS_ERROR_27534##</td></row>
+		<row><td>27535</td><td>##IDS_ERROR_27535##</td></row>
+		<row><td>27536</td><td>##IDS_ERROR_27536##</td></row>
+		<row><td>27537</td><td>##IDS_ERROR_27537##</td></row>
+		<row><td>27538</td><td>##IDS_ERROR_27538##</td></row>
+		<row><td>27539</td><td>##IDS_ERROR_27539##</td></row>
+		<row><td>27540</td><td>##IDS_ERROR_27540##</td></row>
+		<row><td>27541</td><td>##IDS_ERROR_27541##</td></row>
+		<row><td>27542</td><td>##IDS_ERROR_27542##</td></row>
+		<row><td>27543</td><td>##IDS_ERROR_27543##</td></row>
+		<row><td>27544</td><td>##IDS_ERROR_27544##</td></row>
+		<row><td>27545</td><td>##IDS_ERROR_27545##</td></row>
+		<row><td>27546</td><td>##IDS_ERROR_27546##</td></row>
+		<row><td>27547</td><td>##IDS_ERROR_27547##</td></row>
+		<row><td>27548</td><td>##IDS_ERROR_27548##</td></row>
+		<row><td>27549</td><td>##IDS_ERROR_27549##</td></row>
+		<row><td>27550</td><td>##IDS_ERROR_27550##</td></row>
+		<row><td>27551</td><td>##IDS_ERROR_27551##</td></row>
+		<row><td>27552</td><td>##IDS_ERROR_27552##</td></row>
+		<row><td>27553</td><td>##IDS_ERROR_27553##</td></row>
+		<row><td>27554</td><td>##IDS_ERROR_27554##</td></row>
+		<row><td>32</td><td>##IDS_ERROR_20##</td></row>
+		<row><td>33</td><td>##IDS_ERROR_21##</td></row>
+		<row><td>4</td><td>##IDS_ERROR_3##</td></row>
+		<row><td>5</td><td>##IDS_ERROR_4##</td></row>
+		<row><td>7</td><td>##IDS_ERROR_5##</td></row>
+		<row><td>8</td><td>##IDS_ERROR_6##</td></row>
+		<row><td>9</td><td>##IDS_ERROR_7##</td></row>
+	</table>
+
+	<table name="EventMapping">
+		<col key="yes" def="s72">Dialog_</col>
+		<col key="yes" def="s50">Control_</col>
+		<col key="yes" def="s50">Event</col>
+		<col def="s50">Attribute</col>
+		<row><td>CustomSetup</td><td>ItemDescription</td><td>SelectionDescription</td><td>Text</td></row>
+		<row><td>CustomSetup</td><td>Location</td><td>SelectionPath</td><td>Text</td></row>
+		<row><td>CustomSetup</td><td>Size</td><td>SelectionSize</td><td>Text</td></row>
+		<row><td>SetupInitialization</td><td>ActionData</td><td>ActionData</td><td>Text</td></row>
+		<row><td>SetupInitialization</td><td>ActionText</td><td>ActionText</td><td>Text</td></row>
+		<row><td>SetupProgress</td><td>ActionProgress95</td><td>AdminInstallFinalize</td><td>Progress</td></row>
+		<row><td>SetupProgress</td><td>ActionProgress95</td><td>InstallFiles</td><td>Progress</td></row>
+		<row><td>SetupProgress</td><td>ActionProgress95</td><td>MoveFiles</td><td>Progress</td></row>
+		<row><td>SetupProgress</td><td>ActionProgress95</td><td>RemoveFiles</td><td>Progress</td></row>
+		<row><td>SetupProgress</td><td>ActionProgress95</td><td>RemoveRegistryValues</td><td>Progress</td></row>
+		<row><td>SetupProgress</td><td>ActionProgress95</td><td>SetProgress</td><td>Progress</td></row>
+		<row><td>SetupProgress</td><td>ActionProgress95</td><td>UnmoveFiles</td><td>Progress</td></row>
+		<row><td>SetupProgress</td><td>ActionProgress95</td><td>WriteIniValues</td><td>Progress</td></row>
+		<row><td>SetupProgress</td><td>ActionProgress95</td><td>WriteRegistryValues</td><td>Progress</td></row>
+		<row><td>SetupProgress</td><td>ActionText</td><td>ActionText</td><td>Text</td></row>
+	</table>
+
+	<table name="Extension">
+		<col key="yes" def="s255">Extension</col>
+		<col key="yes" def="s72">Component_</col>
+		<col def="S255">ProgId_</col>
+		<col def="S64">MIME_</col>
+		<col def="s38">Feature_</col>
+	</table>
+
+	<table name="Feature">
+		<col key="yes" def="s38">Feature</col>
+		<col def="S38">Feature_Parent</col>
+		<col def="L64">Title</col>
+		<col def="L255">Description</col>
+		<col def="I2">Display</col>
+		<col def="i2">Level</col>
+		<col def="S72">Directory_</col>
+		<col def="i2">Attributes</col>
+		<col def="S255">ISReleaseFlags</col>
+		<col def="S255">ISComments</col>
+		<col def="S255">ISFeatureCabName</col>
+		<col def="S255">ISProFeatureName</col>
+		<row><td>MainFeature</td><td/><td>##ID_STRING4##</td><td/><td>2</td><td>1</td><td>INSTALLDIR</td><td>0</td><td/><td/><td/><td/></row>
+	</table>
+
+	<table name="FeatureComponents">
+		<col key="yes" def="s38">Feature_</col>
+		<col key="yes" def="s72">Component_</col>
+		<row><td>MainFeature</td><td>AllOtherFiles</td></row>
+		<row><td>MainFeature</td><td>AllOtherFiles1</td></row>
+		<row><td>MainFeature</td><td>AllOtherFiles2</td></row>
+		<row><td>MainFeature</td><td>AllOtherFiles3</td></row>
+		<row><td>MainFeature</td><td>ISRegistryComponent</td></row>
+		<row><td>MainFeature</td><td>ISRegistryComponent1</td></row>
+		<row><td>MainFeature</td><td>VirtualDirComponent</td></row>
+	</table>
+
+	<table name="File">
+		<col key="yes" def="s72">File</col>
+		<col def="s72">Component_</col>
+		<col def="s255">FileName</col>
+		<col def="i4">FileSize</col>
+		<col def="S72">Version</col>
+		<col def="S20">Language</col>
+		<col def="I2">Attributes</col>
+		<col def="i2">Sequence</col>
+		<col def="S255">ISBuildSourcePath</col>
+		<col def="I4">ISAttributes</col>
+		<col def="S72">ISComponentSubFolder_</col>
+		<row><td>license.txt</td><td>AllOtherFiles3</td><td>LICENSE.TXT</td><td>0</td><td/><td/><td/><td>1</td><td>&lt;ISProjectFolder&gt;\LICENSE.TXT</td><td>1</td><td/></row>
+	</table>
+
+	<table name="FileSFPCatalog">
+		<col key="yes" def="s72">File_</col>
+		<col key="yes" def="s255">SFPCatalog_</col>
+	</table>
+
+	<table name="Font">
+		<col key="yes" def="s72">File_</col>
+		<col def="S128">FontTitle</col>
+	</table>
+
+	<table name="ISAssistantTag">
+		<col key="yes" def="s72">Tag</col>
+		<col def="S255">Data</col>
+		<row><td>BiildCDROMEnabled</td><td/></row>
+		<row><td>BiildInternetEnabled</td><td/></row>
+		<row><td>BiildSingleExeEnabled</td><td/></row>
+		<row><td>BiildSingleMSIEnabled</td><td/></row>
+		<row><td>PROJECT_ASSISTANT_DEFAULT_FEATURE</td><td>NewFeature1</td></row>
+		<row><td>PROJECT_ASSISTANT_FEATURES</td><td>NonSelectable</td></row>
+		<row><td>RegistryPageEnabled</td><td>Yes</td></row>
+	</table>
+
+	<table name="ISCEApp">
+		<col key="yes" def="s50">AppKey</col>
+		<col def="s50">AppName</col>
+		<col def="s200">CompanyName</col>
+		<col def="s50">DefDir</col>
+		<col def="S255">IconPath</col>
+		<col def="I4">IconIndex</col>
+		<col def="S255">DeviceFile</col>
+		<col def="s50">DesktopTargetDir</col>
+		<col def="S255">Description</col>
+		<col def="i2">DeleteMedia</col>
+		<col def="I4">InstallNetCF</col>
+		<col def="I4">InstallSQLServer</col>
+		<col def="I4">InstallSQLClient</col>
+		<col def="I4">InstallSQLDev</col>
+		<col def="S255">PreXML</col>
+		<col def="S255">PostXML</col>
+		<col def="I2">NoUninstall</col>
+		<col def="S255">SPCFile</col>
+		<col def="S255">PVKFile</col>
+		<col def="I4">Attributes</col>
+		<col def="S255">RawDeviceFile</col>
+		<col def="S72">Component_</col>
+	</table>
+
+	<table name="ISCEDir">
+		<col key="yes" def="s50">AppKey</col>
+		<col key="yes" def="s50">DirKey</col>
+		<col def="s50">DirParent</col>
+		<col def="s255">DirValue</col>
+	</table>
+
+	<table name="ISCEFile">
+		<col key="yes" def="s50">AppKey</col>
+		<col key="yes" def="s50">FileKey</col>
+		<col def="s255">Name</col>
+		<col def="s50">Destination</col>
+		<col def="s255">Source</col>
+		<col def="i4">Processor</col>
+		<col def="i4">Platform</col>
+		<col def="i4">CopyOption</col>
+		<col def="i4">FileOption</col>
+		<col def="I4">AdvancedOptions</col>
+	</table>
+
+	<table name="ISCEFileExt">
+		<col key="yes" def="s50">AppKey</col>
+		<col key="yes" def="s50">ExtKey</col>
+		<col def="s50">FileKey</col>
+		<col def="S255">Description</col>
+		<col def="s50">Extension</col>
+		<col def="i4">IconIndex</col>
+	</table>
+
+	<table name="ISCEInstall">
+		<col key="yes" def="s255">CEInstallKey</col>
+		<col def="s255">CEAppName</col>
+		<col def="s255">CEDesktopDir</col>
+		<col def="s255">CEIniFileKey</col>
+		<col def="s0">CECabs</col>
+		<col def="s0">CEIcoFile</col>
+		<col def="i2">DeleteMedia</col>
+		<col def="S38">Component_</col>
+	</table>
+
+	<table name="ISCEOtherAppCABs">
+		<col key="yes" def="s50">AppKey</col>
+		<col key="yes" def="s50">FileKey</col>
+		<col def="s255">BuildSourcePath</col>
+	</table>
+
+	<table name="ISCERegistry">
+		<col key="yes" def="s50">AppKey</col>
+		<col key="yes" def="s50">RegKey</col>
+		<col def="i4">Root</col>
+		<col def="s255">Key</col>
+		<col def="S255">Name</col>
+		<col def="S255">Value</col>
+		<col def="i4">Processor</col>
+		<col def="i4">Platform</col>
+		<col def="i4">Overwrite</col>
+	</table>
+
+	<table name="ISCESetupFile">
+		<col key="yes" def="s50">AppKey</col>
+		<col key="yes" def="s50">SetupFileKey</col>
+		<col def="s255">Name</col>
+		<col def="s255">Source</col>
+		<col def="i4">Processor</col>
+		<col def="i4">Platform</col>
+	</table>
+
+	<table name="ISCEShtCut">
+		<col key="yes" def="s50">AppKey</col>
+		<col key="yes" def="s50">ShtCutKey</col>
+		<col def="s255">DisplayName</col>
+		<col def="s255">Destination</col>
+		<col def="s50">Target</col>
+		<col def="i4">Platform</col>
+	</table>
+
+	<table name="ISComCatalogAttribute">
+		<col key="yes" def="s72">ISComCatalogObject_</col>
+		<col key="yes" def="s255">ItemName</col>
+		<col def="S0">ItemValue</col>
+	</table>
+
+	<table name="ISComCatalogCollection">
+		<col key="yes" def="s72">ISComCatalogCollection</col>
+		<col def="s72">ISComCatalogObject_</col>
+		<col def="s255">CollectionName</col>
+	</table>
+
+	<table name="ISComCatalogCollectionObjects">
+		<col key="yes" def="s72">ISComCatalogCollection_</col>
+		<col key="yes" def="s72">ISComCatalogObject_</col>
+	</table>
+
+	<table name="ISComCatalogObject">
+		<col key="yes" def="s72">ISComCatalogObject</col>
+		<col def="s255">DisplayName</col>
+	</table>
+
+	<table name="ISComPlusApplication">
+		<col key="yes" def="s72">ISComCatalogObject_</col>
+		<col def="S255">ComputerName</col>
+		<col def="s72">Component_</col>
+		<col def="I2">ISAttributes</col>
+		<col def="S0">DepFiles</col>
+	</table>
+
+	<table name="ISComPlusProxy">
+		<col key="yes" def="s72">ISComPlusProxy</col>
+		<col def="s72">ISComPlusApplication_</col>
+		<col def="S72">Component_</col>
+		<col def="I2">ISAttributes</col>
+		<col def="S0">DepFiles</col>
+	</table>
+
+	<table name="ISComponentExtended">
+		<col key="yes" def="s72">Component_</col>
+		<col def="I4">OS</col>
+		<col def="S0">Language</col>
+		<col def="s72">FilterProperty</col>
+		<col def="I4">Platforms</col>
+		<col def="S0">FTPLocation</col>
+		<col def="S0">HTTPLocation</col>
+		<col def="S0">Miscellaneous</col>
+		<row><td>AllOtherFiles</td><td/><td/><td>_74B17E30_4D29_487A_8CD4_7B95B8D56A83_FILTER</td><td/><td/><td/><td/></row>
+		<row><td>AllOtherFiles1</td><td/><td/><td>_7B0061CE_6436_4507_9459_93572D569FC1_FILTER</td><td/><td/><td/><td/></row>
+		<row><td>AllOtherFiles10</td><td/><td/><td>_E157B509_E34A_4875_AB50_BA85BE3F5DD2_FILTER</td><td/><td/><td/><td/></row>
+		<row><td>AllOtherFiles11</td><td/><td/><td>_3B6FD6F4_F250_47A8_834C_D7EFD14C3BEF_FILTER</td><td/><td/><td/><td/></row>
+		<row><td>AllOtherFiles12</td><td/><td/><td>_D98A783F_15B1_4E2D_B444_2AFE9178F6F0_FILTER</td><td/><td/><td/><td/></row>
+		<row><td>AllOtherFiles2</td><td/><td/><td>_7322BDD9_A833_41E8_B75C_D99037B19E78_FILTER</td><td/><td/><td/><td/></row>
+		<row><td>AllOtherFiles3</td><td/><td/><td>_C7E1E3F1_ABC8_4A0D_929F_A385EDF1E1F7_FILTER</td><td/><td/><td/><td/></row>
+		<row><td>AllOtherFiles4</td><td/><td/><td>_D740564E_80EF_41ED_958F_95C1F4062224_FILTER</td><td/><td/><td/><td/></row>
+		<row><td>AllOtherFiles5</td><td/><td/><td>_D918C9FE_F0EE_4F2B_870A_095BCA8C2C2F_FILTER</td><td/><td/><td/><td/></row>
+		<row><td>AllOtherFiles6</td><td/><td/><td>_228CD380_C643_4C2C_8ECB_4E7429660108_FILTER</td><td/><td/><td/><td/></row>
+		<row><td>AllOtherFiles7</td><td/><td/><td>_0C21B08E_04C8_4326_8560_DDA770F7D93E_FILTER</td><td/><td/><td/><td/></row>
+		<row><td>AllOtherFiles8</td><td/><td/><td>_845468E3_C693_4547_A32F_F231781A4C8A_FILTER</td><td/><td/><td/><td/></row>
+		<row><td>AllOtherFiles9</td><td/><td/><td>_5F1CCD3B_FD30_4D31_ACEE_707E99BE0281_FILTER</td><td/><td/><td/><td/></row>
+		<row><td>ISRegistryComponent</td><td/><td/><td>_1DEB35F2_294C_4ED0_B0A2_78FB6F4E2EFF_FILTER</td><td/><td/><td/><td/></row>
+		<row><td>ISRegistryComponent1</td><td/><td/><td>_2E54C60F_AF93_4D31_8E1B_F381B381FC92_FILTER</td><td/><td/><td/><td/></row>
+		<row><td>VirtualDirComponent</td><td/><td/><td>_5ACAD922_2F71_4963_BCF2_D05C97252574_FILTER</td><td/><td/><td/><td/></row>
+	</table>
+
+	<table name="ISDIMDependency">
+		<col key="yes" def="s72">ISDIMReference_</col>
+		<col def="s255">RequiredUUID</col>
+		<col def="S255">RequiredMajorVersion</col>
+		<col def="S255">RequiredMinorVersion</col>
+		<col def="S255">RequiredBuildVersion</col>
+		<col def="S255">RequiredRevisionVersion</col>
+	</table>
+
+	<table name="ISDIMReference">
+		<col key="yes" def="s72">ISDIMReference</col>
+		<col def="S0">ISBuildSourcePath</col>
+	</table>
+
+	<table name="ISDIMReferenceDependencies">
+		<col key="yes" def="s72">ISDIMReference_Parent</col>
+		<col key="yes" def="s72">ISDIMDependency_</col>
+	</table>
+
+	<table name="ISDIMVariable">
+		<col key="yes" def="s72">ISDIMVariable</col>
+		<col def="s72">ISDIMReference_</col>
+		<col def="s0">Name</col>
+		<col def="S0">NewValue</col>
+		<col def="I4">Type</col>
+	</table>
+
+	<table name="ISDLLWrapper">
+		<col key="yes" def="s72">EntryPoint</col>
+		<col def="I4">Type</col>
+		<col def="s0">Source</col>
+		<col def="s255">Target</col>
+	</table>
+
+	<table name="ISDRMFile">
+		<col key="yes" def="s72">ISDRMFile</col>
+		<col def="S72">File_</col>
+		<col def="S72">ISDRMLicense_</col>
+		<col def="s255">Shell</col>
+	</table>
+
+	<table name="ISDRMFileAttribute">
+		<col key="yes" def="s72">ISDRMFile_</col>
+		<col key="yes" def="s72">Property</col>
+		<col def="S0">Value</col>
+	</table>
+
+	<table name="ISDRMLicense">
+		<col key="yes" def="s72">ISDRMLicense</col>
+		<col def="S255">Description</col>
+		<col def="S50">ProjectVersion</col>
+		<col def="I4">Attributes</col>
+		<col def="S255">LicenseNumber</col>
+		<col def="S255">RequestCode</col>
+		<col def="S255">ResponseCode</col>
+	</table>
+
+	<table name="ISDependency">
+		<col key="yes" def="S50">ISDependency</col>
+		<col def="I2">Exclude</col>
+	</table>
+
+	<table name="ISDisk1File">
+		<col key="yes" def="s72">ISDisk1File</col>
+		<col def="s255">ISBuildSourcePath</col>
+		<col def="I4">Disk</col>
+	</table>
+
+	<table name="ISDynamicFile">
+		<col key="yes" def="s72">Component_</col>
+		<col key="yes" def="s255">SourceFolder</col>
+		<col def="I2">IncludeFlags</col>
+		<col def="S0">IncludeFiles</col>
+		<col def="S0">ExcludeFiles</col>
+		<col def="I4">ISAttributes</col>
+		<row><td>AllOtherFiles</td><td>&lt;ISProjectFolder&gt;\bin</td><td>4</td><td/><td/><td>0</td></row>
+		<row><td>AllOtherFiles1</td><td>&lt;ISProjectFolder&gt;\conf</td><td>4</td><td/><td/><td>0</td></row>
+		<row><td>AllOtherFiles2</td><td>&lt;ISProjectFolder&gt;\log</td><td>4</td><td/><td/><td>0</td></row>
+	</table>
+
+	<table name="ISFeatureDIMReferences">
+		<col key="yes" def="s38">Feature_</col>
+		<col key="yes" def="s72">ISDIMReference_</col>
+	</table>
+
+	<table name="ISFeatureMergeModuleExcludes">
+		<col key="yes" def="s38">Feature_</col>
+		<col key="yes" def="s255">ModuleID</col>
+		<col key="yes" def="i2">Language</col>
+	</table>
+
+	<table name="ISFeatureMergeModules">
+		<col key="yes" def="s38">Feature_</col>
+		<col key="yes" def="s255">ISMergeModule_</col>
+		<col key="yes" def="i2">Language_</col>
+	</table>
+
+	<table name="ISFileManifests">
+		<col key="yes" def="s72">File_</col>
+		<col key="yes" def="s72">Manifest_</col>
+	</table>
+
+	<table name="ISIISAppPool">
+		<col key="yes" def="s72">AppPool</col>
+		<col def="l72">Name</col>
+		<col def="s72">Component_</col>
+		<col def="I4">Attributes</col>
+		<col def="S72">User</col>
+		<col def="S72">UserPassword</col>
+		<col def="I4">RecycleMinutes</col>
+		<col def="I4">RecycleRequests</col>
+		<col def="S72">RecycleTimes</col>
+		<col def="I4">IdleTimeout</col>
+		<col def="I4">QueueLimit</col>
+		<col def="S72">CPUMon</col>
+		<col def="I4">MaxProc</col>
+	</table>
+
+	<table name="ISIISCommon">
+		<col key="yes" def="s50">ISIISCommon</col>
+		<col def="S50">ISIISCommon_Parent</col>
+		<col def="L255">DisplayName</col>
+		<col def="s50">RootDir</col>
+		<col def="i4">Attributes</col>
+		<col def="L255">DefDoc</col>
+		<col def="I4">SessionTimeout</col>
+		<col def="I4">ScriptTimeout</col>
+		<col def="S255">AnonyUserName</col>
+		<col def="S255">AnonyPasswrd</col>
+		<col def="S0">CustomErrors</col>
+		<col def="L255">AppName</col>
+		<col def="S72">SSLCert</col>
+		<col def="L72">AppPool_</col>
+		<row><td>ISIISCommonVRoot</td><td>ISIISCommonWebsite1</td><td>##ID_STRING3##</td><td>BIN</td><td>28069</td><td>Default.asp</td><td>20</td><td>90</td><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISIISCommonWebsite1</td><td/><td>Default Web Site</td><td>IISROOTFOLDER</td><td>28113</td><td/><td>20</td><td>90</td><td/><td/><td/><td/><td/><td/></row>
+	</table>
+
+	<table name="ISIISMetaData">
+		<col key="yes" def="s72">ISIISCommon_</col>
+		<col key="yes" def="I4">MetaDataProp</col>
+		<col def="I4">MetaDataType</col>
+		<col def="i4">MetaDataUserType</col>
+		<col def="i4">MetaDataAttributes</col>
+		<col def="i4">Order</col>
+		<col def="s0">MetaDataValue</col>
+	</table>
+
+	<table name="ISIISWebServiceExtension">
+		<col key="yes" def="s72">WebServiceExtension</col>
+		<col def="s0">Component_</col>
+		<col def="s72">File</col>
+		<col def="l72">Description</col>
+		<col def="s72">Group</col>
+		<col def="i4">Attributes</col>
+	</table>
+
+	<table name="ISInstallScriptAction">
+		<col key="yes" def="s72">EntryPoint</col>
+		<col def="I4">Type</col>
+		<col def="s72">Source</col>
+		<col def="S255">Target</col>
+	</table>
+
+	<table name="ISLanguage">
+		<col key="yes" def="s50">ISLanguage</col>
+		<col def="I2">Included</col>
+		<row><td>1033</td><td>1</td></row>
+	</table>
+
+	<table name="ISLinkerLibrary">
+		<col key="yes" def="s72">ISLinkerLibrary</col>
+		<col def="s255">Library</col>
+		<col def="i4">Order</col>
+		<row><td>Isrt.obl</td><td>&lt;ISProductFolder&gt;\Script\ISRT\Lib\isrt.obl</td><td>2</td></row>
+		<row><td>Iswi.obl</td><td>&lt;ISProductFolder&gt;\Script\ISWi\Lib\iswi.obl</td><td>1</td></row>
+	</table>
+
+	<table name="ISLocalControl">
+		<col key="yes" def="s72">Dialog_</col>
+		<col key="yes" def="s50">Control_</col>
+		<col key="yes" def="s50">ISLanguage_</col>
+		<col def="I4">Attributes</col>
+		<col def="I2">X</col>
+		<col def="I2">Y</col>
+		<col def="I2">Width</col>
+		<col def="I2">Height</col>
+		<col def="S72">Binary_</col>
+		<col def="S255">ISBuildSourcePath</col>
+	</table>
+
+	<table name="ISLocalDialog">
+		<col key="yes" def="S50">Dialog_</col>
+		<col key="yes" def="S50">ISLanguage_</col>
+		<col def="I4">Attributes</col>
+		<col def="S72">TextStyle_</col>
+		<col def="i2">Width</col>
+		<col def="i2">Height</col>
+	</table>
+
+	<table name="ISLocalRadioButton">
+		<col key="yes" def="s72">Property</col>
+		<col key="yes" def="i2">Order</col>
+		<col key="yes" def="s50">ISLanguage_</col>
+		<col def="i2">X</col>
+		<col def="i2">Y</col>
+		<col def="i2">Width</col>
+		<col def="i2">Height</col>
+	</table>
+
+	<table name="ISLogicalDisk">
+		<col key="yes" def="i2">DiskId</col>
+		<col key="yes" def="s255">ISProductConfiguration_</col>
+		<col key="yes" def="s255">ISRelease_</col>
+		<col def="i2">LastSequence</col>
+		<col def="L64">DiskPrompt</col>
+		<col def="S255">Cabinet</col>
+		<col def="S32">VolumeLabel</col>
+		<col def="S32">Source</col>
+		<row><td>1</td><td>Release</td><td>Bin</td><td>0</td><td/><td/><td/><td/></row>
+		<row><td>1</td><td>Release</td><td>Msi</td><td>0</td><td/><td/><td/><td/></row>
+	</table>
+
+	<table name="ISLogicalDiskFeatures">
+		<col key="yes" def="i2">ISLogicalDisk_</col>
+		<col key="yes" def="s255">ISProductConfiguration_</col>
+		<col key="yes" def="s255">ISRelease_</col>
+		<col key="yes" def="S38">Feature_</col>
+		<col def="i2">Sequence</col>
+		<col def="I4">ISAttributes</col>
+	</table>
+
+	<table name="ISMergeModule">
+		<col key="yes" def="s255">ISMergeModule</col>
+		<col key="yes" def="i2">Language</col>
+		<col def="s255">Name</col>
+		<col def="S255">Destination</col>
+		<col def="I4">ISAttributes</col>
+	</table>
+
+	<table name="ISMergeModuleCfgValues">
+		<col key="yes" def="s255">ISMergeModule_</col>
+		<col key="yes" def="i2">Language_</col>
+		<col key="yes" def="s72">ModuleConfiguration_</col>
+		<col def="L0">Value</col>
+		<col def="i2">Format</col>
+		<col def="L255">Type</col>
+		<col def="L255">ContextData</col>
+		<col def="L255">DefaultValue</col>
+		<col def="I2">Attributes</col>
+		<col def="L255">DisplayName</col>
+		<col def="L255">Description</col>
+		<col def="L255">HelpLocation</col>
+		<col def="L255">HelpKeyword</col>
+	</table>
+
+	<table name="ISObject">
+		<col key="yes" def="s50">ObjectName</col>
+		<col def="s15">Language</col>
+	</table>
+
+	<table name="ISObjectProperty">
+		<col key="yes" def="S50">ObjectName</col>
+		<col key="yes" def="S50">Property</col>
+		<col def="S0">Value</col>
+		<col def="I2">IncludeInBuild</col>
+	</table>
+
+	<table name="ISPalmApp">
+		<col key="yes" def="s72">PalmApp</col>
+		<col key="yes" def="s72">Component</col>
+	</table>
+
+	<table name="ISPalmAppFile">
+		<col key="yes" def="s72">PalmApp</col>
+		<col key="yes" def="s72">FileKey</col>
+		<col def="i4">Destination</col>
+	</table>
+
+	<table name="ISPatchConfigImage">
+		<col key="yes" def="S72">PatchConfiguration_</col>
+		<col key="yes" def="s72">UpgradedImage_</col>
+	</table>
+
+	<table name="ISPatchConfiguration">
+		<col key="yes" def="s72">Name</col>
+		<col def="i2">CanPCDiffer</col>
+		<col def="i2">CanPVDiffer</col>
+		<col def="i2">IncludeWholeFiles</col>
+		<col def="i2">LeaveDecompressed</col>
+		<col def="i2">OptimizeForSize</col>
+		<col def="i2">EnablePatchCache</col>
+		<col def="S0">PatchCacheDir</col>
+		<col def="i4">Flags</col>
+		<col def="S0">PatchGuidsToReplace</col>
+		<col def="s0">TargetProductCodes</col>
+		<col def="s50">PatchGuid</col>
+		<col def="s0">OutputPath</col>
+		<col def="i2">MinMsiVersion</col>
+		<col def="I4">Attributes</col>
+	</table>
+
+	<table name="ISPatchConfigurationProperty">
+		<col key="yes" def="S72">ISPatchConfiguration_</col>
+		<col key="yes" def="S50">Property</col>
+		<col def="S50">Value</col>
+	</table>
+
+	<table name="ISPatchExternalFile">
+		<col key="yes" def="s50">Name</col>
+		<col key="yes" def="s13">ISUpgradedImage_</col>
+		<col def="s72">FileKey</col>
+		<col def="s255">FilePath</col>
+	</table>
+
+	<table name="ISPatchWholeFile">
+		<col key="yes" def="s50">UpgradedImage</col>
+		<col key="yes" def="s72">FileKey</col>
+		<col def="S72">Component</col>
+	</table>
+
+	<table name="ISPathVariable">
+		<col key="yes" def="s32">ISPathVariable</col>
+		<col def="S255">Value</col>
+		<col def="S255">TestValue</col>
+		<col def="i4">Type</col>
+		<row><td>CommonFilesFolder</td><td/><td/><td>1</td></row>
+		<row><td>ISPROJECTDIR</td><td/><td/><td>1</td></row>
+		<row><td>ISProductFolder</td><td/><td/><td>1</td></row>
+		<row><td>ISProjectDataFolder</td><td/><td/><td>1</td></row>
+		<row><td>ISProjectFolder</td><td/><td/><td>1</td></row>
+		<row><td>PATH_TO_IIS_FILES</td><td>C:\W\jakarta\jtc\jk\native\iis</td><td/><td>2</td></row>
+		<row><td>ProgramFilesFolder</td><td/><td/><td>1</td></row>
+		<row><td>SystemFolder</td><td/><td/><td>1</td></row>
+		<row><td>WindowsFolder</td><td/><td/><td>1</td></row>
+	</table>
+
+	<table name="ISProductConfiguration">
+		<col key="yes" def="s72">ISProductConfiguration</col>
+		<col def="S255">ProductConfigurationFlags</col>
+		<col def="I4">GeneratePackageCode</col>
+		<row><td>Release</td><td/><td>1</td></row>
+	</table>
+
+	<table name="ISProductConfigurationInstance">
+		<col key="yes" def="s72">ISProductConfiguration_</col>
+		<col key="yes" def="i2">InstanceId</col>
+		<col key="yes" def="s72">Property</col>
+		<col def="s255">Value</col>
+	</table>
+
+	<table name="ISProductConfigurationProperty">
+		<col key="yes" def="s72">ISProductConfiguration_</col>
+		<col key="yes" def="s72">Property</col>
+		<col def="L255">Value</col>
+		<row><td>Release</td><td>SetupFileName</td><td>setup</td></row>
+	</table>
+
+	<table name="ISRelease">
+		<col key="yes" def="s72">ISRelease</col>
+		<col key="yes" def="s72">ISProductConfiguration_</col>
+		<col def="s255">BuildLocation</col>
+		<col def="s255">PackageName</col>
+		<col def="i4">Type</col>
+		<col def="s255">SupportedLanguagesUI</col>
+		<col def="i4">MsiSourceType</col>
+		<col def="i4">ReleaseType</col>
+		<col def="s72">Platforms</col>
+		<col def="S255">SupportedLanguagesData</col>
+		<col def="s6">DefaultLanguage</col>
+		<col def="i4">SupportedOSs</col>
+		<col def="s50">DiskSize</col>
+		<col def="i4">DiskSizeUnit</col>
+		<col def="i4">DiskClusterSize</col>
+		<col def="S255">ReleaseFlags</col>
+		<col def="i4">DiskSpanning</col>
+		<col def="S255">SynchMsi</col>
+		<col def="s255">MediaLocation</col>
+		<col def="S255">URLLocation</col>
+		<col def="S255">DigitalURL</col>
+		<col def="S255">DigitalPVK</col>
+		<col def="S255">DigitalSPC</col>
+		<col def="S255">Password</col>
+		<col def="S255">VersionCopyright</col>
+		<col def="i4">Attributes</col>
+		<col def="S255">CDBrowser</col>
+		<col def="S255">DotNetBuildConfiguration</col>
+		<col def="S255">MsiCommandLine</col>
+		<col def="I4">ISSetupPrerequisiteLocation</col>
+		<row><td>Bin</td><td>Release</td><td>&lt;ISProjectDataFolder&gt;</td><td>PackageName</td><td>4</td><td>1033</td><td>2</td><td>1</td><td>Intel</td><td/><td>1033</td><td>3</td><td>0</td><td>1</td><td>0</td><td/><td>0</td><td/><td>MediaLocation</td><td/><td>http://</td><td/><td/><td/><td/><td>8988685</td><td/><td/><td/><td/></row>
+		<row><td>Msi</td><td>Release</td><td>&lt;ISProjectDataFolder&gt;</td><td>PackageName</td><td>0</td><td>1033</td><td>2</td><td>1</td><td>Intel</td><td/><td>1033</td><td>3</td><td>650</td><td>0</td><td>2048</td><td/><td>0</td><td/><td>MediaLocation</td><td/><td>http://</td><td/><td/><td/><td/><td>8464397</td><td/><td/><td/><td/></row>
+	</table>
+
+	<table name="ISReleaseASPublishInfo">
+		<col key="yes" def="s72">ISRelease_</col>
+		<col key="yes" def="s72">ISProductConfiguration_</col>
+		<col key="yes" def="S0">Property</col>
+		<col def="S0">Value</col>
+	</table>
+
+	<table name="ISReleaseExtended">
+		<col key="yes" def="s72">ISRelease_</col>
+		<col key="yes" def="s72">ISProductConfiguration_</col>
+		<col def="I4">WebType</col>
+		<col def="S255">WebURL</col>
+		<col def="I4">WebCabSize</col>
+		<col def="S255">OneClickCabName</col>
+		<col def="S255">OneClickHtmlName</col>
+		<col def="S255">WebLocalCachePath</col>
+		<col def="I4">EngineLocation</col>
+		<col def="S255">Win9xMsiUrl</col>
+		<col def="S255">WinNTMsiUrl</col>
+		<col def="I4">ISEngineLocation</col>
+		<col def="S255">ISEngineURL</col>
+		<col def="I4">OneClickTargetBrowser</col>
+		<col def="S255">DigitalCertificateIdNS</col>
+		<col def="S255">DigitalCertificateDBaseNS</col>
+		<col def="S255">DigitalCertificatePasswordNS</col>
+		<col def="I4">DotNetRedistLocation</col>
+		<col def="S255">DotNetRedistURL</col>
+		<col def="I4">DotNetVersion</col>
+		<col def="S255">DotNetBaseLanguage</col>
+		<col def="S0">DotNetLangaugePacks</col>
+		<col def="S255">DotNetFxCmdLine</col>
+		<col def="S255">DotNetLangPackCmdLine</col>
+		<col def="S50">JSharpCmdLine</col>
+		<col def="I4">Attributes</col>
+		<col def="I4">JSharpRedistLocation</col>
+		<col def="I4">MsiEngineVersion</col>
+		<col def="S255">WinMsi30Url</col>
+		<col def="S255">CertPassword</col>
+		<row><td>Bin</td><td>Release</td><td>0</td><td>http://</td><td>0</td><td>install</td><td>install</td><td>[WindowsFolder]Downloaded Installations</td><td>1</td><td>http://www.installengine.com/Msiengine20</td><td>http://www.installengine.com/Msiengine20</td><td>2</td><td>http://www.installengine.com/cert05/isengine</td><td>2</td><td/><td/><td/><td>3</td><td>http://www.installengine.com/cert05/dotnetfx</td><td>0</td><td>1033</td><td/><td/><td/><td/><td>16</td><td>3</td><td>4</td><td>http://www.installengine.com/Msiengine30</td><td/></row>
+		<row><td>Msi</td><td>Release</td><td>0</td><td>http://</td><td>0</td><td>install</td><td>install</td><td>[WindowsFolder]Downloaded Installations</td><td>2</td><td>http://www.installengine.com/Msiengine20</td><td>http://www.installengine.com/Msiengine20</td><td>2</td><td>http://www.installengine.com/cert05/isengine</td><td/><td/><td/><td/><td>3</td><td>http://www.installengine.com/cert05/dotnetfx</td><td>0</td><td>1033</td><td/><td/><td/><td/><td>16</td><td>3</td><td>4</td><td>http://www.installengine.com/Msiengine30</td><td/></row>
+		<row><td>Release 1</td><td>Release</td><td/><td/><td/><td/><td/><td/><td/><td/><td/><td/><td/><td/><td/><td/><td/><td/><td/><td/><td/><td/><td/><td/><td/><td/><td/><td/><td/><td/></row>
+	</table>
+
+	<table name="ISReleasePublishInfo">
+		<col key="yes" def="s72">ISRelease_</col>
+		<col key="yes" def="s72">ISProductConfiguration_</col>
+		<col def="S255">Repository</col>
+		<col def="S255">DisplayName</col>
+		<col def="S255">Publisher</col>
+		<col def="S255">Description</col>
+		<col def="I4">ISAttributes</col>
+	</table>
+
+	<table name="ISSQLConnection">
+		<col key="yes" def="s72">ISSQLConnection</col>
+		<col def="s255">Server</col>
+		<col def="s255">Database</col>
+		<col def="s255">UserName</col>
+		<col def="s255">Password</col>
+		<col def="s255">Authentication</col>
+		<col def="i2">Attributes</col>
+		<col def="i2">Order</col>
+		<col def="S0">Comments</col>
+		<col def="I4">CmdTimeout</col>
+		<col def="S0">BatchSeparator</col>
+	</table>
+
+	<table name="ISSQLConnectionDBServer">
+		<col key="yes" def="s72">ISSQLConnectionDBServer</col>
+		<col key="yes" def="s72">ISSQLConnection_</col>
+		<col key="yes" def="s72">ISSQLDBMetaData_</col>
+		<col def="i2">Order</col>
+	</table>
+
+	<table name="ISSQLConnectionScript">
+		<col key="yes" def="s72">ISSQLConnection_</col>
+		<col key="yes" def="s72">ISSQLScriptFile_</col>
+		<col def="i2">Order</col>
+	</table>
+
+	<table name="ISSQLDBMetaData">
+		<col key="yes" def="s72">ISSQLDBMetaData</col>
+		<col def="S0">DisplayName</col>
+		<col def="S0">AdoDriverName</col>
+		<col def="S0">AdoCxnDriver</col>
+		<col def="S0">AdoCxnServer</col>
+		<col def="S0">AdoCxnDatabase</col>
+		<col def="S0">AdoCxnUserID</col>
+		<col def="S0">AdoCxnPassword</col>
+		<col def="S0">AdoCxnWindowsSecurity</col>
+		<col def="S0">AdoCxnNetLibrary</col>
+		<col def="S0">TestDatabaseCmd</col>
+		<col def="S0">TestTableCmd</col>
+		<col def="S0">VersionInfoCmd</col>
+		<col def="S0">VersionBeginToken</col>
+		<col def="S0">VersionEndToken</col>
+		<col def="S0">LocalInstanceNames</col>
+		<col def="S0">CreateDbCmd</col>
+		<col def="S0">SwitchDbCmd</col>
+		<col def="I4">ISAttributes</col>
+		<col def="S0">TestTableCmd2</col>
+		<col def="S0">WinAuthentUserId</col>
+		<col def="S0">DsnODBCName</col>
+	</table>
+
+	<table name="ISSQLRequirement">
+		<col key="yes" def="s72">ISSQLRequirement</col>
+		<col key="yes" def="s72">ISSQLConnection_</col>
+		<col def="S15">MajorVersion</col>
+		<col def="S25">ServicePackLevel</col>
+		<col def="i4">Attributes</col>
+		<col def="S72">ISSQLConnectionDBServer_</col>
+	</table>
+
+	<table name="ISSQLScriptError">
+		<col key="yes" def="i4">ErrNumber</col>
+		<col key="yes" def="S72">ISSQLScriptFile_</col>
+		<col def="i2">ErrHandling</col>
+		<col def="L255">Message</col>
+		<col def="i2">Attributes</col>
+	</table>
+
+	<table name="ISSQLScriptFile">
+		<col key="yes" def="s72">ISSQLScriptFile</col>
+		<col def="s72">Component_</col>
+		<col def="i2">Scheduling</col>
+		<col def="L255">InstallText</col>
+		<col def="L255">UninstallText</col>
+		<col def="S0">ISBuildSourcePath</col>
+		<col def="S0">Comments</col>
+		<col def="i2">ErrorHandling</col>
+		<col def="i2">Attributes</col>
+		<col def="S15">Version</col>
+	</table>
+
+	<table name="ISSQLScriptImport">
+		<col key="yes" def="s72">ISSQLScriptFile_</col>
+		<col def="S255">Server</col>
+		<col def="S255">Database</col>
+		<col def="S255">UserName</col>
+		<col def="S255">Password</col>
+		<col def="i4">Authentication</col>
+		<col def="S0">IncludeTables</col>
+		<col def="S0">ExcludeTables</col>
+		<col def="i4">Attributes</col>
+	</table>
+
+	<table name="ISSQLScriptReplace">
+		<col key="yes" def="s72">ISSQLScriptReplace</col>
+		<col key="yes" def="s72">ISSQLScriptFile_</col>
+		<col def="S0">Search</col>
+		<col def="S0">Replace</col>
+		<col def="i4">Attributes</col>
+	</table>
+
+	<table name="ISScriptFile">
+		<col key="yes" def="s255">ISScriptFile</col>
+	</table>
+
+	<table name="ISSelfReg">
+		<col key="yes" def="s72">FileKey</col>
+		<col def="I2">Cost</col>
+		<col def="I2">Order</col>
+		<col def="S50">CmdLine</col>
+	</table>
+
+	<table name="ISSetupFile">
+		<col key="yes" def="s72">ISSetupFile</col>
+		<col def="S255">FileName</col>
+		<col def="V0">Stream</col>
+		<col def="S50">Language</col>
+		<col def="I2">Splash</col>
+		<col def="S0">Path</col>
+	</table>
+
+	<table name="ISSetupPrerequisites">
+		<col key="yes" def="s72">ISSetupPrerequisites</col>
+		<col def="S255">ISBuildSourcePath</col>
+		<col def="I2">Order</col>
+	</table>
+
+	<table name="ISSetupType">
+		<col key="yes" def="s38">ISSetupType</col>
+		<col def="L255">Description</col>
+		<col def="L255">Display_Name</col>
+		<col def="i2">Display</col>
+		<col def="S255">Comments</col>
+	</table>
+
+	<table name="ISSetupTypeFeatures">
+		<col key="yes" def="s38">ISSetupType_</col>
+		<col key="yes" def="s38">Feature_</col>
+	</table>
+
+	<table name="ISStorages">
+		<col key="yes" def="s72">Name</col>
+		<col def="S0">ISBuildSourcePath</col>
+	</table>
+
+	<table name="ISString">
+		<col key="yes" def="s255">ISString</col>
+		<col key="yes" def="s50">ISLanguage_</col>
+		<col def="S0">Value</col>
+		<col def="I2">Encoded</col>
+		<col def="S0">Comment</col>
+		<col def="I4">TimeStamp</col>
+		<row><td>COMPANY_NAME</td><td>1033</td><td>Apache Software Foundation</td><td>0</td><td/><td>2115503849</td></row>
+		<row><td>DN_AlwaysInstall</td><td>1033</td><td>Always Install</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDPROP_EXPRESS_LAUNCH_CONDITION_COLOR</td><td>1033</td><td>The color settings of your system are not adequate for running [ProductName].</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDPROP_EXPRESS_LAUNCH_CONDITION_OS</td><td>1033</td><td>[ProductName] requires that your computer is running Windows NT 4.0 or Windows 2000 or Windows XP or Windows 2003 Server</td><td>0</td><td/><td>2115550953</td></row>
+		<row><td>IDPROP_EXPRESS_LAUNCH_CONDITION_PROCESSOR</td><td>1033</td><td>The processor is not adequate for running [ProductName].</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDPROP_EXPRESS_LAUNCH_CONDITION_RAM</td><td>1033</td><td>The amount of RAM is not adequate for running [ProductName].</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDPROP_EXPRESS_LAUNCH_CONDITION_SCREEN</td><td>1033</td><td>The screen resolution is not adequate for running [ProductName].</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDPROP_SETUPTYPE_COMPACT</td><td>1033</td><td>Compact</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDPROP_SETUPTYPE_COMPACT_DESC</td><td>1033</td><td>Compact Description</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDPROP_SETUPTYPE_COMPLETE</td><td>1033</td><td>Complete</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDPROP_SETUPTYPE_COMPLETE_DESC</td><td>1033</td><td>Complete</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDPROP_SETUPTYPE_CUSTOM</td><td>1033</td><td>Custom</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDPROP_SETUPTYPE_CUSTOM_DESC</td><td>1033</td><td>Custom Description</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDPROP_SETUPTYPE_CUSTOM_DESC_PRO</td><td>1033</td><td>Custom</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDPROP_SETUPTYPE_TYPICAL</td><td>1033</td><td>Typical</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDPROP_SETUPTYPE_TYPICAL_DESC</td><td>1033</td><td>Typical Description</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_1</td><td>1033</td><td>[1]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_1b</td><td>1033</td><td>[1]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_1c</td><td>1033</td><td>[1]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_1d</td><td>1033</td><td>[1]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_Advertising</td><td>1033</td><td>Advertising application</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_AllocatingRegistry</td><td>1033</td><td>Allocating registry space</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_AppCommandLine</td><td>1033</td><td>Application: [1], Command line: [2]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_AppId</td><td>1033</td><td>AppId: [1]{{, AppType: [2]}}</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_AppIdAppTypeRSN</td><td>1033</td><td>AppId: [1]{{, AppType: [2], Users: [3], RSN: [4]}}</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_Application</td><td>1033</td><td>Application: [1]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_BindingExes</td><td>1033</td><td>Binding executables</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_ClassId</td><td>1033</td><td>Class ID: [1]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_ClsID</td><td>1033</td><td>Class ID: [1]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_ComponentIDQualifier</td><td>1033</td><td>Component ID: [1], Qualifier: [2]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_ComponentIdQualifier2</td><td>1033</td><td>Component ID: [1], Qualifier: [2]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_ComputingSpace</td><td>1033</td><td>Computing space requirements</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_ComputingSpace2</td><td>1033</td><td>Computing space requirements</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_ComputingSpace3</td><td>1033</td><td>Computing space requirements</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_ContentTypeExtension</td><td>1033</td><td>MIME Content Type: [1], Extension: [2]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_ContentTypeExtension2</td><td>1033</td><td>MIME Content Type: [1], Extension: [2]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_CopyingNetworkFiles</td><td>1033</td><td>Copying files to the network</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_CopyingNewFiles</td><td>1033</td><td>Copying new files</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_CreatingDuplicate</td><td>1033</td><td>Creating duplicate files</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_CreatingFolders</td><td>1033</td><td>Creating folders</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_CreatingIISRoots</td><td>1033</td><td>Creating IIS Virtual Roots...</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_CreatingShortcuts</td><td>1033</td><td>Creating shortcuts</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_DeletingServices</td><td>1033</td><td>Deleting services</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_EnvironmentStrings</td><td>1033</td><td>Updating environment strings</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_EvaluateLaunchConditions</td><td>1033</td><td>Evaluating launch conditions</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_Extension</td><td>1033</td><td>Extension: [1]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_Extension2</td><td>1033</td><td>Extension: [1]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_Feature</td><td>1033</td><td>Feature: [1]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_FeatureColon</td><td>1033</td><td>Feature: [1]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_File</td><td>1033</td><td>File: [1]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_File2</td><td>1033</td><td>File: [1]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_FileDependencies</td><td>1033</td><td>File: [1],  Dependencies: [2]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_FileDir</td><td>1033</td><td>File: [1], Directory: [9]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_FileDir2</td><td>1033</td><td>File: [1], Directory: [9]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_FileDir3</td><td>1033</td><td>File: [1], Directory: [9]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_FileDirSize</td><td>1033</td><td>File: [1], Directory: [9], Size: [6]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_FileDirSize2</td><td>1033</td><td>File: [1],  Directory: [9],  Size: [6]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_FileDirSize3</td><td>1033</td><td>File: [1],  Directory: [9],  Size: [6]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_FileDirSize4</td><td>1033</td><td>File: [1],  Directory: [2],  Size: [3]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_FileDirectorySize</td><td>1033</td><td>File: [1],  Directory: [9],  Size: [6]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_FileFolder</td><td>1033</td><td>File: [1], Folder: [2]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_FileFolder2</td><td>1033</td><td>File: [1], Folder: [2]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_FileSectionKeyValue</td><td>1033</td><td>File: [1],  Section: [2],  Key: [3], Value: [4]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_FileSectionKeyValue2</td><td>1033</td><td>File: [1],  Section: [2],  Key: [3], Value: [4]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_Folder</td><td>1033</td><td>Folder: [1]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_Folder1</td><td>1033</td><td>Folder: [1]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_Font</td><td>1033</td><td>Font: [1]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_Font2</td><td>1033</td><td>Font: [1]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_FoundApp</td><td>1033</td><td>Found application: [1]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_FreeSpace</td><td>1033</td><td>Free space: [1]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_GeneratingScript</td><td>1033</td><td>Generating script operations for action:</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_InitializeODBCDirs</td><td>1033</td><td>Initializing ODBC directories</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_InstallODBC</td><td>1033</td><td>Installing ODBC components</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_InstallServices</td><td>1033</td><td>Installing new services</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_InstallingSystemCatalog</td><td>1033</td><td>Installing system catalog</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_KeyName</td><td>1033</td><td>Key: [1], Name: [2]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_KeyNameValue</td><td>1033</td><td>Key: [1], Name: [2], Value: [3]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_LibId</td><td>1033</td><td>LibID: [1]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_Libid2</td><td>1033</td><td>LibID: [1]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_MigratingFeatureStates</td><td>1033</td><td>Migrating feature states from related applications</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_MovingFiles</td><td>1033</td><td>Moving files</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_NameValueAction</td><td>1033</td><td>Name: [1], Value: [2], Action [3]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_NameValueAction2</td><td>1033</td><td>Name: [1], Value: [2], Action [3]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_PatchingFiles</td><td>1033</td><td>Patching files</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_ProgID</td><td>1033</td><td>ProgID: [1]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_ProgID2</td><td>1033</td><td>ProgID: [1]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_PropertySignature</td><td>1033</td><td>Property: [1], Signature: [2]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_PublishProductFeatures</td><td>1033</td><td>Publishing product features</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_PublishProductInfo</td><td>1033</td><td>Publishing product information</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_PublishingQualifiedComponents</td><td>1033</td><td>Publishing qualified components</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_RegUser</td><td>1033</td><td>Registering user</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_RegisterClassServer</td><td>1033</td><td>Registering class servers</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_RegisterExtensionServers</td><td>1033</td><td>Registering extension servers</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_RegisterFonts</td><td>1033</td><td>Registering fonts</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_RegisterMimeInfo</td><td>1033</td><td>Registering MIME info</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_RegisterTypeLibs</td><td>1033</td><td>Registering type libraries</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_RegisteringComPlus</td><td>1033</td><td>Registering COM+ Applications and Components</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_RegisteringModules</td><td>1033</td><td>Registering modules</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_RegisteringProduct</td><td>1033</td><td>Registering product</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_RegisteringProgIdentifiers</td><td>1033</td><td>Registering program identifiers</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_RemoveApps</td><td>1033</td><td>Removing applications</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_RemovingBackup</td><td>1033</td><td>Removing backup files</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_RemovingDuplicates</td><td>1033</td><td>Removing duplicated files</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_RemovingFiles</td><td>1033</td><td>Removing files</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_RemovingFolders</td><td>1033</td><td>Removing folders</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_RemovingIISRoots</td><td>1033</td><td>Removing IIS Virtual Roots...</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_RemovingIni</td><td>1033</td><td>Removing INI file entries</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_RemovingMoved</td><td>1033</td><td>Removing moved files</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_RemovingODBC</td><td>1033</td><td>Removing ODBC components</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_RemovingRegistry</td><td>1033</td><td>Removing system registry values</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_RemovingShortcuts</td><td>1033</td><td>Removing shortcuts</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_RollingBack</td><td>1033</td><td>Rolling back action:</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_SearchForRelated</td><td>1033</td><td>Searching for related applications</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_SearchInstalled</td><td>1033</td><td>Searching for installed applications</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_SearchingQualifyingProducts</td><td>1033</td><td>Searching for qualifying products</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_SearchingQualifyingProducts2</td><td>1033</td><td>Searching for qualifying products</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_Service</td><td>1033</td><td>Service: [1]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_Service2</td><td>1033</td><td>Service: [2]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_Service3</td><td>1033</td><td>Service: [1]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_Service4</td><td>1033</td><td>Service: [1]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_Shortcut</td><td>1033</td><td>Shortcut: [1]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_Shortcut1</td><td>1033</td><td>Shortcut: [1]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_StartingServices</td><td>1033</td><td>Starting services</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_StoppingServices</td><td>1033</td><td>Stopping services</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_UnpublishProductFeatures</td><td>1033</td><td>Unpublishing product features</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_UnpublishQualified</td><td>1033</td><td>Unpublishing Qualified Components</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_UnpublishingProductInfo</td><td>1033</td><td>Unpublishing product information</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_UnregTypeLibs</td><td>1033</td><td>Unregistering type libraries</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_UnregisterClassServers</td><td>1033</td><td>Unregister class servers</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_UnregisterExtensionServers</td><td>1033</td><td>Unregistering extension servers</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_UnregisterModules</td><td>1033</td><td>Unregistering modules</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_UnregisteringComPlus</td><td>1033</td><td>Unregistering COM+ Applications and Components</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_UnregisteringFonts</td><td>1033</td><td>Unregistering fonts</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_UnregisteringMimeInfo</td><td>1033</td><td>Unregistering MIME info</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_UnregisteringProgramIds</td><td>1033</td><td>Unregistering program identifiers</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_UpdateComponentRegistration</td><td>1033</td><td>Updating component registration</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_UpdateEnvironmentStrings</td><td>1033</td><td>Updating environment strings</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_Validating</td><td>1033</td><td>Validating install</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_WritingINI</td><td>1033</td><td>Writing INI file values</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ACTIONTEXT_WritingRegistry</td><td>1033</td><td>Writing system registry values</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_BACK</td><td>1033</td><td>&lt; &amp;Back</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_CANCEL</td><td>1033</td><td>Cancel</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_CANCEL2</td><td>1033</td><td>&amp;Cancel</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_CHANGE</td><td>1033</td><td>&amp;Change...</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_COMPLUS_PROGRESSTEXT_COST</td><td>1033</td><td>Costing COM+ application: [1]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_COMPLUS_PROGRESSTEXT_INSTALL</td><td>1033</td><td>Installing COM+ application: [1]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_COMPLUS_PROGRESSTEXT_UNINSTALL</td><td>1033</td><td>Uninstalling COM+ application: [1]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_DatabaseFolder_InstallDatabaseTo</td><td>1033</td><td>Install [ProductName] database to:</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_0</td><td>1033</td><td>{{Fatal error: }}</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_1</td><td>1033</td><td>Error [1].</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_10</td><td>1033</td><td>=== Logging started: [Date]  [Time] ===</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_100</td><td>1033</td><td>Could not remove shortcut [2]. Verify that the shortcut file exists and that you can access it.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_101</td><td>1033</td><td>Could not register type library for file [2].  Contact your support personnel.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_102</td><td>1033</td><td>Could not unregister type library for file [2].  Contact your support personnel.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_103</td><td>1033</td><td>Could not update the INI file [2][3].  Verify that the file exists and that you can access it.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_104</td><td>1033</td><td>Could not schedule file [2] to replace file [3] on reboot.  Verify that you have write permissions to file [3].</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_105</td><td>1033</td><td>Error removing ODBC driver manager, ODBC error [2]: [3]. Contact your support personnel.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_106</td><td>1033</td><td>Error installing ODBC driver manager, ODBC error [2]: [3]. Contact your support personnel.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_107</td><td>1033</td><td>Error removing ODBC driver [4], ODBC error [2]: [3]. Verify that you have sufficient privileges to remove ODBC drivers.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_108</td><td>1033</td><td>Error installing ODBC driver [4], ODBC error [2]: [3]. Verify that the file [4] exists and that you can access it.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_109</td><td>1033</td><td>Error configuring ODBC data source [4], ODBC error [2]: [3]. Verify that the file [4] exists and that you can access it.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_11</td><td>1033</td><td>=== Logging stopped: [Date]  [Time] ===</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_110</td><td>1033</td><td>Service [2] ([3]) failed to start.  Verify that you have sufficient privileges to start system services.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_111</td><td>1033</td><td>Service [2] ([3]) could not be stopped.  Verify that you have sufficient privileges to stop system services.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_112</td><td>1033</td><td>Service [2] ([3]) could not be deleted.  Verify that you have sufficient privileges to remove system services.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_113</td><td>1033</td><td>Service [2] ([3]) could not be installed.  Verify that you have sufficient privileges to install system services.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_114</td><td>1033</td><td>Could not update environment variable [2].  Verify that you have sufficient privileges to modify environment variables.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_115</td><td>1033</td><td>You do not have sufficient privileges to complete this installation for all users of the machine.  Log on as an administrator and then retry this installation.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_116</td><td>1033</td><td>Could not set file security for file [3]. Error: [2].  Verify that you have sufficient privileges to modify the security permissions for this file.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_117</td><td>1033</td><td>Component Services (COM+ 1.0) are not installed on this computer.  This installation requires Component Services in order to complete successfully.  Component Services are available on Windows 2000.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_118</td><td>1033</td><td>Error registering COM+ application.  Contact your support personnel for more information.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_119</td><td>1033</td><td>Error unregistering COM+ application.  Contact your support personnel for more information.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_12</td><td>1033</td><td>Action start [Time]: [1].</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_120</td><td>1033</td><td>Removing older versions of this application</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_121</td><td>1033</td><td>Preparing to remove older versions of this application</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_122</td><td>1033</td><td>Error applying patch to file [2].  It has probably been updated by other means, and can no longer be modified by this patch.  For more information contact your patch vendor.  {{System Error: [3]}}</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_123</td><td>1033</td><td>[2] cannot install one of its required products. Contact your technical support group.  {{System Error: [3].}}</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_124</td><td>1033</td><td>The older version of [2] cannot be removed.  Contact your technical support group.  {{System Error [3].}}</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_125</td><td>1033</td><td>The description for service '[2]' ([3]) could not be changed.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_126</td><td>1033</td><td>The Windows Installer service cannot update the system file [2] because the file is protected by Windows.  You may need to update your operating system for this program to work correctly. {{Package version: [3], OS Protected version: [4]}}</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_127</td><td>1033</td><td>The Windows Installer service cannot update the protected Windows file [2]. {{Package version: [3], OS Protected version: [4], SFP Error: [5]}}</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_128</td><td>1033</td><td>The Windows Installer service cannot update one or more protected Windows files. SFP Error: [2]. List of protected files: [3]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_129</td><td>1033</td><td>User installations are disabled via policy on the machine.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_13</td><td>1033</td><td>Action ended [Time]: [1]. Return value [2].</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_130</td><td>1033</td><td>This setup requires Internet Information Server 4.0 or higher for configuring IIS Virtual Roots. Please make sure that you have IIS 4.0 or higher.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_131</td><td>1033</td><td>This setup requires Administrator privileges for configuring IIS Virtual Roots.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_14</td><td>1033</td><td>Time remaining: {[1] minutes }{[2] seconds}</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_15</td><td>1033</td><td>Out of memory. Shut down other applications before retrying.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_16</td><td>1033</td><td>Installer is no longer responding.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_17</td><td>1033</td><td>Installer terminated prematurely.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_18</td><td>1033</td><td>Please wait while Windows configures [ProductName]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_19</td><td>1033</td><td>Gathering required information...</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_2</td><td>1033</td><td>Warning [1].</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_20</td><td>1033</td><td>{[ProductName] }Setup completed successfully.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_21</td><td>1033</td><td>{[ProductName] }Setup failed.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_22</td><td>1033</td><td>Error reading from file: [2]. {{ System error [3].}}  Verify that the file exists and that you can access it.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_23</td><td>1033</td><td>Cannot create the file [3].  A directory with this name already exists.  Cancel the installation and try installing to a different location.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_2331</td><td>1033</td><td>Error loading library [2] or finding entry point [3].</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_24</td><td>1033</td><td>Please insert the disk: [2]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_25</td><td>1033</td><td>The installer has insufficient privileges to access this directory: [2].  The installation cannot continue.  Log on as an administrator or contact your system administrator.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_26</td><td>1033</td><td>Error writing to file [2].  Verify that you have access to that directory.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27</td><td>1033</td><td>Error reading from file [2].  Verify that the file exists and that you can access it.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27502</td><td>1033</td><td>Could not connect to [2] '[3]'. [4]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27503</td><td>1033</td><td>Error retrieving version string from [2] '[3]'. [4]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27504</td><td>1033</td><td>SQL version requirements not met: [3]. This installation requires [2] [4] or later.</td><td>0</td><td/><td/></row>
+		<row><td>IDS_ERROR_27505</td><td>1033</td><td>Could not open SQL script file [2].</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27506</td><td>1033</td><td>Error executing SQL script [2]. Line [3]. [4]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27507</td><td>1033</td><td>Connection or browsing for database servers requires that MDAC be installed.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27508</td><td>1033</td><td>Error installing COM+ application [2]. [3]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27509</td><td>1033</td><td>Error uninstalling COM+ application [2]. [3]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27510</td><td>1033</td><td>Error installing COM+ application [2].  Could not load Microsoft(R) .NET class libraries. Registering .NET serviced components requires that Microsoft(R) .NET Framework be installed.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27511</td><td>1033</td><td>Could not execute SQL script file [2]. Connection not open: [3]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27512</td><td>1033</td><td>Error beginning transactions for [2] '[3]'. Database [4]. [5]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27513</td><td>1033</td><td>Error committing transactions for [2] '[3]'. Database [4]. [5]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27514</td><td>1033</td><td>This installation requires a Microsoft SQL Server. The specified server '[3]' is a Microsoft SQL Server Desktop Engine (MSDE).</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27515</td><td>1033</td><td>Error retrieving schema version from [2] '[3]'. Database: '[4]'. [5]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27516</td><td>1033</td><td>Error writing schema version to [2] '[3]'. Database: '[4]'. [5]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27517</td><td>1033</td><td>This installation requires Administrator privileges for installing COM+ applications. Log on as an administrator and then retry this installation.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27518</td><td>1033</td><td>The COM+ application "[2]" is configured to run as an NT service; this requires COM+ 1.5 or later on the system. Since your system has COM+ 1.0, this application will not be installed.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27519</td><td>1033</td><td>Error updating XML file [2]. [3]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27520</td><td>1033</td><td>Error opening XML file [2]. [3]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27521</td><td>1033</td><td>This setup requires MSXML 3.0 or higher for configuring XML files. Please make sure that you have version 3.0 or higher.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27522</td><td>1033</td><td>Error creating XML file [2]. [3]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27523</td><td>1033</td><td>Error loading servers.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27524</td><td>1033</td><td>Error loading NetApi32.DLL. The ISNetApi.dll needs to have NetApi32.DLL properly loaded and requires an NT based operating system.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27525</td><td>1033</td><td>Server not found. Verify that the specified server exists. The server name can not be empty.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27526</td><td>1033</td><td>Unspecified error from ISNetApi.dll.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27527</td><td>1033</td><td>The buffer is too small.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27528</td><td>1033</td><td>Access denied. Check administrative rights.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27529</td><td>1033</td><td>Invalid computer.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27530</td><td>1033</td><td>Undefined switch case.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27531</td><td>1033</td><td>Unhandled exception.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27532</td><td>1033</td><td>Invalid user name for this server or domain.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27533</td><td>1033</td><td>The case-sensitive passwords do not match.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27534</td><td>1033</td><td>The list is empty.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27535</td><td>1033</td><td>Access violation.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27536</td><td>1033</td><td>Error getting group.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27537</td><td>1033</td><td>Error adding user to group. Verify that the group exists for this domain or server.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27538</td><td>1033</td><td>Error creating user.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27539</td><td>1033</td><td>ERROR_NETAPI_ERROR_NOT_PRIMARY returned from NetAPI.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27540</td><td>1033</td><td>The specified user already exists.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27541</td><td>1033</td><td>The specified group already exists.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27542</td><td>1033</td><td>Invalid password. Verify that the password is in accordance with your network password policy.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27543</td><td>1033</td><td>Invalid name.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27544</td><td>1033</td><td>Invalid group.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27545</td><td>1033</td><td>The user name can not be empty and must be in the format DOMAIN\Username.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27546</td><td>1033</td><td>Error loading or creating INI file in the user TEMP directory.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27547</td><td>1033</td><td>ISNetAPI.dll is not loaded or there was an error loading the dll. This dll needs to be loaded for this operation. Verify that the dll is in the SUPPORTDIR directory.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27548</td><td>1033</td><td>Error deleting INI file containing new user information from the user's TEMP directory.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27549</td><td>1033</td><td>Error getting the primary domain controller (PDC).</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27550</td><td>1033</td><td>Every field must have a value in order to create a user.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27551</td><td>1033</td><td>ODBC driver for [2] not found. This is required to connect to [2] database servers.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_27552</td><td>1033</td><td>Error creating database [4]. Server: [2] [3]. [5]</td><td>0</td><td/><td/></row>
+		<row><td>IDS_ERROR_27553</td><td>1033</td><td>Error connecting to database [4]. Server: [2] [3]. [5]</td><td>0</td><td/><td/></row>
+		<row><td>IDS_ERROR_27554</td><td>1033</td><td>Error attempting to open connection [2]. No valid database metadata associated with this connection.</td><td>0</td><td/><td/></row>
+		<row><td>IDS_ERROR_28</td><td>1033</td><td>Another application has exclusive access to the file [2].  Please shut down all other applications, then click Retry.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_29</td><td>1033</td><td>There is not enough disk space to install the file [2].  Free some disk space and click Retry, or click Cancel to exit.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_3</td><td>1033</td><td>Info [1].</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_30</td><td>1033</td><td>Source file not found: [2].  Verify that the file exists and that you can access it.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_31</td><td>1033</td><td>Error reading from file: [3]. {{ System error [2].}}  Verify that the file exists and that you can access it.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_32</td><td>1033</td><td>Error writing to file: [3]. {{ System error [2].}}  Verify that you have access to that directory.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_33</td><td>1033</td><td>Source file not found{{(cabinet)}}: [2].  Verify that the file exists and that you can access it.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_34</td><td>1033</td><td>Cannot create the directory [2].  A file with this name already exists.  Please rename or remove the file and click Retry, or click Cancel to exit.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_35</td><td>1033</td><td>The volume [2] is currently unavailable.  Please select another.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_36</td><td>1033</td><td>The specified path [2] is unavailable.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_37</td><td>1033</td><td>Unable to write to the specified folder [2].</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_38</td><td>1033</td><td>A network error occurred while attempting to read from the file [2]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_39</td><td>1033</td><td>An error occurred while attempting to create the directory [2]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_4</td><td>1033</td><td>Internal Error [1]. [2]{, [3]}{, [4]}</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_40</td><td>1033</td><td>A network error occurred while attempting to create the directory [2]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_41</td><td>1033</td><td>A network error occurred while attempting to open the source file cabinet [2].</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_42</td><td>1033</td><td>The specified path is too long [2].</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_43</td><td>1033</td><td>The Installer has insufficient privileges to modify the file [2].</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_44</td><td>1033</td><td>A portion of the path [2] exceeds the length allowed by the system.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_45</td><td>1033</td><td>The path [2] contains words that are not valid in folders.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_46</td><td>1033</td><td>The path [2] contains an invalid character.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_47</td><td>1033</td><td>[2] is not a valid short file name.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_48</td><td>1033</td><td>Error getting file security: [3] GetLastError: [2]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_49</td><td>1033</td><td>Invalid Drive: [2]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_5</td><td>1033</td><td>{{Disk full: }}</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_50</td><td>1033</td><td>Could not create key [2]. {{ System error [3].}}  Verify that you have sufficient access to that key, or contact your support personnel.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_51</td><td>1033</td><td>Could not open key: [2]. {{ System error [3].}}  Verify that you have sufficient access to that key, or contact your support personnel.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_52</td><td>1033</td><td>Could not delete value [2] from key [3]. {{ System error [4].}}  Verify that you have sufficient access to that key, or contact your support personnel.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_53</td><td>1033</td><td>Could not delete key [2]. {{ System error [3].}}  Verify that you have sufficient access to that key, or contact your support personnel.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_54</td><td>1033</td><td>Could not read value [2] from key [3]. {{ System error [4].}}  Verify that you have sufficient access to that key, or contact your support personnel.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_55</td><td>1033</td><td>Could not write value [2] to key [3]. {{ System error [4].}}  Verify that you have sufficient access to that key, or contact your support personnel.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_56</td><td>1033</td><td>Could not get value names for key [2]. {{ System error [3].}}  Verify that you have sufficient access to that key, or contact your support personnel.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_57</td><td>1033</td><td>Could not get sub key names for key [2]. {{ System error [3].}}  Verify that you have sufficient access to that key, or contact your support personnel.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_58</td><td>1033</td><td>Could not read security information for key [2]. {{ System error [3].}}  Verify that you have sufficient access to that key, or contact your support personnel.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_59</td><td>1033</td><td>Could not increase the available registry space. [2] KB of free registry space is required for the installation of this application.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_6</td><td>1033</td><td>Action [Time]: [1]. [2]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_60</td><td>1033</td><td>Another installation is in progress. You must complete that installation before continuing this one.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_61</td><td>1033</td><td>Error accessing secured data. Please make sure the Windows Installer is configured properly and try the installation again.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_62</td><td>1033</td><td>User [2] has previously initiated an installation for product [3].  That user will need to run that installation again before using that product.  Your current installation will now continue.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_63</td><td>1033</td><td>User [2] has previously initiated an installation for product [3].  That user will need to run that installation again before using that product.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_64</td><td>1033</td><td>Out of disk space -- Volume: '[2]'; required space: [3] KB; available space: [4] KB.  Free some disk space and retry.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_65</td><td>1033</td><td>Are you sure you want to cancel?</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_66</td><td>1033</td><td>The file [2][3] is being held in use{ by the following process: Name: [4], ID: [5], Window Title: [6]}.  Close that application and retry.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_67</td><td>1033</td><td>The product [2] is already installed, preventing the installation of this product.  The two products are incompatible.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_68</td><td>1033</td><td>Out of disk space -- Volume: [2]; required space: [3] KB; available space: [4] KB.  If rollback is disabled, enough space is available. Click Cancel to quit, Retry to check available disk space again, or Ignore to continue without rollback.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_69</td><td>1033</td><td>Could not access network location [2].</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_7</td><td>1033</td><td>[ProductName]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_70</td><td>1033</td><td>The following applications should be closed before continuing the installation:</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_71</td><td>1033</td><td>Could not find any previously installed compliant products on the machine for installing this product.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_72</td><td>1033</td><td>The key [2] is not valid.  Verify that you entered the correct key.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_73</td><td>1033</td><td>The installer must restart your system before configuration of [2] can continue.  Click Yes to restart now or No if you plan to restart later.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_74</td><td>1033</td><td>You must restart your system for the configuration changes made to [2] to take effect. Click Yes to restart now or No if you plan to restart later.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_75</td><td>1033</td><td>An installation for [2] is currently suspended.  You must undo the changes made by that installation to continue.  Do you want to undo those changes?</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_76</td><td>1033</td><td>A previous installation for this product is in progress.  You must undo the changes made by that installation to continue.  Do you want to undo those changes?</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_77</td><td>1033</td><td>No valid source could be found for product [2].  The Windows Installer cannot continue.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_78</td><td>1033</td><td>Installation operation completed successfully.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_79</td><td>1033</td><td>Installation operation failed.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_8</td><td>1033</td><td>{[2]}{, [3]}{, [4]}</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_80</td><td>1033</td><td>Product: [2] -- [3]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_81</td><td>1033</td><td>You may either restore your computer to its previous state or continue the installation later. Would you like to restore?</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_82</td><td>1033</td><td>An error occurred while writing installation information to disk.  Check to make sure enough disk space is available, and click Retry, or Cancel to end the installation.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_83</td><td>1033</td><td>One or more of the files required to restore your computer to its previous state could not be found.  Restoration will not be possible.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_84</td><td>1033</td><td>The path [2] is not valid.  Please specify a valid path.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_85</td><td>1033</td><td>Out of memory. Shut down other applications before retrying.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_86</td><td>1033</td><td>There is no disk in drive [2]. Please insert one and click Retry, or click Cancel to go back to the previously selected volume.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_87</td><td>1033</td><td>There is no disk in drive [2]. Please insert one and click Retry, or click Cancel to return to the browse dialog and select a different volume.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_88</td><td>1033</td><td>The folder [2] does not exist.  Please enter a path to an existing folder.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_89</td><td>1033</td><td>You have insufficient privileges to read this folder.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_9</td><td>1033</td><td>Message type: [1], Argument: [2]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_90</td><td>1033</td><td>A valid destination folder for the installation could not be determined.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_91</td><td>1033</td><td>Error attempting to read from the source installation database: [2].</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_92</td><td>1033</td><td>Scheduling reboot operation: Renaming file [2] to [3]. Must reboot to complete operation.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_93</td><td>1033</td><td>Scheduling reboot operation: Deleting file [2]. Must reboot to complete operation.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_94</td><td>1033</td><td>Module [2] failed to register.  HRESULT [3].  Contact your support personnel.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_95</td><td>1033</td><td>Module [2] failed to unregister.  HRESULT [3].  Contact your support personnel.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_96</td><td>1033</td><td>Failed to cache package [2]. Error: [3]. Contact your support personnel.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_97</td><td>1033</td><td>Could not register font [2].  Verify that you have sufficient permissions to install fonts, and that the system supports this font.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_98</td><td>1033</td><td>Could not unregister font [2]. Verify that you have sufficient permissions to remove fonts.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ERROR_99</td><td>1033</td><td>Could not create shortcut [2]. Verify that the destination folder exists and that you can access it.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_INSTALLDIR</td><td>1033</td><td>[INSTALLDIR]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_INSTALLSHIELD</td><td>1033</td><td>InstallShield</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_INSTALLSHIELD_FORMATTED</td><td>1033</td><td>{&amp;MSSWhiteSerif8}InstallShield</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ISSCRIPT_VERSION_MISSING</td><td>1033</td><td>The InstallScript engine is missing from this machine.  If available, please run ISScript.msi, or contact your support personnel for further assistance.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_ISSCRIPT_VERSION_OLD</td><td>1033</td><td>The InstallScript engine on this machine is older than the version required to run this setup.  If available, please install the latest version of ISScript.msi, or contact your support personnel for further assistance.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_NEXT</td><td>1033</td><td>&amp;Next &gt;</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_OK</td><td>1033</td><td>OK</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_PRINT_BUTTON</td><td>1033</td><td>&amp;Print</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_PRODUCTNAME_INSTALLSHIELD</td><td>1033</td><td>[ProductName] - InstallShield Wizard</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_PROGMSG_IIS_CREATEAPPPOOL</td><td>1033</td><td>Creating application pool %s</td><td>0</td><td/><td/></row>
+		<row><td>IDS_PROGMSG_IIS_CREATEAPPPOOLS</td><td>1033</td><td>Creating application Pools...</td><td>0</td><td/><td/></row>
+		<row><td>IDS_PROGMSG_IIS_CREATEVROOT</td><td>1033</td><td>Creating IIS virtual directory %s</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_PROGMSG_IIS_CREATEVROOTS</td><td>1033</td><td>Creating IIS virtual directories...</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_PROGMSG_IIS_CREATEWEBSERVICEEXTENSION</td><td>1033</td><td>Creating web service extension</td><td>0</td><td/><td/></row>
+		<row><td>IDS_PROGMSG_IIS_CREATEWEBSERVICEEXTENSIONS</td><td>1033</td><td>Creating web service extensions...</td><td>0</td><td/><td/></row>
+		<row><td>IDS_PROGMSG_IIS_EXTRACT</td><td>1033</td><td>Extracting information for IIS virtual directories...</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_PROGMSG_IIS_EXTRACTDONE</td><td>1033</td><td>Extracted information for IIS virtual directories...</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_PROGMSG_IIS_REMOVEAPPPOOL</td><td>1033</td><td>Removing application pool</td><td>0</td><td/><td/></row>
+		<row><td>IDS_PROGMSG_IIS_REMOVEAPPPOOLS</td><td>1033</td><td>Removing application pools...</td><td>0</td><td/><td/></row>
+		<row><td>IDS_PROGMSG_IIS_REMOVESITE</td><td>1033</td><td>Removing web site at port %d</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_PROGMSG_IIS_REMOVEVROOT</td><td>1033</td><td>Removing IIS virtual directory %s</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_PROGMSG_IIS_REMOVEVROOTS</td><td>1033</td><td>Removing IIS virtual directories...</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_PROGMSG_IIS_REMOVEWEBSERVICEEXTENSION</td><td>1033</td><td>Removing web service extension</td><td>0</td><td/><td/></row>
+		<row><td>IDS_PROGMSG_IIS_REMOVEWEBSERVICEEXTENSIONS</td><td>1033</td><td>Removing web service extensions...</td><td>0</td><td/><td/></row>
+		<row><td>IDS_PROGMSG_IIS_ROLLBACKAPPPOOLS</td><td>1033</td><td>Rolling back application pools...</td><td>0</td><td/><td/></row>
+		<row><td>IDS_PROGMSG_IIS_ROLLBACKVROOTS</td><td>1033</td><td>Rolling back virtual directory and web site changes...</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_PROGMSG_IIS_ROLLBACKWEBSERVICEEXTENSIONS</td><td>1033</td><td>Rolling back web service extensions...</td><td>0</td><td/><td/></row>
+		<row><td>IDS_PROGMSG_XML_COSTING</td><td>1033</td><td>Costing XML files...</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_PROGMSG_XML_CREATE_FILE</td><td>1033</td><td>Creating XML file %s...</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_PROGMSG_XML_FILES</td><td>1033</td><td>Performing XML file changes...</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_PROGMSG_XML_REMOVE_FILE</td><td>1033</td><td>Removing XML file %s...</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_PROGMSG_XML_ROLLBACK_FILES</td><td>1033</td><td>Rolling back XML file changes...</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_PROGMSG_XML_UPDATE_FILE</td><td>1033</td><td>Updating XML file %s...</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_SQLBROWSE_INTRO</td><td>1033</td><td>From the list of servers below, select the database server you would like to target.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_SQLLOGIN_BROWSE</td><td>1033</td><td>B&amp;rowse...</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_SQLLOGIN_CONNECT</td><td>1033</td><td>Connect using:</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_SQLLOGIN_DESC</td><td>1033</td><td>Select database server and authentication method</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_SQLLOGIN_ID</td><td>1033</td><td>&amp;Login ID:</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_SQLLOGIN_INTRO</td><td>1033</td><td>Select the database server to install to from the list below or click Browse to see a list of all database servers. You can also specify the way to authenticate your login using your current credentials or a SQL Login ID and Password.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_SQLLOGIN_PSWD</td><td>1033</td><td>&amp;Password:</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_SQLLOGIN_SERVER</td><td>1033</td><td>&amp;Database Server:</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_SQLLOGIN_SQL</td><td>1033</td><td>S&amp;erver authentication using the Login ID and password below</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_SQLLOGIN_TITLE</td><td>1033</td><td>{&amp;MSSansBold8}Database Server</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_SQLLOGIN_WIN</td><td>1033</td><td>&amp;Windows authentication credentials of current user</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_SQLSCRIPT_INSTALLING</td><td>1033</td><td>Executing SQL Install Script...</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_SQLSCRIPT_UNINSTALLING</td><td>1033</td><td>Executing SQL Uninstall Script...</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_STANDARD_USE_SETUPEXE</td><td>1033</td><td>This installation cannot be run by directly launching the MSI package. You must run setup.exe.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_SetupTips_Advertise</td><td>1033</td><td>Will be installed on first use. (Available only if the feature supports this option.)</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_SetupTips_AllInstalledLocal</td><td>1033</td><td>Will be completely installed to the local hard drive.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_SetupTips_CustomSetup</td><td>1033</td><td>{&amp;MSSansBold8}Custom Setup Tips</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_SetupTips_CustomSetupDescription</td><td>1033</td><td>Custom Setup allows you to selectively install program features.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_SetupTips_IconInstallState</td><td>1033</td><td>The icon next to the feature name indicates the install state of the feature. Click the icon to drop down the install state menu for each feature.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_SetupTips_InstallState</td><td>1033</td><td>This install state means the feature...</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_SetupTips_Network</td><td>1033</td><td>Will be installed to run from the network. (Available only if the feature supports this option.)</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_SetupTips_OK</td><td>1033</td><td>OK</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_SetupTips_SubFeaturesInstalledLocal</td><td>1033</td><td>Will have some subfeatures installed to the local hard drive. (Available only if the feature has subfeatures.)</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_SetupTips_WillNotBeInstalled</td><td>1033</td><td>Will not be installed.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_Available</td><td>1033</td><td>Available</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_Bytes</td><td>1033</td><td>bytes</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_CompilingFeaturesCost</td><td>1033</td><td>Compiling cost for this feature...</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_Differences</td><td>1033</td><td>Differences</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_DiskSize</td><td>1033</td><td>Disk Size</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureCompletelyRemoved</td><td>1033</td><td>This feature will be completely removed.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureContinueNetwork</td><td>1033</td><td>This feature will continue to be run from the network</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureFreeSpace</td><td>1033</td><td>This feature frees up [1] on your hard drive.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureInstalledCD</td><td>1033</td><td>This feature, and all subfeatures, will be installed to run from the CD.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureInstalledCD2</td><td>1033</td><td>This feature will be installed to run from CD.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureInstalledLocal</td><td>1033</td><td>This feature, and all subfeatures, will be installed on local hard drive.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureInstalledLocal2</td><td>1033</td><td>This feature will be installed on local hard drive.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureInstalledNetwork</td><td>1033</td><td>This feature, and all subfeatures, will be installed to run from the network.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureInstalledNetwork2</td><td>1033</td><td>This feature will be installed to run from network.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureInstalledRequired</td><td>1033</td><td>Will be installed when required.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureInstalledWhenRequired</td><td>1033</td><td>This feature will be set to be installed when required.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureInstalledWhenRequired2</td><td>1033</td><td>This feature will be installed when required.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureLocal</td><td>1033</td><td>This feature will be installed on the local hard drive.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureLocal2</td><td>1033</td><td>This feature will be installed on your local hard drive.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureNetwork</td><td>1033</td><td>This feature will be installed to run from the network.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureNetwork2</td><td>1033</td><td>This feature will be available to run from the network.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureNotAvailable</td><td>1033</td><td>This feature will not be available.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureOnCD</td><td>1033</td><td>This feature will be installed to run from CD.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureOnCD2</td><td>1033</td><td>This feature will be available to run from CD.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureRemainLocal</td><td>1033</td><td>This feature will remain on your local hard drive.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureRemoveNetwork</td><td>1033</td><td>This feature will be removed from your local hard drive, but will be still available to run from the network.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureRemovedCD</td><td>1033</td><td>This feature will be removed from your local hard drive but will still be available to run from CD.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureRemovedUnlessRequired</td><td>1033</td><td>This feature will be removed from your local hard drive but will be set to be installed when required.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureRequiredSpace</td><td>1033</td><td>This feature requires [1] on your hard drive.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureRunFromCD</td><td>1033</td><td>This feature will continue to be run from the CD</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureSpaceFree</td><td>1033</td><td>This feature frees up [1] on your hard drive. It has [2] of [3] subfeatures selected. The subfeatures free up [4] on your hard drive.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureSpaceFree2</td><td>1033</td><td>This feature frees up [1] on your hard drive. It has [2] of [3] subfeatures selected. The subfeatures require [4] on your hard drive.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureSpaceFree3</td><td>1033</td><td>This feature requires [1] on your hard drive. It has [2] of [3] subfeatures selected. The subfeatures free up [4] on your hard drive.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureSpaceFree4</td><td>1033</td><td>This feature requires [1] on your hard drive. It has [2] of [3] subfeatures selected. The subfeatures require [4] on your hard drive.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureUnavailable</td><td>1033</td><td>This feature will become unavailable.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureUninstallNoNetwork</td><td>1033</td><td>This feature will be uninstalled completely, and you won't be able to run it from the network.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureWasCD</td><td>1033</td><td>This feature was run from the CD but will be set to be installed when required.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureWasCDLocal</td><td>1033</td><td>This feature was run from the CD but will be installed on the local hard drive.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureWasOnNetworkInstalled</td><td>1033</td><td>This feature was run from the network but will be installed when required.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureWasOnNetworkLocal</td><td>1033</td><td>This feature was run from the network but will be installed on the local hard drive.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_FeatureWillBeUninstalled</td><td>1033</td><td>This feature will be uninstalled completely, and you won't be able to run it from CD.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_Folder</td><td>1033</td><td>Fldr|New Folder</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_GB</td><td>1033</td><td>GB</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_KB</td><td>1033</td><td>KB</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_MB</td><td>1033</td><td>MB</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_Required</td><td>1033</td><td>Required</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_TimeRemaining</td><td>1033</td><td>Time remaining: {[1] min }[2] sec</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS_UITEXT_Volume</td><td>1033</td><td>Volume</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__AgreeToLicense_0</td><td>1033</td><td>I &amp;do not accept the terms in the license agreement</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__AgreeToLicense_1</td><td>1033</td><td>I &amp;accept the terms in the license agreement</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__DatabaseFolder_ChangeFolder</td><td>1033</td><td>Click Next to install to this folder, or click Change to install to a different folder.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__DatabaseFolder_DatabaseDir</td><td>1033</td><td>[DATABASEDIR]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__DatabaseFolder_DatabaseFolder</td><td>1033</td><td>{&amp;MSSansBold8}Database Folder</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__DestinationFolder_Change</td><td>1033</td><td>&amp;Change...</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__DestinationFolder_ChangeFolder</td><td>1033</td><td>Click Next to install to this folder, or click Change to install to a different folder.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__DestinationFolder_DestinationFolder</td><td>1033</td><td>{&amp;MSSansBold8}Destination Folder</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__DestinationFolder_InstallTo</td><td>1033</td><td>Install [ProductName] to:</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__DisplayName_Custom</td><td>1033</td><td>Custom</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__DisplayName_Minimal</td><td>1033</td><td>Minimal</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__DisplayName_Typical</td><td>1033</td><td>Typical</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsAdminInstallBrowse_11</td><td>1033</td><td/><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsAdminInstallBrowse_4</td><td>1033</td><td/><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsAdminInstallBrowse_8</td><td>1033</td><td/><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsAdminInstallBrowse_BrowseDestination</td><td>1033</td><td>Browse to the destination folder.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsAdminInstallBrowse_ChangeDestination</td><td>1033</td><td>{&amp;MSSansBold8}Change Current Destination Folder</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsAdminInstallBrowse_CreateFolder</td><td>1033</td><td>Create new folder|</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsAdminInstallBrowse_FolderName</td><td>1033</td><td>&amp;Folder name:</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsAdminInstallBrowse_LookIn</td><td>1033</td><td>&amp;Look in:</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsAdminInstallBrowse_UpOneLevel</td><td>1033</td><td>Up one level|</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsAdminInstallPointWelcome_ServerImage</td><td>1033</td><td>The InstallShield(R) Wizard will create a server image of [ProductName] at a specified network location. To continue, click Next.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsAdminInstallPointWelcome_Wizard</td><td>1033</td><td>{&amp;TahomaBold10}Welcome to the InstallShield Wizard for [ProductName]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsAdminInstallPoint_Change</td><td>1033</td><td>&amp;Change...</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsAdminInstallPoint_EnterNetworkLocation</td><td>1033</td><td>Enter the network location or click Change to browse to a location.  Click Install to create a server image of [ProductName] at the specified network location or click Cancel to exit the wizard.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsAdminInstallPoint_Install</td><td>1033</td><td>&amp;Install</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsAdminInstallPoint_NetworkLocation</td><td>1033</td><td>&amp;Network location:</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsAdminInstallPoint_NetworkLocationFormatted</td><td>1033</td><td>{&amp;MSSansBold8}Network Location</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsAdminInstallPoint_SpecifyNetworkLocation</td><td>1033</td><td>Specify a network location for the server image of the product.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsBrowseButton</td><td>1033</td><td>&amp;Browse...</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsBrowseFolderDlg_11</td><td>1033</td><td/><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsBrowseFolderDlg_4</td><td>1033</td><td/><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsBrowseFolderDlg_8</td><td>1033</td><td/><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsBrowseFolderDlg_BrowseDestFolder</td><td>1033</td><td>Browse to the destination folder.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsBrowseFolderDlg_ChangeCurrentFolder</td><td>1033</td><td>{&amp;MSSansBold8}Change Current Destination Folder</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsBrowseFolderDlg_CreateFolder</td><td>1033</td><td>Create New Folder|</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsBrowseFolderDlg_FolderName</td><td>1033</td><td>&amp;Folder name:</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsBrowseFolderDlg_LookIn</td><td>1033</td><td>&amp;Look in:</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsBrowseFolderDlg_OK</td><td>1033</td><td>OK</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsBrowseFolderDlg_UpOneLevel</td><td>1033</td><td>Up One Level|</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsBrowseForAccount</td><td>1033</td><td>Browse for a User Account</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsBrowseGroup</td><td>1033</td><td>Select a Group</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsBrowseUsernameTitle</td><td>1033</td><td>Select a User Name</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsCancelDlg_ConfirmCancel</td><td>1033</td><td>Are you sure you want to cancel [ProductName] installation?</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsCancelDlg_No</td><td>1033</td><td>&amp;No</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsCancelDlg_Yes</td><td>1033</td><td>&amp;Yes</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsConfirmPassword</td><td>1033</td><td>Con&amp;firm password:</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsCreateNewUserTitle</td><td>1033</td><td>New User Information</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsCreateUserBrowse</td><td>1033</td><td>N&amp;ew User Information...</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsCustomSelectionDlg_Change</td><td>1033</td><td>&amp;Change...</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsCustomSelectionDlg_ClickFeatureIcon</td><td>1033</td><td>Click on an icon in the list below to change how a feature is installed.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsCustomSelectionDlg_CustomSetup</td><td>1033</td><td>{&amp;MSSansBold8}Custom Setup</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsCustomSelectionDlg_FeatureDescription</td><td>1033</td><td>Feature Description</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsCustomSelectionDlg_FeaturePath</td><td>1033</td><td>&lt;selected feature path&gt;</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsCustomSelectionDlg_FeatureSize</td><td>1033</td><td>Feature size</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsCustomSelectionDlg_Help</td><td>1033</td><td>&amp;Help</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsCustomSelectionDlg_InstallTo</td><td>1033</td><td>Install to:</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsCustomSelectionDlg_MultilineDescription</td><td>1033</td><td>Multiline description of the currently selected item</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsCustomSelectionDlg_SelectFeatures</td><td>1033</td><td>Select the program features you want installed.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsCustomSelectionDlg_Space</td><td>1033</td><td>&amp;Space</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsDiskSpaceDlg_DiskSpace</td><td>1033</td><td>Disk space required for the installation exceeds available disk space.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsDiskSpaceDlg_HighlightedVolumes</td><td>1033</td><td>The highlighted volumes do not have enough disk space available for the currently selected features. You can remove files from the highlighted volumes, choose to install fewer features onto local drives, or select different destination drives.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsDiskSpaceDlg_Numbers</td><td>1033</td><td>{120}{70}{70}{70}{70}</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsDiskSpaceDlg_OK</td><td>1033</td><td>OK</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsDiskSpaceDlg_OutOfDiskSpace</td><td>1033</td><td>{&amp;MSSansBold8}Out of Disk Space</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsDomainOrServer</td><td>1033</td><td>&amp;Domain or server:</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsErrorDlg_Abort</td><td>1033</td><td>&amp;Abort</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsErrorDlg_ErrorText</td><td>1033</td><td>&lt;error text goes here&gt;&lt;error text goes here&gt;&lt;error text goes here&gt;&lt;error text goes here&gt;&lt;error text goes here&gt;&lt;error text goes here&gt;&lt;error text goes here&gt;&lt;error text goes here&gt;&lt;error text goes here&gt;&lt;error text goes here&gt;&lt;error text goes here&gt;</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsErrorDlg_Ignore</td><td>1033</td><td>&amp;Ignore</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsErrorDlg_InstallerInfo</td><td>1033</td><td>[ProductName] Installer Information</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsErrorDlg_NO</td><td>1033</td><td>&amp;No</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsErrorDlg_OK</td><td>1033</td><td>&amp;OK</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsErrorDlg_Retry</td><td>1033</td><td>&amp;Retry</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsErrorDlg_Yes</td><td>1033</td><td>&amp;Yes</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsExitDialog_Finish</td><td>1033</td><td>&amp;Finish</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsExitDialog_InstallSuccess</td><td>1033</td><td>The InstallShield Wizard has successfully installed [ProductName]. Click Finish to exit the wizard.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsExitDialog_LaunchProgram</td><td>1033</td><td>Launch the program</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsExitDialog_ShowReadMe</td><td>1033</td><td>Show the readme file</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsExitDialog_UninstallSuccess</td><td>1033</td><td>The InstallShield Wizard has successfully uninstalled [ProductName]. Click Finish to exit the wizard.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsExitDialog_Update_InternetConnection</td><td>1033</td><td>Your Internet connection can be used to make sure that you have the latest updates.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsExitDialog_Update_PossibleUpdates</td><td>1033</td><td>Some program files might have been updated since you purchased your copy of [ProductName].</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsExitDialog_Update_SetupFinished</td><td>1033</td><td>Setup has finished installing [ProductName].</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsExitDialog_Update_YesCheckForUpdates</td><td>1033</td><td>&amp;Yes, check for program updates (Recommended) after the setup completes.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsExitDialog_WizardCompleted</td><td>1033</td><td>{&amp;TahomaBold10}InstallShield Wizard Completed</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsFatalError_ClickFinish</td><td>1033</td><td>Click Finish to exit the wizard.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsFatalError_Finish</td><td>1033</td><td>&amp;Finish</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsFatalError_KeepOrRestore</td><td>1033</td><td>You can either keep any existing installed elements on your system to continue this installation at a later time or you can restore your system to its original state prior to the installation.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsFatalError_NotModified</td><td>1033</td><td>Your system has not been modified. To complete installation at another time, please run setup again.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsFatalError_RestoreOrContinueLater</td><td>1033</td><td>Click Restore or Continue Later to exit the wizard.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsFatalError_WizardCompleted</td><td>1033</td><td>{&amp;TahomaBold10}InstallShield Wizard Completed</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsFatalError_WizardInterrupted</td><td>1033</td><td>The wizard was interrupted before [ProductName] could be completely installed.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsFeatureDetailsDlg_DiskSpaceRequirements</td><td>1033</td><td>{&amp;MSSansBold8}Disk Space Requirements</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsFeatureDetailsDlg_Numbers</td><td>1033</td><td>{120}{70}{70}{70}{70}</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsFeatureDetailsDlg_OK</td><td>1033</td><td>OK</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsFeatureDetailsDlg_SpaceRequired</td><td>1033</td><td>The disk space required for the installation of the selected features.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsFeatureDetailsDlg_VolumesTooSmall</td><td>1033</td><td>The highlighted volumes do not have enough disk space available for the currently selected features. You can remove files from the highlighted volumes, choose to install fewer features onto local drives, or select different destination drives.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsFilesInUse_ApplicationsUsingFiles</td><td>1033</td><td>The following applications are using files that need to be updated by this setup. Close these applications and click Retry to continue.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsFilesInUse_Exit</td><td>1033</td><td>&amp;Exit</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsFilesInUse_FilesInUse</td><td>1033</td><td>{&amp;MSSansBold8}Files in Use</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsFilesInUse_FilesInUseMessage</td><td>1033</td><td>Some files that need to be updated are currently in use.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsFilesInUse_Ignore</td><td>1033</td><td>&amp;Ignore</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsFilesInUse_Retry</td><td>1033</td><td>&amp;Retry</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsGroup</td><td>1033</td><td>&amp;Group:</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsGroupLabel</td><td>1033</td><td>Gr&amp;oup:</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsInitDlg_1</td><td>1033</td><td/><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsInitDlg_2</td><td>1033</td><td/><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsInitDlg_PreparingWizard</td><td>1033</td><td>[ProductName] Setup is preparing the InstallShield Wizard which will guide you through the program setup process.  Please wait.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsInitDlg_WelcomeWizard</td><td>1033</td><td>{&amp;TahomaBold10}Welcome to the InstallShield Wizard for [ProductName]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsLicenseDlg_LicenseAgreement</td><td>1033</td><td>{&amp;MSSansBold8}License Agreement</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsLicenseDlg_ReadLicenseAgreement</td><td>1033</td><td>Please read the following license agreement carefully.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsLogonInfoDescription</td><td>1033</td><td>Specify the user name and password of the user account that will logon to use this application. The user account must be in the form DOMAIN\Username.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsLogonInfoTitle</td><td>1033</td><td>{&amp;MSSansBold8}Logon Information</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsLogonInfoTitleDescription</td><td>1033</td><td>Specify a user name and password</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsLogonNewUserDescription</td><td>1033</td><td>Select the button below to specify information about a new user that will be created during the installation.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsMaintenanceDlg_11</td><td>1033</td><td/><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsMaintenanceDlg_ChangeFeatures</td><td>1033</td><td>Change which program features are installed. This option displays the Custom Selection dialog in which you can change the way features are installed.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsMaintenanceDlg_MaitenanceOptions</td><td>1033</td><td>Modify, repair, or remove the program.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsMaintenanceDlg_Modify</td><td>1033</td><td>{&amp;MSSansBold8}&amp;Modify</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsMaintenanceDlg_ProgramMaintenance</td><td>1033</td><td>{&amp;MSSansBold8}Program Maintenance</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsMaintenanceDlg_Remove</td><td>1033</td><td>{&amp;MSSansBold8}&amp;Remove</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsMaintenanceDlg_RemoveProductName</td><td>1033</td><td>Remove [ProductName] from your computer.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsMaintenanceDlg_Repair</td><td>1033</td><td>{&amp;MSSansBold8}Re&amp;pair</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsMaintenanceDlg_RepairMessage</td><td>1033</td><td>Repair installation errors in the program. This option fixes missing or corrupt files, shortcuts, and registry entries.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsMaintenanceWelcome_MaintenanceOptionsDescription</td><td>1033</td><td>The InstallShield(R) Wizard will allow you to modify, repair, or remove [ProductName]. To continue, click Next.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsMaintenanceWelcome_WizardWelcome</td><td>1033</td><td>{&amp;TahomaBold10}Welcome to the InstallShield Wizard for [ProductName]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsPatchDlg_PatchClickUpdate</td><td>1033</td><td>The InstallShield(R) Wizard will install the Patch for [ProductName] on your computer.  To continue, click Update.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsPatchDlg_PatchWizard</td><td>1033</td><td>[ProductName] Patch - InstallShield Wizard</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsPatchDlg_Update</td><td>1033</td><td>&amp;Update &gt;</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsPatchDlg_WelcomePatchWizard</td><td>1033</td><td>{&amp;TahomaBold10}Welcome to the Patch for [ProductName]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsProgressDlg_2</td><td>1033</td><td/><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsProgressDlg_Hidden</td><td>1033</td><td>(Hidden for now)</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsProgressDlg_HiddenTimeRemaining</td><td>1033</td><td>)Hidden for now)Estimated time remaining:</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsProgressDlg_InstallingProductName</td><td>1033</td><td>{&amp;MSSansBold8}Installing [ProductName]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsProgressDlg_ProgressDone</td><td>1033</td><td>Progress done</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsProgressDlg_SecHidden</td><td>1033</td><td>(Hidden for now)Sec.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsProgressDlg_Status</td><td>1033</td><td>Status:</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsProgressDlg_Uninstalling</td><td>1033</td><td>{&amp;MSSansBold8}Uninstalling [ProductName]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsProgressDlg_UninstallingFeatures</td><td>1033</td><td>The program features you selected are being uninstalled.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsProgressDlg_UninstallingFeatures2</td><td>1033</td><td>The program features you selected are being installed.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsProgressDlg_WaitUninstall</td><td>1033</td><td>Please wait while the InstallShield Wizard uninstalls [ProductName]. This may take several minutes.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsProgressDlg_WaitUninstall2</td><td>1033</td><td>Please wait while the InstallShield Wizard installs [ProductName]. This may take several minutes.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsReadmeDlg_Cancel</td><td>1033</td><td>&amp;Cancel</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsReadmeDlg_PleaseReadInfo</td><td>1033</td><td>Please read the following readme information carefully.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsReadmeDlg_ReadMeInfo</td><td>1033</td><td>{&amp;MSSansBold8}Readme Information</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsRegisterUserDlg_16</td><td>1033</td><td/><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsRegisterUserDlg_Anyone</td><td>1033</td><td>&amp;Anyone who uses this computer (all users)</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsRegisterUserDlg_CustomerInformation</td><td>1033</td><td>{&amp;MSSansBold8}Customer Information</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsRegisterUserDlg_InstallFor</td><td>1033</td><td>Install this application for:</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsRegisterUserDlg_OnlyMe</td><td>1033</td><td>Only for &amp;me ([USERNAME])</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsRegisterUserDlg_Organization</td><td>1033</td><td>&amp;Organization:</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsRegisterUserDlg_PleaseEnterInfo</td><td>1033</td><td>Please enter your information.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsRegisterUserDlg_SerialNumber</td><td>1033</td><td>&amp;Serial Number:</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsRegisterUserDlg_Tahoma50</td><td>1033</td><td>{\Tahoma8}{50}</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsRegisterUserDlg_Tahoma80</td><td>1033</td><td>{\Tahoma8}{80}</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsRegisterUserDlg_UserName</td><td>1033</td><td>&amp;User Name:</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsResumeDlg_ResumeSuspended</td><td>1033</td><td>The InstallShield(R) Wizard will complete the suspended installation of [ProductName] on your computer. To continue, click Next.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsResumeDlg_Resuming</td><td>1033</td><td>{&amp;TahomaBold10}Resuming the InstallShield Wizard for [ProductName]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsResumeDlg_WizardResume</td><td>1033</td><td>The InstallShield(R) Wizard will complete the installation of [ProductName] on your computer. To continue, click Next.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsSelectDomainOrServer</td><td>1033</td><td>Select a Domain or Server</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsSelectDomainUserInstructions</td><td>1033</td><td>Use the browse buttons to select a domain\server and a user name.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsSetupTypeMinDlg_13</td><td>1033</td><td/><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsSetupTypeMinDlg_AllFeatures</td><td>1033</td><td>All program features will be installed. (Requires the most disk space.)</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsSetupTypeMinDlg_ChooseFeatures</td><td>1033</td><td>Choose which program features you want installed and where they will be installed. Recommended for advanced users.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsSetupTypeMinDlg_ChooseSetupType</td><td>1033</td><td>Choose the setup type that best suits your needs.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsSetupTypeMinDlg_Complete</td><td>1033</td><td>{&amp;MSSansBold8}&amp;Complete</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsSetupTypeMinDlg_Custom</td><td>1033</td><td>{&amp;MSSansBold8}Cu&amp;stom</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsSetupTypeMinDlg_Minimal</td><td>1033</td><td>{&amp;MSSansBold8}&amp;Minimal</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsSetupTypeMinDlg_MinimumFeatures</td><td>1033</td><td>Minimum required features will be installed.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsSetupTypeMinDlg_SelectSetupType</td><td>1033</td><td>Please select a setup type.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsSetupTypeMinDlg_SetupType</td><td>1033</td><td>{&amp;MSSansBold8}Setup Type</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsSetupTypeMinDlg_Typical</td><td>1033</td><td>{&amp;MSSansBold8}&amp;Typical</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsUserExit_ClickFinish</td><td>1033</td><td>Click Finish to exit the wizard.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsUserExit_Finish</td><td>1033</td><td>&amp;Finish</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsUserExit_KeepOrRestore</td><td>1033</td><td>You can either keep any existing installed elements on your system to continue this installation at a later time or you can restore your system to its original state prior to the installation.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsUserExit_NotModified</td><td>1033</td><td>Your system has not been modified. To install this program at a later time, please run the installation again.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsUserExit_RestoreOrContinue</td><td>1033</td><td>Click Restore or Continue Later to exit the wizard.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsUserExit_WizardCompleted</td><td>1033</td><td>{&amp;TahomaBold10}InstallShield Wizard Completed</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsUserExit_WizardInterrupted</td><td>1033</td><td>The wizard was interrupted before [ProductName] could be completely installed.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsUserNameLabel</td><td>1033</td><td>&amp;User name:</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsVerifyReadyDlg_BackOrCancel</td><td>1033</td><td>If you want to review or change any of your installation settings, click Back. Click Cancel to exit the wizard.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsVerifyReadyDlg_ClickInstall</td><td>1033</td><td>Click Install to begin the installation.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsVerifyReadyDlg_Company</td><td>1033</td><td>Company: [COMPANYNAME]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsVerifyReadyDlg_CurrentSettings</td><td>1033</td><td>Current Settings:</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsVerifyReadyDlg_DestFolder</td><td>1033</td><td>Destination Folder:</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsVerifyReadyDlg_Install</td><td>1033</td><td>&amp;Install</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsVerifyReadyDlg_Installdir</td><td>1033</td><td>[INSTALLDIR]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsVerifyReadyDlg_ModifyReady</td><td>1033</td><td>{&amp;MSSansBold8}Ready to Modify the Program</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsVerifyReadyDlg_ReadyInstall</td><td>1033</td><td>{&amp;MSSansBold8}Ready to Install the Program</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsVerifyReadyDlg_ReadyRepair</td><td>1033</td><td>{&amp;MSSansBold8}Ready to Repair the Program</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsVerifyReadyDlg_SelectedSetupType</td><td>1033</td><td>[SelectedSetupType]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsVerifyReadyDlg_Serial</td><td>1033</td><td>Serial: [ISX_SERIALNUM]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsVerifyReadyDlg_SetupType</td><td>1033</td><td>Setup Type:</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsVerifyReadyDlg_UserInfo</td><td>1033</td><td>User Information:</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsVerifyReadyDlg_UserName</td><td>1033</td><td>Name: [USERNAME]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsVerifyReadyDlg_WizardReady</td><td>1033</td><td>The wizard is ready to begin installation.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsVerifyRemoveAllDlg_ChoseRemoveProgram</td><td>1033</td><td>You have chosen to remove the program from your system.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsVerifyRemoveAllDlg_ClickBack</td><td>1033</td><td>If you want to review or change any settings, click Back.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsVerifyRemoveAllDlg_ClickRemove</td><td>1033</td><td>Click Remove to remove [ProductName] from your computer. After removal, this program will no longer be available for use.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsVerifyRemoveAllDlg_Remove</td><td>1033</td><td>&amp;Remove</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsVerifyRemoveAllDlg_RemoveProgram</td><td>1033</td><td>{&amp;MSSansBold8}Remove the Program</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsWelcomeDlg_InstallProductName</td><td>1033</td><td>The InstallShield(R) Wizard will install [ProductName] on your computer. To continue, click Next.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsWelcomeDlg_WarningCopyright</td><td>1033</td><td>WARNING: This program is protected by copyright law and international treaties.</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__IsWelcomeDlg_WelcomeProductName</td><td>1033</td><td>{&amp;TahomaBold10}Welcome to the InstallShield Wizard for [ProductName]</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__TargetReq_DESC_COLOR</td><td>1033</td><td>The color settings of your system are not adequate for running [ProductName].</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__TargetReq_DESC_OS</td><td>1033</td><td>The operating system is not adequate for running [ProductName].</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__TargetReq_DESC_PROCESSOR</td><td>1033</td><td>The processor is not adequate for running [ProductName].</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__TargetReq_DESC_RAM</td><td>1033</td><td>The amount of RAM is not adequate for running [ProductName].</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>IDS__TargetReq_DESC_RESOLUTION</td><td>1033</td><td>The screen resolution is not adequate for running [ProductName].</td><td>0</td><td/><td>-1256677244</td></row>
+		<row><td>ID_STRING1</td><td>1033</td><td>http://jakarta.apache.org</td><td>0</td><td/><td>2115538665</td></row>
+		<row><td>ID_STRING2</td><td>1033</td><td>DisplayName1</td><td>0</td><td/><td>2115549418</td></row>
+		<row><td>ID_STRING3</td><td>1033</td><td>jakarta</td><td>0</td><td/><td>2115510538</td></row>
+		<row><td>ID_STRING4</td><td>1033</td><td>CoreFiles</td><td>0</td><td/><td>2115544011</td></row>
+		<row><td>ID_STRING5</td><td>1033</td><td>Tomcat Isapi Redirector</td><td>0</td><td/><td>161207880</td></row>
+		<row><td>ID_STRING6</td><td>1033</td><td>See http://tomcat.apache.org/connectors-doc/</td><td>0</td><td/><td>161197672</td></row>
+		<row><td>IIDS_UITEXT_FeatureUninstalled</td><td>1033</td><td>This feature will remain uninstalled.</td><td>0</td><td/><td>-1256677244</td></row>
+	</table>
+
+	<table name="ISTargetImage">
+		<col key="yes" def="s13">UpgradedImage_</col>
+		<col key="yes" def="s13">Name</col>
+		<col def="s0">MsiPath</col>
+		<col def="i2">Order</col>
+		<col def="I4">Flags</col>
+		<col def="i2">IgnoreMissingFiles</col>
+	</table>
+
+	<table name="ISUpgradeMsiItem">
+		<col key="yes" def="s72">UpgradeItem</col>
+		<col def="s0">ObjectSetupPath</col>
+		<col def="S255">ISReleaseFlags</col>
+		<col def="i2">ISAttributes</col>
+	</table>
+
+	<table name="ISUpgradedImage">
+		<col key="yes" def="s13">Name</col>
+		<col def="s0">MsiPath</col>
+		<col def="s8">Family</col>
+	</table>
+
+	<table name="ISVRoot">
+		<col key="yes" def="s50">VRootKey</col>
+		<col def="l255">VRootName</col>
+		<col def="s50">VRootDir</col>
+		<col def="i4">VRootProps</col>
+		<col def="S255">VRootAppName</col>
+		<col def="S255">VRootDefDoc</col>
+		<col def="S255">Condition</col>
+		<col def="I4">SessionTimeout</col>
+		<col def="I4">ScriptTimeout</col>
+		<col def="S255">AnonyUserName</col>
+		<col def="S255">AnonyPasswrd</col>
+		<col def="s72">Component_</col>
+		<row><td>ISIISCommonVRoot</td><td>Obsolete</td><td>Obsolete</td><td>0</td><td/><td/><td/><td/><td/><td/><td/><td>VirtualDirComponent</td></row>
+	</table>
+
+	<table name="ISVRootAppMaps">
+		<col key="yes" def="s255">VRootKey</col>
+		<col key="yes" def="s255">VRootAppMapKey</col>
+		<col def="l50">Extension</col>
+		<col def="l255">ExecPath</col>
+		<col def="l255">Verb</col>
+		<col def="i4">AppMapProps</col>
+	</table>
+
+	<table name="ISWebSite">
+		<col key="yes" def="s50">ISIISCommon_</col>
+		<col def="s50">Port</col>
+		<col def="S50">IP</col>
+		<col def="s50">SiteNumber</col>
+		<col def="I4">WebSiteProps</col>
+		<row><td>ISIISCommonWebsite1</td><td>80</td><td/><td>1</td><td>0</td></row>
+	</table>
+
+	<table name="ISXmlElement">
+		<col key="yes" def="s72">ISXmlElement</col>
+		<col def="s72">ISXmlFile_</col>
+		<col def="S72">ISXmlElement_Parent</col>
+		<col def="L0">XPath</col>
+		<col def="L0">Content</col>
+		<col def="I4">ISAttributes</col>
+	</table>
+
+	<table name="ISXmlElementAttrib">
+		<col key="yes" def="s72">ISXmlElementAttrib</col>
+		<col key="yes" def="s72">ISXmlElement_</col>
+		<col def="L255">Name</col>
+		<col def="L0">Value</col>
+		<col def="I4">ISAttributes</col>
+	</table>
+
+	<table name="ISXmlFile">
+		<col key="yes" def="s72">ISXmlFile</col>
+		<col def="l255">FileName</col>
+		<col def="s72">Component_</col>
+		<col def="s50">Directory</col>
+		<col def="I4">ISAttributes</col>
+		<col def="S0">SelectionNamespaces</col>
+	</table>
+
+	<table name="ISXmlLocator">
+		<col key="yes" def="s72">Signature_</col>
+		<col key="yes" def="S72">Parent</col>
+		<col def="S255">Element</col>
+		<col def="S255">Attribute</col>
+		<col def="I2">ISAttributes</col>
+	</table>
+
+	<table name="Icon">
+		<col key="yes" def="s72">Name</col>
+		<col def="V0">Data</col>
+		<col def="S255">ISBuildSourcePath</col>
+		<col def="I2">ISIconIndex</col>
+		<row><td>ARPPRODUCTICON.exe</td><td/><td>&lt;ISProjectFolder&gt;\tomcat.ico</td><td>0</td></row>
+	</table>
+
+	<table name="IniFile">
+		<col key="yes" def="s72">IniFile</col>
+		<col def="l255">FileName</col>
+		<col def="S72">DirProperty</col>
+		<col def="l255">Section</col>
+		<col def="l128">Key</col>
+		<col def="s255">Value</col>
+		<col def="i2">Action</col>
+		<col def="s72">Component_</col>
+	</table>
+
+	<table name="IniLocator">
+		<col key="yes" def="s72">Signature_</col>
+		<col def="s255">FileName</col>
+		<col def="s96">Section</col>
+		<col def="s128">Key</col>
+		<col def="I2">Field</col>
+		<col def="I2">Type</col>
+	</table>
+
+	<table name="InstallExecuteSequence">
+		<col key="yes" def="s72">Action</col>
+		<col def="S255">Condition</col>
+		<col def="I2">Sequence</col>
+		<col def="S255">ISComments</col>
+		<col def="I4">ISAttributes</col>
+		<row><td>AllocateRegistrySpace</td><td>NOT Installed</td><td>1550</td><td>AllocateRegistrySpace</td><td/></row>
+		<row><td>AppSearch</td><td/><td>400</td><td>AppSearch</td><td/></row>
+		<row><td>BindImage</td><td/><td>4300</td><td>BindImage</td><td/></row>
+		<row><td>CCPSearch</td><td>CCP_TEST</td><td>500</td><td>CCPSearch</td><td/></row>
+		<row><td>CostFinalize</td><td/><td>1000</td><td>CostFinalize</td><td/></row>
+		<row><td>CostInitialize</td><td/><td>800</td><td>CostInitialize</td><td/></row>
+		<row><td>CreateFolders</td><td/><td>3700</td><td>CreateFolders</td><td/></row>
+		<row><td>CreateShortcuts</td><td/><td>4500</td><td>CreateShortcuts</td><td/></row>
+		<row><td>DeleteServices</td><td>VersionNT</td><td>2000</td><td>DeleteServices</td><td/></row>
+		<row><td>DuplicateFiles</td><td/><td>4210</td><td>DuplicateFiles</td><td/></row>
+		<row><td>FileCost</td><td/><td>900</td><td>FileCost</td><td/></row>
+		<row><td>FindRelatedProducts</td><td>NOT ISSETUPDRIVEN</td><td>420</td><td>FindRelatedProducts</td><td/></row>
+		<row><td>ISSelfRegisterCosting</td><td/><td>2201</td><td/><td/></row>
+		<row><td>ISSelfRegisterFiles</td><td/><td>5601</td><td/><td/></row>
+		<row><td>ISSelfRegisterFinalize</td><td/><td>6601</td><td/><td/></row>
+		<row><td>ISUnSelfRegisterFiles</td><td/><td>2202</td><td/><td/></row>
+		<row><td>InstallFiles</td><td/><td>4000</td><td>InstallFiles</td><td/></row>
+		<row><td>InstallFilter</td><td/><td>4001</td><td/><td/></row>
+		<row><td>InstallFinalize</td><td/><td>6600</td><td>InstallFinalize</td><td/></row>
+		<row><td>InstallInitialize</td><td/><td>1501</td><td>InstallInitialize</td><td/></row>
+		<row><td>InstallODBC</td><td/><td>5400</td><td>InstallODBC</td><td/></row>
+		<row><td>InstallServices</td><td>VersionNT</td><td>5800</td><td>InstallServices</td><td/></row>
+		<row><td>InstallValidate</td><td/><td>1400</td><td>InstallValidate</td><td/></row>
+		<row><td>IsolateComponents</td><td/><td>950</td><td>IsolateComponents</td><td/></row>
+		<row><td>LaunchConditions</td><td>Not Installed</td><td>410</td><td>LaunchConditions</td><td/></row>
+		<row><td>MigrateFeatureStates</td><td/><td>1200</td><td>MigrateFeatureStates</td><td/></row>
+		<row><td>MoveFiles</td><td/><td>3800</td><td>MoveFiles</td><td/></row>
+		<row><td>MsiPublishAssemblies</td><td/><td>6250</td><td>MsiPublishAssemblies</td><td/></row>
+		<row><td>MsiUnpublishAssemblies</td><td/><td>1750</td><td>MsiUnpublishAssemblies</td><td/></row>
+		<row><td>PatchFiles</td><td/><td>4090</td><td>PatchFiles</td><td/></row>
+		<row><td>ProcessComponents</td><td/><td>1600</td><td>ProcessComponents</td><td/></row>
+		<row><td>PublishComponents</td><td/><td>6200</td><td>PublishComponents</td><td/></row>
+		<row><td>PublishFeatures</td><td/><td>6300</td><td>PublishFeatures</td><td/></row>
+		<row><td>PublishProduct</td><td/><td>6400</td><td>PublishProduct</td><td/></row>
+		<row><td>RMCCPSearch</td><td>Not CCP_SUCCESS And CCP_TEST</td><td>600</td><td>RMCCPSearch</td><td/></row>
+		<row><td>RegisterClassInfo</td><td/><td>4600</td><td>RegisterClassInfo</td><td/></row>
+		<row><td>RegisterComPlus</td><td/><td>5700</td><td>RegisterComPlus</td><td/></row>
+		<row><td>RegisterExtensionInfo</td><td/><td>4700</td><td>RegisterExtensionInfo</td><td/></row>
+		<row><td>RegisterFonts</td><td/><td>5300</td><td>RegisterFonts</td><td/></row>
+		<row><td>RegisterMIMEInfo</td><td/><td>4900</td><td>RegisterMIMEInfo</td><td/></row>
+		<row><td>RegisterProduct</td><td/><td>6100</td><td>RegisterProduct</td><td/></row>
+		<row><td>RegisterProgIdInfo</td><td/><td>4800</td><td>RegisterProgIdInfo</td><td/></row>
+		<row><td>RegisterTypeLibraries</td><td/><td>5500</td><td>RegisterTypeLibraries</td><td/></row>
+		<row><td>RegisterUser</td><td/><td>6000</td><td>RegisterUser</td><td/></row>
+		<row><td>RemoveDuplicateFiles</td><td/><td>3400</td><td>RemoveDuplicateFiles</td><td/></row>
+		<row><td>RemoveEnvironmentStrings</td><td/><td>3300</td><td>RemoveEnvironmentStrings</td><td/></row>
+		<row><td>RemoveExistingProducts</td><td/><td>1410</td><td>RemoveExistingProducts</td><td/></row>
+		<row><td>RemoveFiles</td><td/><td>3500</td><td>RemoveFiles</td><td/></row>
+		<row><td>RemoveFolders</td><td/><td>3600</td><td>RemoveFolders</td><td/></row>
+		<row><td>RemoveIniValues</td><td/><td>3100</td><td>RemoveIniValues</td><td/></row>
+		<row><td>RemoveODBC</td><td/><td>2400</td><td>RemoveODBC</td><td/></row>
+		<row><td>RemoveRegistryValues</td><td/><td>2600</td><td>RemoveRegistryValues</td><td/></row>
+		<row><td>RemoveShortcuts</td><td/><td>3200</td><td>RemoveShortcuts</td><td/></row>
+		<row><td>ScheduleReboot</td><td>ISSCHEDULEREBOOT</td><td>6410</td><td>ScheduleReboot</td><td/></row>
+		<row><td>SelfRegModules</td><td/><td>5600</td><td>SelfRegModules</td><td/></row>
+		<row><td>SelfUnregModules</td><td/><td>2200</td><td>SelfUnregModules</td><td/></row>
+		<row><td>SetARPINSTALLLOCATION</td><td>Not Installed</td><td>1010</td><td>SetARPINSTALLLOCATION</td><td/></row>
+		<row><td>SetODBCFolders</td><td/><td>1100</td><td>SetODBCFolders</td><td/></row>
+		<row><td>StartServices</td><td>VersionNT</td><td>5900</td><td>StartServices</td><td/></row>
+		<row><td>StopServices</td><td>VersionNT</td><td>1900</td><td>StopServices</td><td/></row>
+		<row><td>UnpublishComponents</td><td/><td>1700</td><td>UnpublishComponents</td><td/></row>
+		<row><td>UnpublishFeatures</td><td/><td>1800</td><td>UnpublishFeatures</td><td/></row>
+		<row><td>UnregisterClassInfo</td><td/><td>2700</td><td>UnregisterClassInfo</td><td/></row>
+		<row><td>UnregisterComPlus</td><td/><td>2100</td><td>UnregisterComPlus</td><td/></row>
+		<row><td>UnregisterExtensionInfo</td><td/><td>2800</td><td>UnregisterExtensionInfo</td><td/></row>
+		<row><td>UnregisterFonts</td><td/><td>2500</td><td>UnregisterFonts</td><td/></row>
+		<row><td>UnregisterMIMEInfo</td><td/><td>3000</td><td>UnregisterMIMEInfo</td><td/></row>
+		<row><td>UnregisterProgIdInfo</td><td/><td>2900</td><td>UnregisterProgIdInfo</td><td/></row>
+		<row><td>UnregisterTypeLibraries</td><td/><td>2300</td><td>UnregisterTypeLibraries</td><td/></row>
+		<row><td>ValidateProductID</td><td/><td>700</td><td>ValidateProductID</td><td/></row>
+		<row><td>WriteEnvironmentStrings</td><td/><td>5200</td><td>WriteEnvironmentStrings</td><td/></row>
+		<row><td>WriteIniValues</td><td/><td>5100</td><td>WriteIniValues</td><td/></row>
+		<row><td>WriteRegistryValues</td><td/><td>5000</td><td>WriteRegistryValues</td><td/></row>
+		<row><td>caCreateVRoots</td><td/><td>4002</td><td/><td/></row>
+		<row><td>caExtractIISSuppFiles</td><td/><td>1500</td><td/><td/></row>
+		<row><td>caIISCleanup</td><td/><td>6602</td><td/><td/></row>
+		<row><td>caRemoveVRoots</td><td/><td>3503</td><td/><td/></row>
+		<row><td>caRlbackVRoots</td><td/><td>3502</td><td/><td/></row>
+	</table>
+
+	<table name="InstallShield">
+		<col key="yes" def="s72">Property</col>
+		<col def="S0">Value</col>
+		<row><td>ActiveLanguage</td><td>1033</td></row>
+		<row><td>Comments</td><td/></row>
+		<row><td>CurrentMedia</td><td dt:dt="bin.base64" md5="ad56c01706a65016783625b2b7eb1639">
+TQBzAGkAAQBSAGUAbABlAGEAcwBlAA==
+			</td></row>
+		<row><td>ISCompilerOption_CompileBeforeBuild</td><td>1</td></row>
+		<row><td>ISCompilerOption_Debug</td><td>0</td></row>
+		<row><td>ISCompilerOption_IncludePath</td><td/></row>
+		<row><td>ISCompilerOption_MaxErrors</td><td>50</td></row>
+		<row><td>ISCompilerOption_MaxWarnings</td><td>50</td></row>
+		<row><td>ISCompilerOption_OutputPath</td><td>&lt;ISProjectDataFolder&gt;\Script Files</td></row>
+		<row><td>ISCompilerOption_PreProcessor</td><td/></row>
+		<row><td>ISCompilerOption_WarningLevel</td><td>3</td></row>
+		<row><td>ISCompilerOption_WarningsAsErrors</td><td>1</td></row>
+		<row><td>ISDialogLangID</td><td/></row>
+		<row><td>ISUSLock</td><td>{3872C0E8-6909-4854-B023-A63C791DEF8B}</td></row>
+		<row><td>ISUSSignature</td><td>{33FEE5F9-7895-4F44-87E1-D98785707659}</td></row>
+		<row><td>IsProjectFileText</td><td>1</td></row>
+		<row><td>MsiExecCmdLineOptions</td><td/></row>
+		<row><td>MsiLogFile</td><td/></row>
+		<row><td>OnUpgrade</td><td>1</td></row>
+		<row><td>Owner</td><td>Mladen Turk</td></row>
+		<row><td>PatchFamily</td><td>MyPatchFamily1</td></row>
+		<row><td>PatchSequence</td><td>1.0.0</td></row>
+		<row><td>SaveAsSchema</td><td/></row>
+		<row><td>SccEnabled</td><td>0</td></row>
+		<row><td>SccPath</td><td/></row>
+		<row><td>SchemaVersion</td><td>763</td></row>
+		<row><td>Type</td><td>MSI</td></row>
+	</table>
+
+	<table name="InstallUISequence">
+		<col key="yes" def="s72">Action</col>
+		<col def="S255">Condition</col>
+		<col def="I2">Sequence</col>
+		<col def="S255">ISComments</col>
+		<col def="I4">ISAttributes</col>
+		<row><td>AppSearch</td><td/><td>400</td><td>AppSearch</td><td/></row>
+		<row><td>CCPSearch</td><td>CCP_TEST</td><td>500</td><td>CCPSearch</td><td/></row>
+		<row><td>CostFinalize</td><td/><td>1000</td><td>CostFinalize</td><td/></row>
+		<row><td>CostInitialize</td><td/><td>800</td><td>CostInitialize</td><td/></row>
+		<row><td>ExecuteAction</td><td/><td>1300</td><td>ExecuteAction</td><td/></row>
+		<row><td>FileCost</td><td/><td>900</td><td>FileCost</td><td/></row>
+		<row><td>FindRelatedProducts</td><td/><td>430</td><td>FindRelatedProducts</td><td/></row>
+		<row><td>InstallWelcome</td><td>Not Installed And (Not PATCH Or IS_MAJOR_UPGRADE)</td><td>1210</td><td>InstallWelcome</td><td/></row>
+		<row><td>IsolateComponents</td><td/><td>950</td><td>IsolateComponents</td><td/></row>
+		<row><td>LaunchConditions</td><td>Not Installed</td><td>410</td><td>LaunchConditions</td><td/></row>
+		<row><td>MaintenanceWelcome</td><td>Installed And Not RESUME And Not Preselected And Not PATCH</td><td>1230</td><td>MaintenanceWelcome</td><td/></row>
+		<row><td>MigrateFeatureStates</td><td/><td>1200</td><td>MigrateFeatureStates</td><td/></row>
+		<row><td>PatchWelcome</td><td>PATCH And Not IS_MAJOR_UPGRADE</td><td>1205</td><td>Patch Panel</td><td/></row>
+		<row><td>RMCCPSearch</td><td>Not CCP_SUCCESS And CCP_TEST</td><td>600</td><td>RMCCPSearch</td><td/></row>
+		<row><td>ResolveSource</td><td>Not Installed And Not PATCH</td><td>990</td><td>ResolveSource</td><td/></row>
+		<row><td>SetAllUsersProfileNT</td><td>VersionNT = 400</td><td>970</td><td/><td/></row>
+		<row><td>SetupCompleteError</td><td/><td>-3</td><td>SetupCompleteError</td><td/></row>
+		<row><td>SetupCompleteSuccess</td><td/><td>-1</td><td>SetupCompleteSuccess</td><td/></row>
+		<row><td>SetupInitialization</td><td/><td>420</td><td>SetupInitialization</td><td/></row>
+		<row><td>SetupInterrupted</td><td/><td>-2</td><td>SetupInterrupted</td><td/></row>
+		<row><td>SetupProgress</td><td/><td>1240</td><td>SetupProgress</td><td/></row>
+		<row><td>SetupResume</td><td>Installed And (RESUME Or Preselected) And Not PATCH</td><td>1220</td><td>SetupResume</td><td/></row>
+		<row><td>ValidateProductID</td><td/><td>700</td><td>ValidateProductID</td><td/></row>
+		<row><td>setAllUsersProfile2K</td><td>VersionNT &gt;= 500</td><td>980</td><td/><td/></row>
+		<row><td>setUserProfileNT</td><td>VersionNT</td><td>960</td><td/><td/></row>
+	</table>
+
+	<table name="IsolatedComponent">
+		<col key="yes" def="s72">Component_Shared</col>
+		<col key="yes" def="s72">Component_Application</col>
+	</table>
+
+	<table name="LaunchCondition">
+		<col key="yes" def="s255">Condition</col>
+		<col def="l255">Description</col>
+		<row><td>(Not Version9X)</td><td>##IDPROP_EXPRESS_LAUNCH_CONDITION_OS##</td></row>
+	</table>
+
+	<table name="ListBox">
+		<col key="yes" def="s72">Property</col>
+		<col key="yes" def="i2">Order</col>
+		<col def="s64">Value</col>
+		<col def="L64">Text</col>
+		<row><td>IS_SQLSERVER_LIST</td><td>1</td><td>TestValue</td><td/></row>
+	</table>
+
+	<table name="ListView">
+		<col key="yes" def="s72">Property</col>
+		<col key="yes" def="i2">Order</col>
+		<col def="s64">Value</col>
+		<col def="L64">Text</col>
+		<col def="S72">Binary_</col>
+	</table>
+
+	<table name="LockPermissions">
+		<col key="yes" def="s72">LockObject</col>
+		<col key="yes" def="s32">Table</col>
+		<col key="yes" def="S255">Domain</col>
+		<col key="yes" def="s255">User</col>
+		<col def="I4">Permission</col>
+	</table>
+
+	<table name="MIME">
+		<col key="yes" def="s64">ContentType</col>
+		<col def="s255">Extension_</col>
+		<col def="S38">CLSID</col>
+	</table>
+
+	<table name="Media">
+		<col key="yes" def="i2">DiskId</col>
+		<col def="i2">LastSequence</col>
+		<col def="L64">DiskPrompt</col>
+		<col def="S255">Cabinet</col>
+		<col def="S32">VolumeLabel</col>
+		<col def="S32">Source</col>
+	</table>
+
+	<table name="MoveFile">
+		<col key="yes" def="s72">FileKey</col>
+		<col def="s72">Component_</col>
+		<col def="L255">SourceName</col>
+		<col def="L255">DestName</col>
+		<col def="S72">SourceFolder</col>
+		<col def="s72">DestFolder</col>
+		<col def="i2">Options</col>
+	</table>
+
+	<table name="MsiAssembly">
+		<col key="yes" def="s72">Component_</col>
+		<col def="s38">Feature_</col>
+		<col def="S72">File_Manifest</col>
+		<col def="S72">File_Application</col>
+		<col def="I2">Attributes</col>
+	</table>
+
+	<table name="MsiAssemblyName">
+		<col key="yes" def="s72">Component_</col>
+		<col key="yes" def="s255">Name</col>
+		<col def="s255">Value</col>
+	</table>
+
+	<table name="MsiDigitalCertificate">
+		<col key="yes" def="s72">DigitalCertificate</col>
+		<col def="v0">CertData</col>
+	</table>
+
+	<table name="MsiDigitalSignature">
+		<col key="yes" def="s32">Table</col>
+		<col key="yes" def="s72">SignObject</col>
+		<col def="s72">DigitalCertificate_</col>
+		<col def="V0">Hash</col>
+	</table>
+
+	<table name="MsiDriverPackages">
+		<col key="yes" def="s72">Component</col>
+		<col def="i4">Flags</col>
+		<col def="I4">Sequence</col>
+		<col def="S0">ReferenceComponents</col>
+	</table>
+
+	<table name="MsiFileHash">
+		<col key="yes" def="s72">File_</col>
+		<col def="i2">Options</col>
+		<col def="i4">HashPart1</col>
+		<col def="i4">HashPart2</col>
+		<col def="i4">HashPart3</col>
+		<col def="i4">HashPart4</col>
+	</table>
+
+	<table name="MsiPatchCertificate">
+		<col key="yes" def="S72">PatchCertificate</col>
+		<col def="s72">DigitalCertificate_</col>
+	</table>
+
+	<table name="MsiPatchMetadata">
+		<col key="yes" def="s72">PatchConfiguration_</col>
+		<col key="yes" def="S72">Company</col>
+		<col key="yes" def="s72">Property</col>
+		<col def="S0">Value</col>
+	</table>
+
+	<table name="MsiPatchOldAssemblyFile">
+		<col key="yes" def="s72">File_</col>
+		<col key="yes" def="S72">Assembly_</col>
+	</table>
+
+	<table name="MsiPatchOldAssemblyName">
+		<col key="yes" def="s72">Assembly_</col>
+		<col key="yes" def="s255">Name</col>
+		<col def="S255">Value</col>
+	</table>
+
+	<table name="MsiPatchSequence">
+		<col key="yes" def="s72">PatchConfiguration_</col>
+		<col key="yes" def="s0">PatchFamily</col>
+		<col key="yes" def="S0">Target</col>
+		<col def="s0">Sequence</col>
+		<col def="i2">Supersede</col>
+	</table>
+
+	<table name="ODBCAttribute">
+		<col key="yes" def="s72">Driver_</col>
+		<col key="yes" def="s40">Attribute</col>
+		<col def="S255">Value</col>
+	</table>
+
+	<table name="ODBCDataSource">
+		<col key="yes" def="s72">DataSource</col>
+		<col def="s72">Component_</col>
+		<col def="s255">Description</col>
+		<col def="s255">DriverDescription</col>
+		<col def="i2">Registration</col>
+	</table>
+
+	<table name="ODBCDriver">
+		<col key="yes" def="s72">Driver</col>
+		<col def="s72">Component_</col>
+		<col def="s255">Description</col>
+		<col def="s72">File_</col>
+		<col def="S72">File_Setup</col>
+	</table>
+
+	<table name="ODBCSourceAttribute">
+		<col key="yes" def="s72">DataSource_</col>
+		<col key="yes" def="s32">Attribute</col>
+		<col def="S255">Value</col>
+	</table>
+
+	<table name="ODBCTranslator">
+		<col key="yes" def="s72">Translator</col>
+		<col def="s72">Component_</col>
+		<col def="s255">Description</col>
+		<col def="s72">File_</col>
+		<col def="S72">File_Setup</col>
+	</table>
+
+	<table name="Patch">
+		<col key="yes" def="s72">File_</col>
+		<col key="yes" def="i2">Sequence</col>
+		<col def="i4">PatchSize</col>
+		<col def="i2">Attributes</col>
+		<col def="V0">Header</col>
+		<col def="S255">ISBuildSourcePath</col>
+	</table>
+
+	<table name="PatchPackage">
+		<col key="yes" def="s38">PatchId</col>
+		<col def="i2">Media_</col>
+	</table>
+
+	<table name="ProgId">
+		<col key="yes" def="s255">ProgId</col>
+		<col def="S255">ProgId_Parent</col>
+		<col def="S38">Class_</col>
+		<col def="L255">Description</col>
+		<col def="S72">Icon_</col>
+		<col def="I2">IconIndex</col>
+		<col def="I4">ISAttributes</col>
+	</table>
+
+	<table name="Property">
+		<col key="yes" def="s72">Property</col>
+		<col def="L0">Value</col>
+		<col def="S255">ISComments</col>
+		<row><td>ARPPRODUCTICON</td><td>ARPPRODUCTICON.exe</td><td/></row>
+		<row><td>ARPURLINFOABOUT</td><td>##ID_STRING1##</td><td/></row>
+		<row><td>AgreeToLicense</td><td>No</td><td/></row>
+		<row><td>ApplicationUsers</td><td>AllUsers</td><td/></row>
+		<row><td>DWUSINTERVAL</td><td>30</td><td/></row>
+		<row><td>DWUSLINK</td><td>CEAC2798B9CCC7BFDEAC879F5E6C978F29BC978F69DCE0AFCEFC673F895C57EF898B905F79AC</td><td/></row>
+		<row><td>DefaultUIFont</td><td>Tahoma8</td><td/></row>
+		<row><td>DialogCaption</td><td>InstallShield for Windows Installer</td><td/></row>
+		<row><td>DiskPrompt</td><td>[1]</td><td/></row>
+		<row><td>DisplayNameCustom</td><td>##IDS__DisplayName_Custom##</td><td/></row>
+		<row><td>DisplayNameMinimal</td><td>##IDS__DisplayName_Minimal##</td><td/></row>
+		<row><td>DisplayNameTypical</td><td>##IDS__DisplayName_Typical##</td><td/></row>
+		<row><td>Display_IsBitmapDlg</td><td>1</td><td/></row>
+		<row><td>ErrorDialog</td><td>SetupError</td><td/></row>
+		<row><td>INSTALLLEVEL</td><td>100</td><td/></row>
+		<row><td>ISCHECKFORPRODUCTUPDATES</td><td>1</td><td/></row>
+		<row><td>ISENABLEDWUSFINISHDIALOG</td><td/><td/></row>
+		<row><td>ISVROOT_PORT_NO</td><td>0</td><td/></row>
+		<row><td>IS_COMPLUS_PROGRESSTEXT_COST</td><td>##IDS_COMPLUS_PROGRESSTEXT_COST##</td><td/></row>
+		<row><td>IS_COMPLUS_PROGRESSTEXT_INSTALL</td><td>##IDS_COMPLUS_PROGRESSTEXT_INSTALL##</td><td/></row>
+		<row><td>IS_COMPLUS_PROGRESSTEXT_UNINSTALL</td><td>##IDS_COMPLUS_PROGRESSTEXT_UNINSTALL##</td><td/></row>
+		<row><td>IS_PROGMSG_XML_COSTING</td><td>##IDS_PROGMSG_XML_COSTING##</td><td/></row>
+		<row><td>IS_PROGMSG_XML_CREATE_FILE</td><td>##IDS_PROGMSG_XML_CREATE_FILE##</td><td/></row>
+		<row><td>IS_PROGMSG_XML_FILES</td><td>##IDS_PROGMSG_XML_FILES##</td><td/></row>
+		<row><td>IS_PROGMSG_XML_REMOVE_FILE</td><td>##IDS_PROGMSG_XML_REMOVE_FILE##</td><td/></row>
+		<row><td>IS_PROGMSG_XML_ROLLBACK_FILES</td><td>##IDS_PROGMSG_XML_ROLLBACK_FILES##</td><td/></row>
+		<row><td>IS_PROGMSG_XML_UPDATE_FILE</td><td>##IDS_PROGMSG_XML_UPDATE_FILE##</td><td/></row>
+		<row><td>IS_SQLSERVER_AUTHENTICATION</td><td>0</td><td/></row>
+		<row><td>IS_SQLSERVER_DATABASE</td><td/><td/></row>
+		<row><td>IS_SQLSERVER_PASSWORD</td><td/><td/></row>
+		<row><td>IS_SQLSERVER_SERVER</td><td/><td/></row>
+		<row><td>IS_SQLSERVER_USERNAME</td><td>sa</td><td/></row>
+		<row><td>InstallChoice</td><td>AR</td><td/></row>
+		<row><td>Manufacturer</td><td>##COMPANY_NAME##</td><td/></row>
+		<row><td>PIDTemplate</td><td>12345&lt;###-%%%%%%%&gt;@@@@@</td><td/></row>
+		<row><td>PROGMSG_IIS_CREATEAPPPOOL</td><td>##IDS_PROGMSG_IIS_CREATEAPPPOOL##</td><td/></row>
+		<row><td>PROGMSG_IIS_CREATEAPPPOOLS</td><td>##IDS_PROGMSG_IIS_CREATEAPPPOOLS##</td><td/></row>
+		<row><td>PROGMSG_IIS_CREATEVROOT</td><td>##IDS_PROGMSG_IIS_CREATEVROOT##</td><td/></row>
+		<row><td>PROGMSG_IIS_CREATEVROOTS</td><td>##IDS_PROGMSG_IIS_CREATEVROOTS##</td><td/></row>
+		<row><td>PROGMSG_IIS_CREATEWEBSERVICEEXTENSION</td><td>##IDS_PROGMSG_IIS_CREATEWEBSERVICEEXTENSION##</td><td/></row>
+		<row><td>PROGMSG_IIS_CREATEWEBSERVICEEXTENSIONS</td><td>##IDS_PROGMSG_IIS_CREATEWEBSERVICEEXTENSIONS##</td><td/></row>
+		<row><td>PROGMSG_IIS_EXTRACT</td><td>##IDS_PROGMSG_IIS_EXTRACT##</td><td/></row>
+		<row><td>PROGMSG_IIS_EXTRACTDONE</td><td>##IDS_PROGMSG_IIS_EXTRACTDONE##</td><td/></row>
+		<row><td>PROGMSG_IIS_REMOVEAPPPOOL</td><td>##IDS_PROGMSG_IIS_REMOVEAPPPOOL##</td><td/></row>
+		<row><td>PROGMSG_IIS_REMOVEAPPPOOLS</td><td>##IDS_PROGMSG_IIS_REMOVEAPPPOOLS##</td><td/></row>
+		<row><td>PROGMSG_IIS_REMOVESITE</td><td>##IDS_PROGMSG_IIS_REMOVESITE##</td><td/></row>
+		<row><td>PROGMSG_IIS_REMOVEVROOT</td><td>##IDS_PROGMSG_IIS_REMOVEVROOT##</td><td/></row>
+		<row><td>PROGMSG_IIS_REMOVEVROOTS</td><td>##IDS_PROGMSG_IIS_REMOVEVROOTS##</td><td/></row>
+		<row><td>PROGMSG_IIS_REMOVEWEBSERVICEEXTENSION</td><td>##IDS_PROGMSG_IIS_REMOVEWEBSERVICEEXTENSION##</td><td/></row>
+		<row><td>PROGMSG_IIS_REMOVEWEBSERVICEEXTENSIONS</td><td>##IDS_PROGMSG_IIS_REMOVEWEBSERVICEEXTENSIONS##</td><td/></row>
+		<row><td>PROGMSG_IIS_ROLLBACKAPPPOOLS</td><td>##IDS_PROGMSG_IIS_ROLLBACKAPPPOOLS##</td><td/></row>
+		<row><td>PROGMSG_IIS_ROLLBACKVROOTS</td><td>##IDS_PROGMSG_IIS_ROLLBACKVROOTS##</td><td/></row>
+		<row><td>PROGMSG_IIS_ROLLBACKWEBSERVICEEXTENSIONS</td><td>##IDS_PROGMSG_IIS_ROLLBACKWEBSERVICEEXTENSIONS##</td><td/></row>
+		<row><td>ProductCode</td><td>{8B799ADD-7E5C-41B9-936B-942F3CAE42A0}</td><td/></row>
+		<row><td>ProductID</td><td>none</td><td/></row>
+		<row><td>ProductLanguage</td><td>1033</td><td/></row>
+		<row><td>ProductName</td><td>Tomcat Isapi Redirector</td><td/></row>
+		<row><td>ProductVersion</td><td>1.2.24</td><td/></row>
+		<row><td>ProgressType0</td><td>install</td><td/></row>
+		<row><td>ProgressType1</td><td>Installing</td><td/></row>
+		<row><td>ProgressType2</td><td>installed</td><td/></row>
+		<row><td>ProgressType3</td><td>installs</td><td/></row>
+		<row><td>RebootYesNo</td><td>Yes</td><td/></row>
+		<row><td>ReinstallModeText</td><td>omus</td><td/></row>
+		<row><td>SHOWLAUNCHPROGRAM</td><td>0</td><td/></row>
+		<row><td>SetupType</td><td>Typical</td><td/></row>
+		<row><td>UpgradeCode</td><td>{0BE8FA4C-503F-4209-A3DA-79B605E542E0}</td><td/></row>
+		<row><td>_IsMaintenance</td><td>Change</td><td/></row>
+		<row><td>_IsSetupTypeMin</td><td>Typical</td><td/></row>
+	</table>
+
+	<table name="PublishComponent">
+		<col key="yes" def="s38">ComponentId</col>
+		<col key="yes" def="s255">Qualifier</col>
+		<col key="yes" def="s72">Component_</col>
+		<col def="L255">AppData</col>
+		<col def="s38">Feature_</col>
+	</table>
+
+	<table name="RadioButton">
+		<col key="yes" def="s72">Property</col>
+		<col key="yes" def="i2">Order</col>
+		<col def="s64">Value</col>
+		<col def="i2">X</col>
+		<col def="i2">Y</col>
+		<col def="i2">Width</col>
+		<col def="i2">Height</col>
+		<col def="L64">Text</col>
+		<col def="L50">Help</col>
+		<col def="I4">ISControlId</col>
+		<row><td>AgreeToLicense</td><td>1</td><td>No</td><td>0</td><td>15</td><td>291</td><td>15</td><td>##IDS__AgreeToLicense_0##</td><td/><td/></row>
+		<row><td>AgreeToLicense</td><td>2</td><td>Yes</td><td>0</td><td>0</td><td>291</td><td>15</td><td>##IDS__AgreeToLicense_1##</td><td/><td/></row>
+		<row><td>ApplicationUsers</td><td>1</td><td>AllUsers</td><td>1</td><td>7</td><td>290</td><td>14</td><td>##IDS__IsRegisterUserDlg_Anyone##</td><td/><td/></row>
+		<row><td>ApplicationUsers</td><td>2</td><td>OnlyCurrentUser</td><td>1</td><td>23</td><td>290</td><td>14</td><td>##IDS__IsRegisterUserDlg_OnlyMe##</td><td/><td/></row>
+		<row><td>IS_SQLSERVER_AUTHENTICATION</td><td>1</td><td>0</td><td>0</td><td>0</td><td>327</td><td>14</td><td>##IDS_SQLLOGIN_WIN##</td><td/><td>0</td></row>
+		<row><td>IS_SQLSERVER_AUTHENTICATION</td><td>2</td><td>1</td><td>0</td><td>14</td><td>327</td><td>19</td><td>##IDS_SQLLOGIN_SQL##</td><td/><td>0</td></row>
+		<row><td>_IsMaintenance</td><td>1</td><td>Change</td><td>0</td><td>0</td><td>290</td><td>14</td><td>##IDS__IsMaintenanceDlg_Modify##</td><td/><td/></row>
+		<row><td>_IsMaintenance</td><td>2</td><td>Reinstall</td><td>0</td><td>60</td><td>290</td><td>14</td><td>##IDS__IsMaintenanceDlg_Repair##</td><td/><td/></row>
+		<row><td>_IsMaintenance</td><td>3</td><td>Remove</td><td>0</td><td>120</td><td>290</td><td>14</td><td>##IDS__IsMaintenanceDlg_Remove##</td><td/><td/></row>
+		<row><td>_IsSetupTypeMin</td><td>1</td><td>Typical</td><td>0</td><td>0</td><td>264</td><td>14</td><td>##IDS__IsSetupTypeMinDlg_Complete##</td><td/><td/></row>
+		<row><td>_IsSetupTypeMin</td><td>2</td><td>Custom</td><td>0</td><td>60</td><td>264</td><td>14</td><td>##IDS__IsSetupTypeMinDlg_Custom##</td><td/><td/></row>
+	</table>
+
+	<table name="RegLocator">
+		<col key="yes" def="s72">Signature_</col>
+		<col def="i2">Root</col>
+		<col def="s255">Key</col>
+		<col def="S255">Name</col>
+		<col def="I2">Type</col>
+		<row><td>_IISROOTFOLDER</td><td>2</td><td>Software\Microsoft\InetStp</td><td>PathWWWRoot</td><td>0</td></row>
+		<row><td>_IIS_VERSION</td><td>2</td><td>SYSTEM\CurrentControlSet\Services\W3SVC\Parameters</td><td>MajorVersion</td><td>2</td></row>
+	</table>
+
+	<table name="Registry">
+		<col key="yes" def="s72">Registry</col>
+		<col def="i2">Root</col>
+		<col def="s255">Key</col>
+		<col def="S255">Name</col>
+		<col def="S0">Value</col>
+		<col def="s72">Component_</col>
+		<col def="I4">ISAttributes</col>
+		<row><td>Registry1</td><td>2</td><td>SOFTWARE\Apache Software Foundation\Jakarta Isapi Redirector\1.0</td><td>rewrite_rule_file</td><td>[INSTALLDIR]conf\rewrite.properties</td><td>ISRegistryComponent1</td><td>0</td></row>
+		<row><td>Registry10</td><td>2</td><td>SOFTWARE\Apache Software Foundation</td><td/><td/><td>ISRegistryComponent1</td><td>0</td></row>
+		<row><td>Registry11</td><td>2</td><td>SOFTWARE\Apache Software Foundation\Jakarta Isapi Redirector</td><td/><td/><td>ISRegistryComponent1</td><td>0</td></row>
+		<row><td>Registry12</td><td>2</td><td>SOFTWARE\Apache Software Foundation\Jakarta Isapi Redirector\1.0</td><td/><td/><td>ISRegistryComponent1</td><td>0</td></row>
+		<row><td>Registry13</td><td>2</td><td>SOFTWARE\Apache Software Foundation\Jakarta Isapi Redirector\1.0</td><td>extension_uri</td><td>/jakarta/isapi_redirect.dll</td><td>ISRegistryComponent1</td><td>0</td></row>
+		<row><td>Registry14</td><td>2</td><td>SOFTWARE\Apache Software Foundation\Jakarta Isapi Redirector\1.0</td><td>log_file</td><td>[INSTALLDIR]log\isapi_redirect.log</td><td>ISRegistryComponent1</td><td>0</td></row>
+		<row><td>Registry15</td><td>2</td><td>SOFTWARE\Apache Software Foundation\Jakarta Isapi Redirector\1.0</td><td>log_level</td><td>info</td><td>ISRegistryComponent1</td><td>0</td></row>
+		<row><td>Registry16</td><td>2</td><td>SOFTWARE\Apache Software Foundation\Jakarta Isapi Redirector\1.0</td><td>worker_file</td><td>[INSTALLDIR]conf\workers.properties.minimal</td><td>ISRegistryComponent1</td><td>0</td></row>
+		<row><td>Registry17</td><td>2</td><td>SOFTWARE\Apache Software Foundation\Jakarta Isapi Redirector\1.0</td><td>worker_mount_file</td><td>[INSTALLDIR]conf\uriworkermap.properties</td><td>ISRegistryComponent1</td><td>0</td></row>
+	</table>
+
+	<table name="RemoveFile">
+		<col key="yes" def="s72">FileKey</col>
+		<col def="s72">Component_</col>
+		<col def="L255">FileName</col>
+		<col def="s72">DirProperty</col>
+		<col def="i2">InstallMode</col>
+	</table>
+
+	<table name="RemoveIniFile">
+		<col key="yes" def="s72">RemoveIniFile</col>
+		<col def="l255">FileName</col>
+		<col def="S72">DirProperty</col>
+		<col def="l96">Section</col>
+		<col def="l128">Key</col>
+		<col def="L255">Value</col>
+		<col def="i2">Action</col>
+		<col def="s72">Component_</col>
+	</table>
+
+	<table name="RemoveRegistry">
+		<col key="yes" def="s72">RemoveRegistry</col>
+		<col def="i2">Root</col>
+		<col def="l255">Key</col>
+		<col def="L255">Name</col>
+		<col def="s72">Component_</col>
+	</table>
+
+	<table name="ReserveCost">
+		<col key="yes" def="s72">ReserveKey</col>
+		<col def="s72">Component_</col>
+		<col def="S72">ReserveFolder</col>
+		<col def="i4">ReserveLocal</col>
+		<col def="i4">ReserveSource</col>
+	</table>
+
+	<table name="SFPCatalog">
+		<col key="yes" def="s255">SFPCatalog</col>
+		<col def="V0">Catalog</col>
+		<col def="S0">Dependency</col>
+	</table>
+
+	<table name="SelfReg">
+		<col key="yes" def="s72">File_</col>
+		<col def="I2">Cost</col>
+	</table>
+
+	<table name="ServiceControl">
+		<col key="yes" def="s72">ServiceControl</col>
+		<col def="s255">Name</col>
+		<col def="i2">Event</col>
+		<col def="S255">Arguments</col>
+		<col def="I2">Wait</col>
+		<col def="s72">Component_</col>
+	</table>
+
+	<table name="ServiceInstall">
+		<col key="yes" def="s72">ServiceInstall</col>
+		<col def="s255">Name</col>
+		<col def="L255">DisplayName</col>
+		<col def="i4">ServiceType</col>
+		<col def="i4">StartType</col>
+		<col def="i4">ErrorControl</col>
+		<col def="S255">LoadOrderGroup</col>
+		<col def="S255">Dependencies</col>
+		<col def="S255">StartName</col>
+		<col def="S255">Password</col>
+		<col def="S255">Arguments</col>
+		<col def="s72">Component_</col>
+		<col def="L255">Description</col>
+	</table>
+
+	<table name="Shortcut">
+		<col key="yes" def="s72">Shortcut</col>
+		<col def="s72">Directory_</col>
+		<col def="l128">Name</col>
+		<col def="s72">Component_</col>
+		<col def="s255">Target</col>
+		<col def="S255">Arguments</col>
+		<col def="L255">Description</col>
+		<col def="I2">Hotkey</col>
+		<col def="S72">Icon_</col>
+		<col def="I2">IconIndex</col>
+		<col def="I2">ShowCmd</col>
+		<col def="S72">WkDir</col>
+		<col def="S255">ISComments</col>
+		<col def="S255">ISShortcutName</col>
+		<col def="I4">ISAttributes</col>
+	</table>
+
+	<table name="Signature">
+		<col key="yes" def="s72">Signature</col>
+		<col def="s255">FileName</col>
+		<col def="S20">MinVersion</col>
+		<col def="S20">MaxVersion</col>
+		<col def="I4">MinSize</col>
+		<col def="I4">MaxSize</col>
+		<col def="I4">MinDate</col>
+		<col def="I4">MaxDate</col>
+		<col def="S255">Languages</col>
+	</table>
+
+	<table name="TextStyle">
+		<col key="yes" def="s72">TextStyle</col>
+		<col def="s32">FaceName</col>
+		<col def="i2">Size</col>
+		<col def="I4">Color</col>
+		<col def="I2">StyleBits</col>
+		<row><td>Arial8</td><td>Arial</td><td>8</td><td/><td/></row>
+		<row><td>Arial9</td><td>Arial</td><td>9</td><td/><td/></row>
+		<row><td>ArialBlue10</td><td>Arial</td><td>10</td><td>16711680</td><td/></row>
+		<row><td>ArialBlueStrike10</td><td>Arial</td><td>10</td><td>16711680</td><td>8</td></row>
+		<row><td>CourierNew8</td><td>Courier New</td><td>8</td><td/><td/></row>
+		<row><td>CourierNew9</td><td>Courier New</td><td>9</td><td/><td/></row>
+		<row><td>MSGothic9</td><td>MS Gothic</td><td>9</td><td/><td/></row>
+		<row><td>MSSGreySerif8</td><td>MS Sans Serif</td><td>8</td><td>8421504</td><td/></row>
+		<row><td>MSSWhiteSerif8</td><td>Tahoma</td><td>8</td><td>16777215</td><td/></row>
+		<row><td>MSSansBold8</td><td>Tahoma</td><td>8</td><td/><td>1</td></row>
+		<row><td>MSSansSerif8</td><td>MS Sans Serif</td><td>8</td><td/><td/></row>
+		<row><td>MSSansSerif9</td><td>MS Sans Serif</td><td>9</td><td/><td/></row>
+		<row><td>Tahoma10</td><td>Tahoma</td><td>10</td><td/><td/></row>
+		<row><td>Tahoma8</td><td>Tahoma</td><td>8</td><td/><td/></row>
+		<row><td>Tahoma9</td><td>Tahoma</td><td>9</td><td/><td/></row>
+		<row><td>TahomaBold10</td><td>Tahoma</td><td>10</td><td/><td>1</td></row>
+		<row><td>TahomaBold8</td><td>Tahoma</td><td>8</td><td/><td>1</td></row>
+		<row><td>Times8</td><td>Times New Roman</td><td>8</td><td/><td/></row>
+		<row><td>Times9</td><td>Times New Roman</td><td>9</td><td/><td/></row>
+		<row><td>TimesItalic12</td><td>Times New Roman</td><td>12</td><td/><td>2</td></row>
+		<row><td>TimesItalicBlue10</td><td>Times New Roman</td><td>10</td><td>16711680</td><td>2</td></row>
+		<row><td>TimesRed16</td><td>Times New Roman</td><td>16</td><td>255</td><td/></row>
+	</table>
+
+	<table name="TypeLib">
+		<col key="yes" def="s38">LibID</col>
+		<col key="yes" def="i2">Language</col>
+		<col key="yes" def="s72">Component_</col>
+		<col def="I4">Version</col>
+		<col def="L128">Description</col>
+		<col def="S72">Directory_</col>
+		<col def="s38">Feature_</col>
+		<col def="I4">Cost</col>
+	</table>
+
+	<table name="UIText">
+		<col key="yes" def="s72">Key</col>
+		<col def="L255">Text</col>
+		<row><td>AbsentPath</td><td/></row>
+		<row><td>GB</td><td>##IDS_UITEXT_GB##</td></row>
+		<row><td>KB</td><td>##IDS_UITEXT_KB##</td></row>
+		<row><td>MB</td><td>##IDS_UITEXT_MB##</td></row>
+		<row><td>MenuAbsent</td><td>##IDS_UITEXT_FeatureNotAvailable##</td></row>
+		<row><td>MenuAdvertise</td><td>##IDS_UITEXT_FeatureInstalledWhenRequired2##</td></row>
+		<row><td>MenuAllCD</td><td>##IDS_UITEXT_FeatureInstalledCD##</td></row>
+		<row><td>MenuAllLocal</td><td>##IDS_UITEXT_FeatureInstalledLocal##</td></row>
+		<row><td>MenuAllNetwork</td><td>##IDS_UITEXT_FeatureInstalledNetwork##</td></row>
+		<row><td>MenuCD</td><td>##IDS_UITEXT_FeatureInstalledCD2##</td></row>
+		<row><td>MenuLocal</td><td>##IDS_UITEXT_FeatureInstalledLocal2##</td></row>
+		<row><td>MenuNetwork</td><td>##IDS_UITEXT_FeatureInstalledNetwork2##</td></row>
+		<row><td>NewFolder</td><td>##IDS_UITEXT_Folder##</td></row>
+		<row><td>SelAbsentAbsent</td><td>##IDS_UITEXT_GB##</td></row>
+		<row><td>SelAbsentAdvertise</td><td>##IDS_UITEXT_FeatureInstalledWhenRequired##</td></row>
+		<row><td>SelAbsentCD</td><td>##IDS_UITEXT_FeatureOnCD##</td></row>
+		<row><td>SelAbsentLocal</td><td>##IDS_UITEXT_FeatureLocal##</td></row>
+		<row><td>SelAbsentNetwork</td><td>##IDS_UITEXT_FeatureNetwork##</td></row>
+		<row><td>SelAdvertiseAbsent</td><td>##IDS_UITEXT_FeatureUnavailable##</td></row>
+		<row><td>SelAdvertiseAdvertise</td><td>##IDS_UITEXT_FeatureInstalledRequired##</td></row>
+		<row><td>SelAdvertiseCD</td><td>##IDS_UITEXT_FeatureOnCD2##</td></row>
+		<row><td>SelAdvertiseLocal</td><td>##IDS_UITEXT_FeatureLocal2##</td></row>
+		<row><td>SelAdvertiseNetwork</td><td>##IDS_UITEXT_FeatureNetwork2##</td></row>
+		<row><td>SelCDAbsent</td><td>##IDS_UITEXT_FeatureWillBeUninstalled##</td></row>
+		<row><td>SelCDAdvertise</td><td>##IDS_UITEXT_FeatureWasCD##</td></row>
+		<row><td>SelCDCD</td><td>##IDS_UITEXT_FeatureRunFromCD##</td></row>
+		<row><td>SelCDLocal</td><td>##IDS_UITEXT_FeatureWasCDLocal##</td></row>
+		<row><td>SelChildCostNeg</td><td>##IDS_UITEXT_FeatureFreeSpace##</td></row>
+		<row><td>SelChildCostPos</td><td>##IDS_UITEXT_FeatureRequiredSpace##</td></row>
+		<row><td>SelCostPending</td><td>##IDS_UITEXT_CompilingFeaturesCost##</td></row>
+		<row><td>SelLocalAbsent</td><td>##IDS_UITEXT_FeatureCompletelyRemoved##</td></row>
+		<row><td>SelLocalAdvertise</td><td>##IDS_UITEXT_FeatureRemovedUnlessRequired##</td></row>
+		<row><td>SelLocalCD</td><td>##IDS_UITEXT_FeatureRemovedCD##</td></row>
+		<row><td>SelLocalLocal</td><td>##IDS_UITEXT_FeatureRemainLocal##</td></row>
+		<row><td>SelLocalNetwork</td><td>##IDS_UITEXT_FeatureRemoveNetwork##</td></row>
+		<row><td>SelNetworkAbsent</td><td>##IDS_UITEXT_FeatureUninstallNoNetwork##</td></row>
+		<row><td>SelNetworkAdvertise</td><td>##IDS_UITEXT_FeatureWasOnNetworkInstalled##</td></row>
+		<row><td>SelNetworkLocal</td><td>##IDS_UITEXT_FeatureWasOnNetworkLocal##</td></row>
+		<row><td>SelNetworkNetwork</td><td>##IDS_UITEXT_FeatureContinueNetwork##</td></row>
+		<row><td>SelParentCostNegNeg</td><td>##IDS_UITEXT_FeatureSpaceFree##</td></row>
+		<row><td>SelParentCostNegPos</td><td>##IDS_UITEXT_FeatureSpaceFree2##</td></row>
+		<row><td>SelParentCostPosNeg</td><td>##IDS_UITEXT_FeatureSpaceFree3##</td></row>
+		<row><td>SelParentCostPosPos</td><td>##IDS_UITEXT_FeatureSpaceFree4##</td></row>
+		<row><td>TimeRemaining</td><td>##IDS_UITEXT_TimeRemaining##</td></row>
+		<row><td>VolumeCostAvailable</td><td>##IDS_UITEXT_Available##</td></row>
+		<row><td>VolumeCostDifference</td><td>##IDS_UITEXT_Differences##</td></row>
+		<row><td>VolumeCostRequired</td><td>##IDS_UITEXT_Required##</td></row>
+		<row><td>VolumeCostSize</td><td>##IDS_UITEXT_DiskSize##</td></row>
+		<row><td>VolumeCostVolume</td><td>##IDS_UITEXT_Volume##</td></row>
+		<row><td>bytes</td><td>##IDS_UITEXT_Bytes##</td></row>
+	</table>
+
+	<table name="Upgrade">
+		<col key="yes" def="s38">UpgradeCode</col>
+		<col key="yes" def="S20">VersionMin</col>
+		<col key="yes" def="S20">VersionMax</col>
+		<col key="yes" def="S255">Language</col>
+		<col key="yes" def="i4">Attributes</col>
+		<col def="S255">Remove</col>
+		<col def="s72">ActionProperty</col>
+		<col def="S72">ISDisplayName</col>
+	</table>
+
+	<table name="Verb">
+		<col key="yes" def="s255">Extension_</col>
+		<col key="yes" def="s32">Verb</col>
+		<col def="I2">Sequence</col>
+		<col def="L255">Command</col>
+		<col def="L255">Argument</col>
+	</table>
+
+	<table name="_Validation">
+		<col key="yes" def="s32">Table</col>
+		<col key="yes" def="s32">Column</col>
+		<col def="s4">Nullable</col>
+		<col def="I4">MinValue</col>
+		<col def="I4">MaxValue</col>
+		<col def="S255">KeyTable</col>
+		<col def="I2">KeyColumn</col>
+		<col def="S32">Category</col>
+		<col def="S255">Set</col>
+		<col def="S255">Description</col>
+		<row><td>ActionText</td><td>Action</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Name of action to be described.</td></row>
+		<row><td>ActionText</td><td>Description</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Localized description displayed in progress dialog and log when action is executing.</td></row>
+		<row><td>ActionText</td><td>Template</td><td>Y</td><td/><td/><td/><td/><td>Template</td><td/><td>Optional localized format template used to format action data records for display during action execution.</td></row>
+		<row><td>AdminExecuteSequence</td><td>Action</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Name of action to invoke, either in the engine or the handler DLL.</td></row>
+		<row><td>AdminExecuteSequence</td><td>Condition</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Optional expression which skips the action if evaluates to expFalse.If the expression syntax is invalid, the engine will terminate, returning iesBadActionData.</td></row>
+		<row><td>AdminExecuteSequence</td><td>ISAttributes</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>This is used to store MM Custom Action Types</td></row>
+		<row><td>AdminExecuteSequence</td><td>ISComments</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Author’s comments on this Sequence.</td></row>
+		<row><td>AdminExecuteSequence</td><td>Sequence</td><td>Y</td><td>-4</td><td>32767</td><td/><td/><td/><td/><td>Number that determines the sort order in which the actions are to be executed.  Leave blank to suppress action.</td></row>
+		<row><td>AdminUISequence</td><td>Action</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Name of action to invoke, either in the engine or the handler DLL.</td></row>
+		<row><td>AdminUISequence</td><td>Condition</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Optional expression which skips the action if evaluates to expFalse.If the expression syntax is invalid, the engine will terminate, returning iesBadActionData.</td></row>
+		<row><td>AdminUISequence</td><td>ISAttributes</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>This is used to store MM Custom Action Types</td></row>
+		<row><td>AdminUISequence</td><td>ISComments</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Author’s comments on this Sequence.</td></row>
+		<row><td>AdminUISequence</td><td>Sequence</td><td>Y</td><td>-4</td><td>32767</td><td/><td/><td/><td/><td>Number that determines the sort order in which the actions are to be executed.  Leave blank to suppress action.</td></row>
+		<row><td>AdvtExecuteSequence</td><td>Action</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Name of action to invoke, either in the engine or the handler DLL.</td></row>
+		<row><td>AdvtExecuteSequence</td><td>Condition</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Optional expression which skips the action if evaluates to expFalse.If the expression syntax is invalid, the engine will terminate, returning iesBadActionData.</td></row>
+		<row><td>AdvtExecuteSequence</td><td>ISAttributes</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>This is used to store MM Custom Action Types</td></row>
+		<row><td>AdvtExecuteSequence</td><td>ISComments</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Author’s comments on this Sequence.</td></row>
+		<row><td>AdvtExecuteSequence</td><td>Sequence</td><td>Y</td><td>-4</td><td>32767</td><td/><td/><td/><td/><td>Number that determines the sort order in which the actions are to be executed.  Leave blank to suppress action.</td></row>
+		<row><td>AdvtUISequence</td><td>Action</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Name of action to invoke, either in the engine or the handler DLL.</td></row>
+		<row><td>AdvtUISequence</td><td>Condition</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Optional expression which skips the action if evaluates to expFalse.If the expression syntax is invalid, the engine will terminate, returning iesBadActionData.</td></row>
+		<row><td>AdvtUISequence</td><td>ISAttributes</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>This is used to store MM Custom Action Types</td></row>
+		<row><td>AdvtUISequence</td><td>ISComments</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Author’s comments on this Sequence.</td></row>
+		<row><td>AdvtUISequence</td><td>Sequence</td><td>Y</td><td>-4</td><td>32767</td><td/><td/><td/><td/><td>Number that determines the sort order in which the actions are to be executed.  Leave blank to suppress action.</td></row>
+		<row><td>AppId</td><td>ActivateAtStorage</td><td>Y</td><td>0</td><td>1</td><td/><td/><td/><td/><td/></row>
+		<row><td>AppId</td><td>AppId</td><td>N</td><td/><td/><td/><td/><td>Guid</td><td/><td/></row>
+		<row><td>AppId</td><td>DllSurrogate</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td/></row>
+		<row><td>AppId</td><td>LocalService</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td/></row>
+		<row><td>AppId</td><td>RemoteServerName</td><td>Y</td><td/><td/><td/><td/><td>Formatted</td><td/><td/></row>
+		<row><td>AppId</td><td>RunAsInteractiveUser</td><td>Y</td><td>0</td><td>1</td><td/><td/><td/><td/><td/></row>
+		<row><td>AppId</td><td>ServiceParameters</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td/></row>
+		<row><td>AppSearch</td><td>Property</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>The property associated with a Signature</td></row>
+		<row><td>AppSearch</td><td>Signature_</td><td>N</td><td/><td/><td>ISXmlLocator;Signature</td><td>1</td><td>Identifier</td><td/><td>The Signature_ represents a unique file signature and is also the foreign key in the Signature,  RegLocator, IniLocator, CompLocator and the DrLocator tables.</td></row>
+		<row><td>BBControl</td><td>Attributes</td><td>Y</td><td>0</td><td>2147483647</td><td/><td/><td/><td/><td>A 32-bit word that specifies the attribute flags to be applied to this control.</td></row>
+		<row><td>BBControl</td><td>BBControl</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Name of the control. This name must be unique within a billboard, but can repeat on different billboard.</td></row>
+		<row><td>BBControl</td><td>Billboard_</td><td>N</td><td/><td/><td>Billboard</td><td>1</td><td>Identifier</td><td/><td>External key to the Billboard table, name of the billboard.</td></row>
+		<row><td>BBControl</td><td>Height</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>Height of the bounding rectangle of the control.</td></row>
+		<row><td>BBControl</td><td>Text</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>A string used to set the initial text contained within a control (if appropriate).</td></row>
+		<row><td>BBControl</td><td>Type</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>The type of the control.</td></row>
+		<row><td>BBControl</td><td>Width</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>Width of the bounding rectangle of the control.</td></row>
+		<row><td>BBControl</td><td>X</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>Horizontal coordinate of the upper left corner of the bounding rectangle of the control.</td></row>
+		<row><td>BBControl</td><td>Y</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>Vertical coordinate of the upper left corner of the bounding rectangle of the control.</td></row>
+		<row><td>Billboard</td><td>Action</td><td>Y</td><td/><td/><td/><td/><td>Identifier</td><td/><td>The name of an action. The billboard is displayed during the progress messages received from this action.</td></row>
+		<row><td>Billboard</td><td>Billboard</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Name of the billboard.</td></row>
+		<row><td>Billboard</td><td>Feature_</td><td>N</td><td/><td/><td>Feature</td><td>1</td><td>Identifier</td><td/><td>An external key to the Feature Table. The billboard is shown only if this feature is being installed.</td></row>
+		<row><td>Billboard</td><td>Ordering</td><td>Y</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>A positive integer. If there is more than one billboard corresponding to an action they will be shown in the order defined by this column.</td></row>
+		<row><td>Binary</td><td>Data</td><td>Y</td><td/><td/><td/><td/><td>Binary</td><td/><td>Binary stream. The binary icon data in PE (.DLL or .EXE) or icon (.ICO) format.</td></row>
+		<row><td>Binary</td><td>ISBuildSourcePath</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Full path to the ICO or EXE file.</td></row>
+		<row><td>Binary</td><td>Name</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Unique key identifying the binary data.</td></row>
+		<row><td>BindImage</td><td>File_</td><td>N</td><td/><td/><td>File</td><td>1</td><td>Identifier</td><td/><td>The index into the File table. This must be an executable file.</td></row>
+		<row><td>BindImage</td><td>Path</td><td>Y</td><td/><td/><td/><td/><td>Paths</td><td/><td>A list of ;  delimited paths that represent the paths to be searched for the import DLLS. The list is usually a list of properties each enclosed within square brackets [] .</td></row>
+		<row><td>CCPSearch</td><td>Signature_</td><td>N</td><td/><td/><td>Signature</td><td>1</td><td>Identifier</td><td/><td>The Signature_ represents a unique file signature and is also the foreign key in the Signature,  RegLocator, IniLocator, CompLocator and the DrLocator tables.</td></row>
+		<row><td>CheckBox</td><td>Property</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>A named property to be tied to the item.</td></row>
+		<row><td>CheckBox</td><td>Value</td><td>Y</td><td/><td/><td/><td/><td>Formatted</td><td/><td>The value string associated with the item.</td></row>
+		<row><td>Class</td><td>AppId_</td><td>Y</td><td/><td/><td>AppId</td><td>1</td><td>Guid</td><td/><td>Optional AppID containing DCOM information for associated application (string GUID).</td></row>
+		<row><td>Class</td><td>Argument</td><td>Y</td><td/><td/><td/><td/><td>Formatted</td><td/><td>optional argument for LocalServers.</td></row>
+		<row><td>Class</td><td>Attributes</td><td>Y</td><td/><td>32767</td><td/><td/><td/><td/><td>Class registration attributes.</td></row>
+		<row><td>Class</td><td>CLSID</td><td>N</td><td/><td/><td/><td/><td>Guid</td><td/><td>The CLSID of an OLE factory.</td></row>
+		<row><td>Class</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Required foreign key into the Component Table, specifying the component for which to return a path when called through LocateComponent.</td></row>
+		<row><td>Class</td><td>Context</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>The numeric server context for this server. CLSCTX_xxxx</td></row>
+		<row><td>Class</td><td>DefInprocHandler</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td>1;2;3</td><td>Optional default inproc handler.  Only optionally provided if Context=CLSCTX_LOCAL_SERVER.  Typically "ole32.dll" or "mapi32.dll"</td></row>
+		<row><td>Class</td><td>Description</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Localized description for the Class.</td></row>
+		<row><td>Class</td><td>Feature_</td><td>N</td><td/><td/><td>Feature</td><td>1</td><td>Identifier</td><td/><td>Required foreign key into the Feature Table, specifying the feature to validate or install in order for the CLSID factory to be operational.</td></row>
+		<row><td>Class</td><td>FileTypeMask</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Optional string containing information for the HKCRthis CLSID) key. If multiple patterns exist, they must be delimited by a semicolon, and numeric subkeys will be generated: 0,1,2...</td></row>
+		<row><td>Class</td><td>IconIndex</td><td>Y</td><td>-32767</td><td>32767</td><td/><td/><td/><td/><td>Optional icon index.</td></row>
+		<row><td>Class</td><td>Icon_</td><td>Y</td><td/><td/><td>Icon</td><td>1</td><td>Identifier</td><td/><td>Optional foreign key into the Icon Table, specifying the icon file associated with this CLSID. Will be written under the DefaultIcon key.</td></row>
+		<row><td>Class</td><td>ProgId_Default</td><td>Y</td><td/><td/><td>ProgId</td><td>1</td><td>Text</td><td/><td>Optional ProgId associated with this CLSID.</td></row>
+		<row><td>ComboBox</td><td>Order</td><td>N</td><td>1</td><td>32767</td><td/><td/><td/><td/><td>A positive integer used to determine the ordering of the items within one list.	The integers do not have to be consecutive.</td></row>
+		<row><td>ComboBox</td><td>Property</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>A named property to be tied to this item. All the items tied to the same property become part of the same combobox.</td></row>
+		<row><td>ComboBox</td><td>Text</td><td>Y</td><td/><td/><td/><td/><td>Formatted</td><td/><td>The visible text to be assigned to the item. Optional. If this entry or the entire column is missing, the text is the same as the value.</td></row>
+		<row><td>ComboBox</td><td>Value</td><td>N</td><td/><td/><td/><td/><td>Formatted</td><td/><td>The value string associated with this item. Selecting the line will set the associated property to this value.</td></row>
+		<row><td>CompLocator</td><td>ComponentId</td><td>N</td><td/><td/><td/><td/><td>Guid</td><td/><td>A string GUID unique to this component, version, and language.</td></row>
+		<row><td>CompLocator</td><td>Signature_</td><td>N</td><td/><td/><td>Signature</td><td>1</td><td>Identifier</td><td/><td>The table key. The Signature_ represents a unique file signature and is also the foreign key in the Signature table.</td></row>
+		<row><td>CompLocator</td><td>Type</td><td>Y</td><td>0</td><td>1</td><td/><td/><td/><td/><td>A boolean value that determines if the registry value is a filename or a directory location.</td></row>
+		<row><td>Complus</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Foreign key referencing Component that controls the ComPlus component.</td></row>
+		<row><td>Complus</td><td>ExpType</td><td>Y</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>ComPlus component attributes.</td></row>
+		<row><td>Component</td><td>Attributes</td><td>N</td><td/><td/><td/><td/><td/><td/><td>Remote execution option, one of irsEnum</td></row>
+		<row><td>Component</td><td>Component</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Primary key used to identify a particular component record.</td></row>
+		<row><td>Component</td><td>ComponentId</td><td>Y</td><td/><td/><td/><td/><td>Guid</td><td/><td>A string GUID unique to this component, version, and language.</td></row>
+		<row><td>Component</td><td>Condition</td><td>Y</td><td/><td/><td/><td/><td>Condition</td><td/><td>A conditional statement that will disable this component if the specified condition evaluates to the 'True' state. If a component is disabled, it will not be installed, regardless of the 'Action' state associated with the component.</td></row>
+		<row><td>Component</td><td>Directory_</td><td>N</td><td/><td/><td>Directory</td><td>1</td><td>Identifier</td><td/><td>Required key of a Directory table record. This is actually a property name whose value contains the actual path, set either by the AppSearch action or with the default setting obtained from the Directory table.</td></row>
+		<row><td>Component</td><td>ISAttributes</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>This is used to store Installshield custom properties of a component.</td></row>
+		<row><td>Component</td><td>ISComments</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>User Comments.</td></row>
+		<row><td>Component</td><td>ISDotNetInstallerArgsCommit</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Arguments passed to the key file of the component if if implements the .NET Installer class</td></row>
+		<row><td>Component</td><td>ISDotNetInstallerArgsInstall</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Arguments passed to the key file of the component if if implements the .NET Installer class</td></row>
+		<row><td>Component</td><td>ISDotNetInstallerArgsRollback</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Arguments passed to the key file of the component if if implements the .NET Installer class</td></row>
+		<row><td>Component</td><td>ISDotNetInstallerArgsUninstall</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Arguments passed to the key file of the component if if implements the .NET Installer class</td></row>
+		<row><td>Component</td><td>ISRegFileToMergeAtBuild</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Path and File name of a .REG file to merge into the component at build time.</td></row>
+		<row><td>Component</td><td>ISScanAtBuildFile</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>File used by the Dot Net scanner to populate dependant assemblies' File_Application field.</td></row>
+		<row><td>Component</td><td>KeyPath</td><td>Y</td><td/><td/><td>File;ODBCDataSource;Registry</td><td>1</td><td>Identifier</td><td/><td>Either the primary key into the File table, Registry table, or ODBCDataSource table. This extract path is stored when the component is installed, and is used to detect the presence of the component and to return the path to it.</td></row>
+		<row><td>Condition</td><td>Condition</td><td>Y</td><td/><td/><td/><td/><td>Condition</td><td/><td>Expression evaluated to determine if Level in the Feature table is to change.</td></row>
+		<row><td>Condition</td><td>Feature_</td><td>N</td><td/><td/><td>Feature</td><td>1</td><td>Identifier</td><td/><td>Reference to a Feature entry in Feature table.</td></row>
+		<row><td>Condition</td><td>Level</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>New selection Level to set in Feature table if Condition evaluates to TRUE.</td></row>
+		<row><td>Control</td><td>Attributes</td><td>Y</td><td>0</td><td>2147483647</td><td/><td/><td/><td/><td>A 32-bit word that specifies the attribute flags to be applied to this control.</td></row>
+		<row><td>Control</td><td>Binary_</td><td>Y</td><td/><td/><td>Binary</td><td>1</td><td>Identifier</td><td/><td>External key to the Binary table.</td></row>
+		<row><td>Control</td><td>Control</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Name of the control. This name must be unique within a dialog, but can repeat on different dialogs. </td></row>
+		<row><td>Control</td><td>Control_Next</td><td>Y</td><td/><td/><td>Control</td><td>2</td><td>Identifier</td><td/><td>The name of an other control on the same dialog. This link defines the tab order of the controls. The links have to form one or more cycles!</td></row>
+		<row><td>Control</td><td>Dialog_</td><td>N</td><td/><td/><td>Dialog</td><td>1</td><td>Identifier</td><td/><td>External key to the Dialog table, name of the dialog.</td></row>
+		<row><td>Control</td><td>Height</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>Height of the bounding rectangle of the control.</td></row>
+		<row><td>Control</td><td>Help</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>The help strings used with the button. The text is optional. </td></row>
+		<row><td>Control</td><td>ISBuildSourcePath</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Full path to .rtf file for scrollable text control</td></row>
+		<row><td>Control</td><td>ISControlId</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>A number used to represent the control ID of the Control, Used in Dialog export</td></row>
+		<row><td>Control</td><td>ISWindowStyle</td><td>Y</td><td>0</td><td>2147483647</td><td/><td/><td/><td/><td>A 32-bit word that specifies non-MSI window styles to be applied to this control.</td></row>
+		<row><td>Control</td><td>Property</td><td>Y</td><td/><td/><td/><td/><td>Identifier</td><td/><td>The name of a defined property to be linked to this control. </td></row>
+		<row><td>Control</td><td>Text</td><td>Y</td><td/><td/><td/><td/><td>Formatted</td><td/><td>A string used to set the initial text contained within a control (if appropriate).</td></row>
+		<row><td>Control</td><td>Type</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>The type of the control.</td></row>
+		<row><td>Control</td><td>Width</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>Width of the bounding rectangle of the control.</td></row>
+		<row><td>Control</td><td>X</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>Horizontal coordinate of the upper left corner of the bounding rectangle of the control.</td></row>
+		<row><td>Control</td><td>Y</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>Vertical coordinate of the upper left corner of the bounding rectangle of the control.</td></row>
+		<row><td>ControlCondition</td><td>Action</td><td>N</td><td/><td/><td/><td/><td/><td>Default;Disable;Enable;Hide;Show</td><td>The desired action to be taken on the specified control.</td></row>
+		<row><td>ControlCondition</td><td>Condition</td><td>N</td><td/><td/><td/><td/><td>Condition</td><td/><td>A standard conditional statement that specifies under which conditions the action should be triggered.</td></row>
+		<row><td>ControlCondition</td><td>Control_</td><td>N</td><td/><td/><td>Control</td><td>2</td><td>Identifier</td><td/><td>A foreign key to the Control table, name of the control.</td></row>
+		<row><td>ControlCondition</td><td>Dialog_</td><td>N</td><td/><td/><td>Dialog</td><td>1</td><td>Identifier</td><td/><td>A foreign key to the Dialog table, name of the dialog.</td></row>
+		<row><td>ControlEvent</td><td>Argument</td><td>N</td><td/><td/><td/><td/><td>Formatted</td><td/><td>A value to be used as a modifier when triggering a particular event.</td></row>
+		<row><td>ControlEvent</td><td>Condition</td><td>Y</td><td/><td/><td/><td/><td>Condition</td><td/><td>A standard conditional statement that specifies under which conditions an event should be triggered.</td></row>
+		<row><td>ControlEvent</td><td>Control_</td><td>N</td><td/><td/><td>Control</td><td>2</td><td>Identifier</td><td/><td>A foreign key to the Control table, name of the control</td></row>
+		<row><td>ControlEvent</td><td>Dialog_</td><td>N</td><td/><td/><td>Dialog</td><td>1</td><td>Identifier</td><td/><td>A foreign key to the Dialog table, name of the dialog.</td></row>
+		<row><td>ControlEvent</td><td>Event</td><td>N</td><td/><td/><td/><td/><td>Formatted</td><td/><td>An identifier that specifies the type of the event that should take place when the user interacts with control specified by the first two entries.</td></row>
+		<row><td>ControlEvent</td><td>Ordering</td><td>Y</td><td>0</td><td>2147483647</td><td/><td/><td/><td/><td>An integer used to order several events tied to the same control. Can be left blank.</td></row>
+		<row><td>CreateFolder</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Foreign key into the Component table.</td></row>
+		<row><td>CreateFolder</td><td>Directory_</td><td>N</td><td/><td/><td>Directory</td><td>1</td><td>Identifier</td><td/><td>Primary key, could be foreign key into the Directory table.</td></row>
+		<row><td>CustomAction</td><td>Action</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Primary key, name of action, normally appears in sequence table unless private use.</td></row>
+		<row><td>CustomAction</td><td>ISComments</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Author’s comments for this custom action.</td></row>
+		<row><td>CustomAction</td><td>Source</td><td>Y</td><td/><td/><td/><td/><td>CustomSource</td><td/><td>The table reference of the source of the code.</td></row>
+		<row><td>CustomAction</td><td>Target</td><td>Y</td><td/><td/><td>ISDLLWrapper;ISInstallScriptAction</td><td>1</td><td>Formatted</td><td/><td>Excecution parameter, depends on the type of custom action</td></row>
+		<row><td>CustomAction</td><td>Type</td><td>N</td><td>1</td><td>32767</td><td/><td/><td/><td/><td>The numeric custom action type, consisting of source location, code type, entry, option flags.</td></row>
+		<row><td>Dialog</td><td>Attributes</td><td>Y</td><td>0</td><td>2147483647</td><td/><td/><td/><td/><td>A 32-bit word that specifies the attribute flags to be applied to this dialog.</td></row>
+		<row><td>Dialog</td><td>Control_Cancel</td><td>Y</td><td/><td/><td>Control</td><td>2</td><td>Identifier</td><td/><td>Defines the cancel control. Hitting escape or clicking on the close icon on the dialog is equivalent to pushing this button.</td></row>
+		<row><td>Dialog</td><td>Control_Default</td><td>Y</td><td/><td/><td>Control</td><td>2</td><td>Identifier</td><td/><td>Defines the default control. Hitting return is equivalent to pushing this button.</td></row>
+		<row><td>Dialog</td><td>Control_First</td><td>N</td><td/><td/><td>Control</td><td>2</td><td>Identifier</td><td/><td>Defines the control that has the focus when the dialog is created.</td></row>
+		<row><td>Dialog</td><td>Dialog</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Name of the dialog.</td></row>
+		<row><td>Dialog</td><td>HCentering</td><td>N</td><td>0</td><td>100</td><td/><td/><td/><td/><td>Horizontal position of the dialog on a 0-100 scale. 0 means left end, 100 means right end of the screen, 50 center.</td></row>
+		<row><td>Dialog</td><td>Height</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>Height of the bounding rectangle of the dialog.</td></row>
+		<row><td>Dialog</td><td>ISComments</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Author’s comments for this dialog.</td></row>
+		<row><td>Dialog</td><td>ISResourceId</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>A Number the Specifies the Dialog ID to be used in Dialog Export</td></row>
+		<row><td>Dialog</td><td>ISWindowStyle</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>A 32-bit word that specifies non-MSI window styles to be applied to this control. This is only used in Script Based Setups.</td></row>
+		<row><td>Dialog</td><td>TextStyle_</td><td>Y</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Foreign Key into TextStyle table, only used in Script Based Projects.</td></row>
+		<row><td>Dialog</td><td>Title</td><td>Y</td><td/><td/><td/><td/><td>Formatted</td><td/><td>A text string specifying the title to be displayed in the title bar of the dialog's window.</td></row>
+		<row><td>Dialog</td><td>VCentering</td><td>N</td><td>0</td><td>100</td><td/><td/><td/><td/><td>Vertical position of the dialog on a 0-100 scale. 0 means top end, 100 means bottom end of the screen, 50 center.</td></row>
+		<row><td>Dialog</td><td>Width</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>Width of the bounding rectangle of the dialog.</td></row>
+		<row><td>Directory</td><td>DefaultDir</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>The default sub-path under parent's path.</td></row>
+		<row><td>Directory</td><td>Directory</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Unique identifier for directory entry, primary key. If a property by this name is defined, it contains the full path to the directory.</td></row>
+		<row><td>Directory</td><td>Directory_Parent</td><td>Y</td><td/><td/><td>Directory</td><td>1</td><td>Identifier</td><td/><td>Reference to the entry in this table specifying the default parent directory. A record parented to itself or with a Null parent represents a root of the install tree.</td></row>
+		<row><td>Directory</td><td>ISAttributes</td><td>Y</td><td/><td/><td/><td/><td/><td>0;1;2;3;4;5;6;7</td><td>This is used to store Installshield custom properties of a directory.  Currently the only one is Shortcut.</td></row>
+		<row><td>Directory</td><td>ISDescription</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Description of folder</td></row>
+		<row><td>Directory</td><td>ISFolderName</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>This is used in Pro projects because the pro identifier used in the tree wasn't necessarily unique.</td></row>
+		<row><td>DrLocator</td><td>Depth</td><td>Y</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>The depth below the path to which the Signature_ is recursively searched. If absent, the depth is assumed to be 0.</td></row>
+		<row><td>DrLocator</td><td>Parent</td><td>Y</td><td/><td/><td/><td/><td>Identifier</td><td/><td>The parent file signature. It is also a foreign key in the Signature table. If null and the Path column does not expand to a full path, then all the fixed drives of the user system are searched using the Path.</td></row>
+		<row><td>DrLocator</td><td>Path</td><td>Y</td><td/><td/><td/><td/><td>AnyPath</td><td/><td>The path on the user system. This is a either a subpath below the value of the Parent or a full path. The path may contain properties enclosed within [ ] that will be expanded.</td></row>
+		<row><td>DrLocator</td><td>Signature_</td><td>N</td><td/><td/><td>Signature</td><td>1</td><td>Identifier</td><td/><td>The Signature_ represents a unique file signature and is also the foreign key in the Signature table.</td></row>
+		<row><td>DuplicateFile</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Foreign key referencing Component that controls the duplicate file.</td></row>
+		<row><td>DuplicateFile</td><td>DestFolder</td><td>Y</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Name of a property whose value is assumed to resolve to the full pathname to a destination folder.</td></row>
+		<row><td>DuplicateFile</td><td>DestName</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Filename to be given to the duplicate file.</td></row>
+		<row><td>DuplicateFile</td><td>FileKey</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Primary key used to identify a particular file entry</td></row>
+		<row><td>DuplicateFile</td><td>File_</td><td>N</td><td/><td/><td>File</td><td>1</td><td>Identifier</td><td/><td>Foreign key referencing the source file to be duplicated.</td></row>
+		<row><td>Environment</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Foreign key into the Component table referencing component that controls the installing of the environmental value.</td></row>
+		<row><td>Environment</td><td>Environment</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Unique identifier for the environmental variable setting</td></row>
+		<row><td>Environment</td><td>Name</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>The name of the environmental value.</td></row>
+		<row><td>Environment</td><td>Value</td><td>Y</td><td/><td/><td/><td/><td>Formatted</td><td/><td>The value to set in the environmental settings.</td></row>
+		<row><td>Error</td><td>Error</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>Integer error number, obtained from header file IError(...) macros.</td></row>
+		<row><td>Error</td><td>Message</td><td>Y</td><td/><td/><td/><td/><td>Template</td><td/><td>Error formatting template, obtained from user ed. or localizers.</td></row>
+		<row><td>EventMapping</td><td>Attribute</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>The name of the control attribute, that is set when this event is received.</td></row>
+		<row><td>EventMapping</td><td>Control_</td><td>N</td><td/><td/><td>Control</td><td>2</td><td>Identifier</td><td/><td>A foreign key to the Control table, name of the control.</td></row>
+		<row><td>EventMapping</td><td>Dialog_</td><td>N</td><td/><td/><td>Dialog</td><td>1</td><td>Identifier</td><td/><td>A foreign key to the Dialog table, name of the Dialog.</td></row>
+		<row><td>EventMapping</td><td>Event</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>An identifier that specifies the type of the event that the control subscribes to.</td></row>
+		<row><td>Extension</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Required foreign key into the Component Table, specifying the component for which to return a path when called through LocateComponent.</td></row>
+		<row><td>Extension</td><td>Extension</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>The extension associated with the table row.</td></row>
+		<row><td>Extension</td><td>Feature_</td><td>N</td><td/><td/><td>Feature</td><td>1</td><td>Identifier</td><td/><td>Required foreign key into the Feature Table, specifying the feature to validate or install in order for the CLSID factory to be operational.</td></row>
+		<row><td>Extension</td><td>MIME_</td><td>Y</td><td/><td/><td>MIME</td><td>1</td><td>Text</td><td/><td>Optional Context identifier, typically "type/format" associated with the extension</td></row>
+		<row><td>Extension</td><td>ProgId_</td><td>Y</td><td/><td/><td>ProgId</td><td>1</td><td>Text</td><td/><td>Optional ProgId associated with this extension.</td></row>
+		<row><td>Feature</td><td>Attributes</td><td>N</td><td/><td/><td/><td/><td/><td>0;1;2;4;5;6;8;9;10;16;17;18;20;21;22;24;25;26;32;33;34;36;37;38;48;49;50;52;53;54</td><td>Feature attributes</td></row>
+		<row><td>Feature</td><td>Description</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Longer descriptive text describing a visible feature item.</td></row>
+		<row><td>Feature</td><td>Directory_</td><td>Y</td><td/><td/><td>Directory</td><td>1</td><td>UpperCase</td><td/><td>The name of the Directory that can be configured by the UI. A non-null value will enable the browse button.</td></row>
+		<row><td>Feature</td><td>Display</td><td>Y</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>Numeric sort order, used to force a specific display ordering.</td></row>
+		<row><td>Feature</td><td>Feature</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Primary key used to identify a particular feature record.</td></row>
+		<row><td>Feature</td><td>Feature_Parent</td><td>Y</td><td/><td/><td>Feature</td><td>1</td><td>Identifier</td><td/><td>Optional key of a parent record in the same table. If the parent is not selected, then the record will not be installed. Null indicates a root item.</td></row>
+		<row><td>Feature</td><td>ISComments</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>Comments</td></row>
+		<row><td>Feature</td><td>ISFeatureCabName</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>Name of CAB used when compressing CABs by Feature. Used to override build generated name for CAB file.</td></row>
+		<row><td>Feature</td><td>ISProFeatureName</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>The name of the feature used by pro projects.  This doesn't have to be unique.</td></row>
+		<row><td>Feature</td><td>ISReleaseFlags</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>Release Flags that specify whether this  feature will be built in a particular release.</td></row>
+		<row><td>Feature</td><td>Level</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>The install level at which record will be initially selected. An install level of 0 will disable an item and prevent its display.</td></row>
+		<row><td>Feature</td><td>Title</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Short text identifying a visible feature item.</td></row>
+		<row><td>FeatureComponents</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Foreign key into Component table.</td></row>
+		<row><td>FeatureComponents</td><td>Feature_</td><td>N</td><td/><td/><td>Feature</td><td>1</td><td>Identifier</td><td/><td>Foreign key into Feature table.</td></row>
+		<row><td>File</td><td>Attributes</td><td>Y</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>Integer containing bit flags representing file attributes (with the decimal value of each bit position in parentheses)</td></row>
+		<row><td>File</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Foreign key referencing Component that controls the file.</td></row>
+		<row><td>File</td><td>File</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Primary key, non-localized token, must match identifier in cabinet.  For uncompressed files, this field is ignored.</td></row>
+		<row><td>File</td><td>FileName</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>File name used for installation.  This may contain a "short name|long name" pair.  It may be just a long name, hence it cannot be of the Filename data type.</td></row>
+		<row><td>File</td><td>FileSize</td><td>N</td><td>0</td><td>2147483647</td><td/><td/><td/><td/><td>Size of file in bytes (long integer).</td></row>
+		<row><td>File</td><td>ISAttributes</td><td>Y</td><td>0</td><td>2147483647</td><td/><td/><td/><td/><td>This field contains the following attributes: UseSystemSettings(0x1)</td></row>
+		<row><td>File</td><td>ISBuildSourcePath</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Full path, the category is of Text instead of Path because of potential use of path variables.</td></row>
+		<row><td>File</td><td>ISComponentSubFolder_</td><td>Y</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Foreign key referencing component subfolder containing this file.  Only for Pro.</td></row>
+		<row><td>File</td><td>Language</td><td>Y</td><td/><td/><td/><td/><td>Language</td><td/><td>List of decimal language Ids, comma-separated if more than one.</td></row>
+		<row><td>File</td><td>Sequence</td><td>N</td><td>1</td><td>32767</td><td/><td/><td/><td/><td>Sequence with respect to the media images; order must track cabinet order.</td></row>
+		<row><td>File</td><td>Version</td><td>Y</td><td/><td/><td>File</td><td>1</td><td>Version</td><td/><td>Version string for versioned files;  Blank for unversioned files.</td></row>
+		<row><td>FileSFPCatalog</td><td>File_</td><td>N</td><td/><td/><td>File</td><td>1</td><td>Identifier</td><td/><td>File associated with the catalog</td></row>
+		<row><td>FileSFPCatalog</td><td>SFPCatalog_</td><td>N</td><td/><td/><td>SFPCatalog</td><td>1</td><td>Text</td><td/><td>Catalog associated with the file</td></row>
+		<row><td>Font</td><td>File_</td><td>N</td><td/><td/><td>File</td><td>1</td><td>Identifier</td><td/><td>Primary key, foreign key into File table referencing font file.</td></row>
+		<row><td>Font</td><td>FontTitle</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Font name.</td></row>
+		<row><td>ISAssistantTag</td><td>Data</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISAssistantTag</td><td>Tag</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEApp</td><td>AppKey</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEApp</td><td>AppName</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEApp</td><td>Attributes</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEApp</td><td>CompanyName</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEApp</td><td>Component_</td><td>Y</td><td/><td/><td>Component</td><td>1</td><td/><td/><td/></row>
+		<row><td>ISCEApp</td><td>DefDir</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEApp</td><td>DeleteMedia</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEApp</td><td>Description</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEApp</td><td>DesktopTargetDir</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEApp</td><td>DeviceFile</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEApp</td><td>IconIndex</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEApp</td><td>IconPath</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEApp</td><td>InstallNetCF</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEApp</td><td>InstallSQLClient</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEApp</td><td>InstallSQLDev</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEApp</td><td>InstallSQLServer</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEApp</td><td>NoUninstall</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEApp</td><td>PVKFile</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEApp</td><td>PostXML</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEApp</td><td>PreXML</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEApp</td><td>RawDeviceFile</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEApp</td><td>SPCFile</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEDir</td><td>AppKey</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEDir</td><td>DirKey</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEDir</td><td>DirParent</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEDir</td><td>DirValue</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEFile</td><td>AdvancedOptions</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEFile</td><td>AppKey</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEFile</td><td>CopyOption</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEFile</td><td>Destination</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEFile</td><td>FileKey</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEFile</td><td>FileOption</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEFile</td><td>Name</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEFile</td><td>Platform</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEFile</td><td>Processor</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEFile</td><td>Source</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEFileExt</td><td>AppKey</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEFileExt</td><td>Description</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEFileExt</td><td>ExtKey</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEFileExt</td><td>Extension</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEFileExt</td><td>FileKey</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEFileExt</td><td>IconIndex</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEInstall</td><td>CEAppName</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEInstall</td><td>CECabs</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEInstall</td><td>CEDesktopDir</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEInstall</td><td>CEIcoFile</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEInstall</td><td>CEIniFileKey</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEInstall</td><td>CEInstallKey</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEInstall</td><td>Component_</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEInstall</td><td>DeleteMedia</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEOtherAppCABs</td><td>AppKey</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEOtherAppCABs</td><td>BuildSourcePath</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEOtherAppCABs</td><td>FileKey</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCERegistry</td><td>AppKey</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCERegistry</td><td>Key</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCERegistry</td><td>Name</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCERegistry</td><td>Overwrite</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCERegistry</td><td>Platform</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCERegistry</td><td>Processor</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCERegistry</td><td>RegKey</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCERegistry</td><td>Root</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCERegistry</td><td>Value</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCESetupFile</td><td>AppKey</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCESetupFile</td><td>Name</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCESetupFile</td><td>Platform</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCESetupFile</td><td>Processor</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCESetupFile</td><td>SetupFileKey</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCESetupFile</td><td>Source</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEShtCut</td><td>AppKey</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEShtCut</td><td>Destination</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEShtCut</td><td>DisplayName</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEShtCut</td><td>Platform</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEShtCut</td><td>ShtCutKey</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISCEShtCut</td><td>Target</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISComCatalogAttribute</td><td>ISComCatalogObject_</td><td>N</td><td/><td/><td>ISComCatalogObject</td><td>1</td><td>Identifier</td><td/><td>Foreign key into the ISComCatalogObject table.</td></row>
+		<row><td>ISComCatalogAttribute</td><td>ItemName</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>The named attribute for a catalog object.</td></row>
+		<row><td>ISComCatalogAttribute</td><td>ItemValue</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>A value associated with the attribute defined in the ItemName column.</td></row>
+		<row><td>ISComCatalogCollection</td><td>CollectionName</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>A catalog collection name.</td></row>
+		<row><td>ISComCatalogCollection</td><td>ISComCatalogCollection</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>A unique key for the ISComCatalogCollection table.</td></row>
+		<row><td>ISComCatalogCollection</td><td>ISComCatalogObject_</td><td>N</td><td/><td/><td>ISComCatalogObject</td><td>1</td><td>Identifier</td><td/><td>Foreign key into the ISComCatalogObject table.</td></row>
+		<row><td>ISComCatalogCollectionObjects</td><td>ISComCatalogCollection_</td><td>N</td><td/><td/><td>ISComCatalogCollection</td><td>1</td><td>Identifier</td><td/><td>A unique key for the ISComCatalogCollection table.</td></row>
+		<row><td>ISComCatalogCollectionObjects</td><td>ISComCatalogObject_</td><td>N</td><td/><td/><td>ISComCatalogObject</td><td>1</td><td>Identifier</td><td/><td>Foreign key into the ISComCatalogObject table.</td></row>
+		<row><td>ISComCatalogObject</td><td>DisplayName</td><td>N</td><td/><td/><td/><td/><td/><td/><td>The display name of a catalog object.</td></row>
+		<row><td>ISComCatalogObject</td><td>ISComCatalogObject</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>A unique key for the ISComCatalogObject table.</td></row>
+		<row><td>ISComPlusApplication</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Foreign key into the Component table that a COM+ application belongs to.</td></row>
+		<row><td>ISComPlusApplication</td><td>ComputerName</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Computer name that a COM+ application belongs to.</td></row>
+		<row><td>ISComPlusApplication</td><td>DepFiles</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>List of the dependent files.</td></row>
+		<row><td>ISComPlusApplication</td><td>ISAttributes</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>InstallShield custom attributes associated with a COM+ application.</td></row>
+		<row><td>ISComPlusApplication</td><td>ISComCatalogObject_</td><td>N</td><td/><td/><td>ISComCatalogObject</td><td>1</td><td>Identifier</td><td/><td>Foreign key into the ISComCatalogObject table.</td></row>
+		<row><td>ISComPlusProxy</td><td>Component_</td><td>Y</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Foreign key into the Component table that a COM+ application proxy belongs to.</td></row>
+		<row><td>ISComPlusProxy</td><td>DepFiles</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>List of the dependent files.</td></row>
+		<row><td>ISComPlusProxy</td><td>ISAttributes</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>InstallShield custom attributes associated with a COM+ application proxy.</td></row>
+		<row><td>ISComPlusProxy</td><td>ISComPlusApplication_</td><td>N</td><td/><td/><td>ISComPlusApplication</td><td>1</td><td>Identifier</td><td/><td>Foreign key into the ISComPlusApplication table that a COM+ application proxy belongs to.</td></row>
+		<row><td>ISComPlusProxy</td><td>ISComPlusProxy</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>A unique key for the ISComPlusProxy table.</td></row>
+		<row><td>ISComponentExtended</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Primary key used to identify a particular component record.</td></row>
+		<row><td>ISComponentExtended</td><td>FTPLocation</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>FTP Location</td></row>
+		<row><td>ISComponentExtended</td><td>FilterProperty</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Property to set if you want to filter a component</td></row>
+		<row><td>ISComponentExtended</td><td>HTTPLocation</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>HTTP Location</td></row>
+		<row><td>ISComponentExtended</td><td>Language</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Language</td></row>
+		<row><td>ISComponentExtended</td><td>Miscellaneous</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Miscellaneous</td></row>
+		<row><td>ISComponentExtended</td><td>OS</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>bitwise addition of OSs</td></row>
+		<row><td>ISComponentExtended</td><td>Platforms</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>bitwise addition of Platforms.</td></row>
+		<row><td>ISDIMDependency</td><td>ISDIMReference_</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>This is the primary key to the ISDIMDependency table</td></row>
+		<row><td>ISDIMDependency</td><td>RequiredBuildVersion</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>the build version identifying the required DIM</td></row>
+		<row><td>ISDIMDependency</td><td>RequiredMajorVersion</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>the major version identifying the required DIM</td></row>
+		<row><td>ISDIMDependency</td><td>RequiredMinorVersion</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>the minor version identifying the required DIM</td></row>
+		<row><td>ISDIMDependency</td><td>RequiredRevisionVersion</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>the revision version identifying the required DIM</td></row>
+		<row><td>ISDIMDependency</td><td>RequiredUUID</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>the UUID identifying the required DIM</td></row>
+		<row><td>ISDIMReference</td><td>ISBuildSourcePath</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Full path, the category is of Text instead of Path because of potential use of path variables.</td></row>
+		<row><td>ISDIMReference</td><td>ISDIMReference</td><td>N</td><td/><td/><td>ISDIMDependency</td><td>1</td><td>Identifier</td><td/><td>This is the primary key to the ISDIMReference table</td></row>
+		<row><td>ISDIMReferenceDependencies</td><td>ISDIMDependency_</td><td>N</td><td/><td/><td>ISDIMDependency</td><td>1</td><td>Identifier</td><td/><td>Foreign key into ISDIMDependency table.</td></row>
+		<row><td>ISDIMReferenceDependencies</td><td>ISDIMReference_Parent</td><td>N</td><td/><td/><td>ISDIMReference</td><td>1</td><td>Identifier</td><td/><td>Foreign key into ISDIMReference table.</td></row>
+		<row><td>ISDIMVariable</td><td>ISDIMReference_</td><td>N</td><td/><td/><td>ISDIMReference</td><td>1</td><td>Identifier</td><td/><td>Foreign key into ISDIMReference table.</td></row>
+		<row><td>ISDIMVariable</td><td>ISDIMVariable</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>This is the primary key to the ISDIMVariable table</td></row>
+		<row><td>ISDIMVariable</td><td>Name</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Name of a variable defined in the .dim file</td></row>
+		<row><td>ISDIMVariable</td><td>NewValue</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>New value that you want to override with</td></row>
+		<row><td>ISDIMVariable</td><td>Type</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>Type of the variable. 0: Build Variable, 1: Runtime Variable</td></row>
+		<row><td>ISDLLWrapper</td><td>EntryPoint</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>This is a foreign key to the target column in the CustomAction table</td></row>
+		<row><td>ISDLLWrapper</td><td>Source</td><td>N</td><td/><td/><td/><td/><td>Formatted</td><td/><td>This is column points to the source file for the DLLWrapper Custom Action</td></row>
+		<row><td>ISDLLWrapper</td><td>Target</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>The function signature</td></row>
+		<row><td>ISDLLWrapper</td><td>Type</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>Type</td></row>
+		<row><td>ISDRMFile</td><td>File_</td><td>Y</td><td/><td/><td>File</td><td>1</td><td>Identifier</td><td/><td>Foreign key into File table.  A null value will cause a build warning.</td></row>
+		<row><td>ISDRMFile</td><td>ISDRMFile</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Unique identifier for this item.</td></row>
+		<row><td>ISDRMFile</td><td>ISDRMLicense_</td><td>Y</td><td/><td/><td>ISDRMLicense</td><td>1</td><td>Identifier</td><td/><td>Foreign key referencing License that packages this file.</td></row>
+		<row><td>ISDRMFile</td><td>Shell</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Text indicating the activation shell used at runtime.</td></row>
+		<row><td>ISDRMFileAttribute</td><td>ISDRMFile_</td><td>N</td><td/><td/><td>ISDRMFile</td><td>1</td><td>Identifier</td><td/><td>Primary foreign key into ISDRMFile table.</td></row>
+		<row><td>ISDRMFileAttribute</td><td>Property</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>The name of the attribute</td></row>
+		<row><td>ISDRMFileAttribute</td><td>Value</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>The value of the attribute</td></row>
+		<row><td>ISDRMLicense</td><td>Attributes</td><td>Y</td><td/><td/><td/><td/><td>Number</td><td/><td>Bitwise field used to specify binary attributes of this license.</td></row>
+		<row><td>ISDRMLicense</td><td>Description</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>An internal description of this license.</td></row>
+		<row><td>ISDRMLicense</td><td>ISDRMLicense</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Unique key identifying the license record.</td></row>
+		<row><td>ISDRMLicense</td><td>LicenseNumber</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>The license number.</td></row>
+		<row><td>ISDRMLicense</td><td>ProjectVersion</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>The version of the project that this license is tied to.</td></row>
+		<row><td>ISDRMLicense</td><td>RequestCode</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>The request code.</td></row>
+		<row><td>ISDRMLicense</td><td>ResponseCode</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>The response code.</td></row>
+		<row><td>ISDependency</td><td>Exclude</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISDependency</td><td>ISDependency</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISDisk1File</td><td>Disk</td><td>Y</td><td/><td/><td/><td/><td/><td>-1;0;1</td><td>Used to differentiate between disk1(1), last disk(-1), and other(0).</td></row>
+		<row><td>ISDisk1File</td><td>ISBuildSourcePath</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Full path of file to be copied to Disk1 folder</td></row>
+		<row><td>ISDisk1File</td><td>ISDisk1File</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Primary key for ISDisk1File table</td></row>
+		<row><td>ISDynamicFile</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Foreign key referencing Component that controls the file.</td></row>
+		<row><td>ISDynamicFile</td><td>ExcludeFiles</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Wildcards for excluded files.</td></row>
+		<row><td>ISDynamicFile</td><td>ISAttributes</td><td>Y</td><td/><td/><td/><td/><td/><td>0;1;2;3</td><td>This is used to store Installshield custom properties of a dynamic filet.  Currently the only one is SelfRegister.</td></row>
+		<row><td>ISDynamicFile</td><td>IncludeFiles</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Wildcards for included files.</td></row>
+		<row><td>ISDynamicFile</td><td>IncludeFlags</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>Include flags.</td></row>
+		<row><td>ISDynamicFile</td><td>SourceFolder</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Full path, the category is of Text instead of Path because of potential use of path variables.</td></row>
+		<row><td>ISFeatureDIMReferences</td><td>Feature_</td><td>N</td><td/><td/><td>Feature</td><td>1</td><td>Identifier</td><td/><td>Foreign key into Feature table.</td></row>
+		<row><td>ISFeatureDIMReferences</td><td>ISDIMReference_</td><td>N</td><td/><td/><td>ISDIMReference</td><td>1</td><td>Identifier</td><td/><td>Foreign key into ISDIMReference table.</td></row>
+		<row><td>ISFeatureMergeModuleExcludes</td><td>Feature_</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Foreign key into Feature table.</td></row>
+		<row><td>ISFeatureMergeModuleExcludes</td><td>Language</td><td>N</td><td/><td/><td/><td/><td/><td/><td>Foreign key into ISMergeModule table.</td></row>
+		<row><td>ISFeatureMergeModuleExcludes</td><td>ModuleID</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Foreign key into ISMergeModule table.
+</td></row>
+		<row><td>ISFeatureMergeModules</td><td>Feature_</td><td>N</td><td/><td/><td>Feature</td><td>1</td><td>Identifier</td><td/><td>Foreign key into Feature table.</td></row>
+		<row><td>ISFeatureMergeModules</td><td>ISMergeModule_</td><td>N</td><td/><td/><td>ISMergeModule</td><td>1</td><td>Text</td><td/><td>Foreign key into ISMergeModule table.
+</td></row>
+		<row><td>ISFeatureMergeModules</td><td>Language_</td><td>N</td><td/><td/><td>ISMergeModule</td><td>2</td><td/><td/><td>Foreign key into ISMergeModule table.</td></row>
+		<row><td>ISFileManifests</td><td>File_</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Foreign key into File table.</td></row>
+		<row><td>ISFileManifests</td><td>Manifest_</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Foreign key into File table.</td></row>
+		<row><td>ISIISAppPool</td><td>AppPool</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISIISAppPool</td><td>Attributes</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISIISAppPool</td><td>CPUMon</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISIISAppPool</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Text</td><td/><td>Foreign key into Component table</td></row>
+		<row><td>ISIISAppPool</td><td>IdleTimeout</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISIISAppPool</td><td>MaxProc</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISIISAppPool</td><td>Name</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Localizable Display Name</td></row>
+		<row><td>ISIISAppPool</td><td>QueueLimit</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISIISAppPool</td><td>RecycleMinutes</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISIISAppPool</td><td>RecycleRequests</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISIISAppPool</td><td>RecycleTimes</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISIISAppPool</td><td>User</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISIISAppPool</td><td>UserPassword</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISIISCommon</td><td>AnonyPasswrd</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Password for anonymous access</td></row>
+		<row><td>ISIISCommon</td><td>AnonyUserName</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>User name for anonymous access</td></row>
+		<row><td>ISIISCommon</td><td>AppName</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>AppName of this VRoot</td></row>
+		<row><td>ISIISCommon</td><td>AppPool_</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Localizable Application Pool Name</td></row>
+		<row><td>ISIISCommon</td><td>Attributes</td><td>N</td><td/><td/><td/><td/><td>Number</td><td/><td>Attributes for this IIS node</td></row>
+		<row><td>ISIISCommon</td><td>CustomErrors</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Delimited list of custom errors</td></row>
+		<row><td>ISIISCommon</td><td>DefDoc</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Localizable Defeault Doc</td></row>
+		<row><td>ISIISCommon</td><td>DisplayName</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Localizable Virtual Root Name</td></row>
+		<row><td>ISIISCommon</td><td>ISIISCommon</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Primary key used to identify a particular  record.</td></row>
+		<row><td>ISIISCommon</td><td>ISIISCommon_Parent</td><td>Y</td><td/><td/><td>ISIISCommon</td><td>1</td><td>Identifier</td><td/><td>This record's parent record.</td></row>
+		<row><td>ISIISCommon</td><td>RootDir</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Root directory for this IIS Node</td></row>
+		<row><td>ISIISCommon</td><td>SSLCert</td><td>Y</td><td/><td/><td>Binary</td><td>1</td><td>Identifier</td><td/><td>Foreign key into the binary table.</td></row>
+		<row><td>ISIISCommon</td><td>ScriptTimeout</td><td>Y</td><td/><td/><td/><td/><td>Number</td><td/><td>ASP Script Timeout</td></row>
+		<row><td>ISIISCommon</td><td>SessionTimeout</td><td>Y</td><td/><td/><td/><td/><td>Number</td><td/><td>Session Timeout</td></row>
+		<row><td>ISIISMetaData</td><td>ISIISCommon_</td><td>N</td><td/><td/><td>ISIISCommon</td><td>1</td><td>Identifier</td><td/><td>Foreign key into ISIISCommon table</td></row>
+		<row><td>ISIISMetaData</td><td>MetaDataAttributes</td><td>N</td><td/><td/><td/><td/><td/><td/><td>This is the dwMDAttributes item in the METADATA_RECORD structure</td></row>
+		<row><td>ISIISMetaData</td><td>MetaDataProp</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>This is the dwMDIdentifier item in the METADATA_RECORD structure</td></row>
+		<row><td>ISIISMetaData</td><td>MetaDataType</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>This is the dwMDDataType item in the METADATA_RECORD structure</td></row>
+		<row><td>ISIISMetaData</td><td>MetaDataUserType</td><td>N</td><td/><td/><td/><td/><td/><td/><td>This is the dwMDUserType item in the METADATA_RECORD structure</td></row>
+		<row><td>ISIISMetaData</td><td>MetaDataValue</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>This is the pbMDData  item in the METADATA_RECORD structure</td></row>
+		<row><td>ISIISMetaData</td><td>Order</td><td>N</td><td/><td/><td/><td/><td/><td/><td>Use this column to order the meta data properties.</td></row>
+		<row><td>ISIISWebServiceExtension</td><td>Attributes</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISIISWebServiceExtension</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Text</td><td/><td>Foreign key into Component table</td></row>
+		<row><td>ISIISWebServiceExtension</td><td>Description</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Localizable Description</td></row>
+		<row><td>ISIISWebServiceExtension</td><td>File</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISIISWebServiceExtension</td><td>Group</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISIISWebServiceExtension</td><td>WebServiceExtension</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISInstallScriptAction</td><td>EntryPoint</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>This is a foreign key to the target column in the CustomAction table</td></row>
+		<row><td>ISInstallScriptAction</td><td>Source</td><td>N</td><td/><td/><td/><td/><td>Formatted</td><td/><td>This is column points to the source file for the DLLWrapper Custom Action</td></row>
+		<row><td>ISInstallScriptAction</td><td>Target</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>The function signature</td></row>
+		<row><td>ISInstallScriptAction</td><td>Type</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>Type</td></row>
+		<row><td>ISLanguage</td><td>ISLanguage</td><td>N</td><td/><td/><td/><td/><td>Language</td><td/><td>This is the language ID.</td></row>
+		<row><td>ISLanguage</td><td>Included</td><td>Y</td><td/><td/><td/><td/><td/><td>0;1</td><td>Specify whether this language should be included.</td></row>
+		<row><td>ISLinkerLibrary</td><td>ISLinkerLibrary</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Unique identifier for the link library.
+</td></row>
+		<row><td>ISLinkerLibrary</td><td>Library</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Full path of the object library (.obl file).
+</td></row>
+		<row><td>ISLinkerLibrary</td><td>Order</td><td>N</td><td/><td/><td/><td/><td/><td/><td>Order of the Library</td></row>
+		<row><td>ISLocalControl</td><td>Attributes</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>A 32-bit word that specifies the attribute flags to be applied to this control.</td></row>
+		<row><td>ISLocalControl</td><td>Binary_</td><td>Y</td><td/><td/><td>Binary</td><td>1</td><td>Identifier</td><td/><td>External key to the Binary table.</td></row>
+		<row><td>ISLocalControl</td><td>Control_</td><td>N</td><td/><td/><td>Control</td><td>2</td><td>Identifier</td><td/><td>Name of the control. This name must be unique within a dialog, but can repeat on different dialogs.</td></row>
+		<row><td>ISLocalControl</td><td>Dialog_</td><td>N</td><td/><td/><td>Dialog</td><td>1</td><td>Identifier</td><td/><td>External key to the Dialog table, name of the dialog.</td></row>
+		<row><td>ISLocalControl</td><td>Height</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>Height of the bounding rectangle of the control.</td></row>
+		<row><td>ISLocalControl</td><td>ISBuildSourcePath</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Full path to .rtf file for scrollable text control</td></row>
+		<row><td>ISLocalControl</td><td>ISLanguage_</td><td>N</td><td/><td/><td>ISLanguage</td><td>1</td><td>Language</td><td/><td>This is a foreign key to the ISLanguage table.</td></row>
+		<row><td>ISLocalControl</td><td>Width</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>Width of the bounding rectangle of the control.</td></row>
+		<row><td>ISLocalControl</td><td>X</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>Horizontal coordinate of the upper left corner of the bounding rectangle of the control.</td></row>
+		<row><td>ISLocalControl</td><td>Y</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>Vertical coordinate of the upper left corner of the bounding rectangle of the control.</td></row>
+		<row><td>ISLocalDialog</td><td>Attributes</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>A 32-bit word that specifies the attribute flags to be applied to this dialog.</td></row>
+		<row><td>ISLocalDialog</td><td>Dialog_</td><td>Y</td><td/><td/><td>Dialog</td><td>1</td><td>Identifier</td><td/><td>Name of the dialog.</td></row>
+		<row><td>ISLocalDialog</td><td>Height</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>Height of the bounding rectangle of the dialog.</td></row>
+		<row><td>ISLocalDialog</td><td>ISLanguage_</td><td>Y</td><td/><td/><td>ISLanguage</td><td>1</td><td>Language</td><td/><td>This is a foreign key to the ISLanguage table.</td></row>
+		<row><td>ISLocalDialog</td><td>TextStyle_</td><td>Y</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Foreign Key into TextStyle table, only used in Script Based Projects.</td></row>
+		<row><td>ISLocalDialog</td><td>Width</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>Width of the bounding rectangle of the dialog.</td></row>
+		<row><td>ISLocalRadioButton</td><td>Height</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>The height of the button.</td></row>
+		<row><td>ISLocalRadioButton</td><td>ISLanguage_</td><td>N</td><td/><td/><td>ISLanguage</td><td>1</td><td>Language</td><td/><td>This is a foreign key to the ISLanguage table.</td></row>
+		<row><td>ISLocalRadioButton</td><td>Order</td><td>N</td><td>1</td><td>32767</td><td>RadioButton</td><td>2</td><td/><td/><td>A positive integer used to determine the ordering of the items within one list..The integers do not have to be consecutive.</td></row>
+		<row><td>ISLocalRadioButton</td><td>Property</td><td>N</td><td/><td/><td>RadioButton</td><td>1</td><td>Identifier</td><td/><td>A named property to be tied to this radio button. All the buttons tied to the same property become part of the same group.</td></row>
+		<row><td>ISLocalRadioButton</td><td>Width</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>The width of the button.</td></row>
+		<row><td>ISLocalRadioButton</td><td>X</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>The horizontal coordinate of the upper left corner of the bounding rectangle of the radio button.</td></row>
+		<row><td>ISLocalRadioButton</td><td>Y</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>The vertical coordinate of the upper left corner of the bounding rectangle of the radio button.</td></row>
+		<row><td>ISLogicalDisk</td><td>Cabinet</td><td>Y</td><td/><td/><td/><td/><td>Cabinet</td><td/><td>If some or all of the files stored on the media are compressed in a cabinet, the name of that cabinet.</td></row>
+		<row><td>ISLogicalDisk</td><td>DiskId</td><td>N</td><td>1</td><td>32767</td><td/><td/><td/><td/><td>Primary key, integer to determine sort order for table.</td></row>
+		<row><td>ISLogicalDisk</td><td>DiskPrompt</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Disk name: the visible text actually printed on the disk.  This will be used to prompt the user when this disk needs to be inserted.</td></row>
+		<row><td>ISLogicalDisk</td><td>ISProductConfiguration_</td><td>N</td><td/><td/><td>ISProductConfiguration</td><td>1</td><td>Text</td><td/><td>Foreign key into the ISProductConfiguration table.</td></row>
+		<row><td>ISLogicalDisk</td><td>ISRelease_</td><td>N</td><td/><td/><td>ISRelease</td><td>1</td><td>Text</td><td/><td>Foreign key into the ISRelease table.</td></row>
+		<row><td>ISLogicalDisk</td><td>LastSequence</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>File sequence number for the last file for this media.</td></row>
+		<row><td>ISLogicalDisk</td><td>Source</td><td>Y</td><td/><td/><td/><td/><td>Property</td><td/><td>The property defining the location of the cabinet file.</td></row>
+		<row><td>ISLogicalDisk</td><td>VolumeLabel</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>The label attributed to the volume.</td></row>
+		<row><td>ISLogicalDiskFeatures</td><td>Feature_</td><td>Y</td><td/><td/><td>Feature</td><td>1</td><td>Identifier</td><td/><td>Required foreign key into the Feature Table,</td></row>
+		<row><td>ISLogicalDiskFeatures</td><td>ISAttributes</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>This is used to store Installshield custom properties, like Compressed, etc.</td></row>
+		<row><td>ISLogicalDiskFeatures</td><td>ISLogicalDisk_</td><td>N</td><td>1</td><td>32767</td><td>ISLogicalDisk</td><td>1</td><td>Identifier</td><td/><td>Foreign key into the ISLogicalDisk table.</td></row>
+		<row><td>ISLogicalDiskFeatures</td><td>ISProductConfiguration_</td><td>N</td><td/><td/><td>ISProductConfiguration</td><td>1</td><td>Text</td><td/><td>Foreign key into the ISProductConfiguration table.</td></row>
+		<row><td>ISLogicalDiskFeatures</td><td>ISRelease_</td><td>N</td><td/><td/><td>ISRelease</td><td>1</td><td>Text</td><td/><td>Foreign key into the ISRelease table.</td></row>
+		<row><td>ISLogicalDiskFeatures</td><td>Sequence</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>File sequence number for the file for this media.</td></row>
+		<row><td>ISMergeModule</td><td>Destination</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Destination.
+</td></row>
+		<row><td>ISMergeModule</td><td>ISAttributes</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>This is used to store Installshield custom properties of a merge module.</td></row>
+		<row><td>ISMergeModule</td><td>ISMergeModule</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>The GUID identifying the merge module.
+</td></row>
+		<row><td>ISMergeModule</td><td>Language</td><td>N</td><td/><td/><td/><td/><td/><td/><td>Default decimal language of module.</td></row>
+		<row><td>ISMergeModule</td><td>Name</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Name of the merge module.
+</td></row>
+		<row><td>ISMergeModuleCfgValues</td><td>Attributes</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>Attributes (from configurable merge module)</td></row>
+		<row><td>ISMergeModuleCfgValues</td><td>ContextData</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>ContextData  (from configurable merge module)</td></row>
+		<row><td>ISMergeModuleCfgValues</td><td>DefaultValue</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>DefaultValue  (from configurable merge module)</td></row>
+		<row><td>ISMergeModuleCfgValues</td><td>Description</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Description (from configurable merge module)</td></row>
+		<row><td>ISMergeModuleCfgValues</td><td>DisplayName</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>DisplayName (from configurable merge module)</td></row>
+		<row><td>ISMergeModuleCfgValues</td><td>Format</td><td>N</td><td/><td/><td/><td/><td/><td/><td>Format (from configurable merge module)</td></row>
+		<row><td>ISMergeModuleCfgValues</td><td>HelpKeyword</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>HelpKeyword (from configurable merge module)</td></row>
+		<row><td>ISMergeModuleCfgValues</td><td>HelpLocation</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>HelpLocation (from configurable merge module)</td></row>
+		<row><td>ISMergeModuleCfgValues</td><td>ISMergeModule_</td><td>N</td><td/><td/><td>ISMergeModule</td><td>1</td><td>Text</td><td/><td>The module signature, a foreign key into the ISMergeModule table</td></row>
+		<row><td>ISMergeModuleCfgValues</td><td>Language_</td><td>N</td><td/><td/><td>ISMergeModule</td><td>2</td><td/><td/><td>Default decimal language of module.</td></row>
+		<row><td>ISMergeModuleCfgValues</td><td>ModuleConfiguration_</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Identifier, foreign key into ModuleConfiguration table (ModuleConfiguration.Name)</td></row>
+		<row><td>ISMergeModuleCfgValues</td><td>Type</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Type (from configurable merge module)</td></row>
+		<row><td>ISMergeModuleCfgValues</td><td>Value</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Value for this item.</td></row>
+		<row><td>ISObject</td><td>Language</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td/></row>
+		<row><td>ISObject</td><td>ObjectName</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td/></row>
+		<row><td>ISObjectProperty</td><td>IncludeInBuild</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>Boolean, 0 for false non 0 for true</td></row>
+		<row><td>ISObjectProperty</td><td>ObjectName</td><td>Y</td><td/><td/><td>ISObject</td><td>1</td><td>Text</td><td/><td/></row>
+		<row><td>ISObjectProperty</td><td>Property</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td/></row>
+		<row><td>ISObjectProperty</td><td>Value</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td/></row>
+		<row><td>ISPalmApp</td><td>Component</td><td>N</td><td/><td/><td>Component</td><td>1</td><td/><td/><td/></row>
+		<row><td>ISPalmApp</td><td>PalmApp</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISPalmAppFile</td><td>Destination</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISPalmAppFile</td><td>FileKey</td><td>N</td><td/><td/><td>File</td><td>1</td><td/><td/><td/></row>
+		<row><td>ISPalmAppFile</td><td>PalmApp</td><td>N</td><td/><td/><td>ISPalmApp</td><td>1</td><td/><td/><td/></row>
+		<row><td>ISPatchConfigImage</td><td>PatchConfiguration_</td><td>Y</td><td/><td/><td>ISPatchConfiguration</td><td>1</td><td>Text</td><td/><td>Foreign key to the ISPatchConfigurationTable</td></row>
+		<row><td>ISPatchConfigImage</td><td>UpgradedImage_</td><td>N</td><td/><td/><td>ISUpgradedImage</td><td>1</td><td>Text</td><td/><td>Foreign key to the ISUpgradedImageTable</td></row>
+		<row><td>ISPatchConfiguration</td><td>Attributes</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>PatchConfiguration attributes</td></row>
+		<row><td>ISPatchConfiguration</td><td>CanPCDiffer</td><td>N</td><td/><td/><td/><td/><td/><td/><td>This is determine whether Product Codes may differ</td></row>
+		<row><td>ISPatchConfiguration</td><td>CanPVDiffer</td><td>N</td><td/><td/><td/><td/><td/><td/><td>This is determine whether the Major Product Version may differ</td></row>
+		<row><td>ISPatchConfiguration</td><td>EnablePatchCache</td><td>N</td><td/><td/><td/><td/><td/><td/><td>This is determine whether to Enable Patch cacheing</td></row>
+		<row><td>ISPatchConfiguration</td><td>Flags</td><td>N</td><td/><td/><td/><td/><td/><td/><td>Patching API Flags</td></row>
+		<row><td>ISPatchConfiguration</td><td>IncludeWholeFiles</td><td>N</td><td/><td/><td/><td/><td/><td/><td>This is determine whether to build a binary level patch</td></row>
+		<row><td>ISPatchConfiguration</td><td>LeaveDecompressed</td><td>N</td><td/><td/><td/><td/><td/><td/><td>This is determine whether to leave intermediate files devcompressed when finished</td></row>
+		<row><td>ISPatchConfiguration</td><td>MinMsiVersion</td><td>N</td><td/><td/><td/><td/><td/><td/><td>Minimum Required MSI Version</td></row>
+		<row><td>ISPatchConfiguration</td><td>Name</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Name of the Patch Configuration</td></row>
+		<row><td>ISPatchConfiguration</td><td>OptimizeForSize</td><td>N</td><td/><td/><td/><td/><td/><td/><td>This is determine whether to Optimize for large files</td></row>
+		<row><td>ISPatchConfiguration</td><td>OutputPath</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Build Location</td></row>
+		<row><td>ISPatchConfiguration</td><td>PatchCacheDir</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Directory to recieve the Patch Cache information</td></row>
+		<row><td>ISPatchConfiguration</td><td>PatchGuid</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Unique Patch Identifier</td></row>
+		<row><td>ISPatchConfiguration</td><td>PatchGuidsToReplace</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>List Of Patch Guids to unregister</td></row>
+		<row><td>ISPatchConfiguration</td><td>TargetProductCodes</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>List Of target Product Codes</td></row>
+		<row><td>ISPatchConfigurationProperty</td><td>ISPatchConfiguration_</td><td>Y</td><td/><td/><td>ISPatchConfiguration</td><td>1</td><td>Text</td><td/><td>Name of the Patch Configuration</td></row>
+		<row><td>ISPatchConfigurationProperty</td><td>Property</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Name of the Patch Configuration Property value</td></row>
+		<row><td>ISPatchConfigurationProperty</td><td>Value</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Value of the Patch Configuration Property</td></row>
+		<row><td>ISPatchExternalFile</td><td>FileKey</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Filekey</td></row>
+		<row><td>ISPatchExternalFile</td><td>FilePath</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Filepath</td></row>
+		<row><td>ISPatchExternalFile</td><td>ISUpgradedImage_</td><td>N</td><td/><td/><td>ISUpgradedImage</td><td>1</td><td>Text</td><td/><td>Foreign key to the isupgraded image table</td></row>
+		<row><td>ISPatchExternalFile</td><td>Name</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Uniqu name to identify this record.</td></row>
+		<row><td>ISPatchWholeFile</td><td>Component</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Component containing file key</td></row>
+		<row><td>ISPatchWholeFile</td><td>FileKey</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Key of file to be included as whole</td></row>
+		<row><td>ISPatchWholeFile</td><td>UpgradedImage</td><td>N</td><td/><td/><td>ISUpgradedImage</td><td>1</td><td>Text</td><td/><td>Foreign key to ISUpgradedImage Table</td></row>
+		<row><td>ISPathVariable</td><td>ISPathVariable</td><td>N</td><td/><td/><td/><td/><td/><td/><td>The name of the path variable.</td></row>
+		<row><td>ISPathVariable</td><td>TestValue</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>The test value of the path variable.</td></row>
+		<row><td>ISPathVariable</td><td>Type</td><td>N</td><td/><td/><td/><td/><td/><td>1;2;4;8</td><td>The type of the path variable.</td></row>
+		<row><td>ISPathVariable</td><td>Value</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>The value of the path variable.</td></row>
+		<row><td>ISProductConfiguration</td><td>GeneratePackageCode</td><td>Y</td><td/><td/><td/><td/><td>Number</td><td>0;1</td><td>Indicates whether or not to generate a package code.
+</td></row>
+		<row><td>ISProductConfiguration</td><td>ISProductConfiguration</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>The name of the product configuration.
+</td></row>
+		<row><td>ISProductConfiguration</td><td>ProductConfigurationFlags</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Product configuration (release) flags.
+</td></row>
+		<row><td>ISProductConfigurationInstance</td><td>ISProductConfiguration_</td><td>N</td><td/><td/><td>ISProductConfiguration</td><td>1</td><td>Text</td><td/><td>Foreign key into the ISProductConfiguration table.
+</td></row>
+		<row><td>ISProductConfigurationInstance</td><td>InstanceId</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>Identifies the instance number of this instance. This value is stored in the Property InstanceId.</td></row>
+		<row><td>ISProductConfigurationInstance</td><td>Property</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Product Congiuration property name</td></row>
+		<row><td>ISProductConfigurationInstance</td><td>Value</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>String value for property.</td></row>
+		<row><td>ISProductConfigurationProperty</td><td>ISProductConfiguration_</td><td>N</td><td/><td/><td>ISProductConfiguration</td><td>1</td><td>Text</td><td/><td>Foreign key into the ISProductConfiguration table.
+</td></row>
+		<row><td>ISProductConfigurationProperty</td><td>Property</td><td>N</td><td/><td/><td>Property</td><td>1</td><td>Text</td><td/><td>Product Congiuration property name</td></row>
+		<row><td>ISProductConfigurationProperty</td><td>Value</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>String value for property. Never null or empty.
+
+
+
+</td></row>
+		<row><td>ISRelease</td><td>Attributes</td><td>N</td><td/><td/><td/><td/><td/><td/><td>Bitfield holding boolean values for various release attributes.</td></row>
+		<row><td>ISRelease</td><td>BuildLocation</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Build location.</td></row>
+		<row><td>ISRelease</td><td>CDBrowser</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Demoshield browser location.</td></row>
+		<row><td>ISRelease</td><td>DefaultLanguage</td><td>N</td><td/><td/><td/><td/><td>Language</td><td/><td>Default language for setup.</td></row>
+		<row><td>ISRelease</td><td>DigitalPVK</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Digital signing private key (.pvk) file.</td></row>
+		<row><td>ISRelease</td><td>DigitalSPC</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Digital signing Software Publisher Certificate (.spc) file.</td></row>
+		<row><td>ISRelease</td><td>DigitalURL</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Digital signing URL.</td></row>
+		<row><td>ISRelease</td><td>DiskClusterSize</td><td>N</td><td/><td/><td/><td/><td/><td/><td>Disk cluster size.</td></row>
+		<row><td>ISRelease</td><td>DiskSize</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Disk size.</td></row>
+		<row><td>ISRelease</td><td>DiskSizeUnit</td><td>N</td><td/><td/><td/><td/><td/><td>0;1;2</td><td>Disk size units (KB or MB).</td></row>
+		<row><td>ISRelease</td><td>DiskSpanning</td><td>N</td><td/><td/><td/><td/><td/><td>0;1;2</td><td>Disk spanning (automatic, enforce size, etc.).</td></row>
+		<row><td>ISRelease</td><td>DotNetBuildConfiguration</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Build Configuration for .NET solutions.</td></row>
+		<row><td>ISRelease</td><td>ISProductConfiguration_</td><td>N</td><td/><td/><td>ISProductConfiguration</td><td>1</td><td>Text</td><td/><td>Foreign key into the ISProductConfiguration table.</td></row>
+		<row><td>ISRelease</td><td>ISRelease</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>The name of the release.
+</td></row>
+		<row><td>ISRelease</td><td>ISSetupPrerequisiteLocation</td><td>Y</td><td/><td/><td/><td/><td/><td>0;1;2</td><td>Location the Setup Prerequisites will be placed in</td></row>
+		<row><td>ISRelease</td><td>MediaLocation</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Media location on disk.</td></row>
+		<row><td>ISRelease</td><td>MsiCommandLine</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Command line passed to the msi package from setup.exe</td></row>
+		<row><td>ISRelease</td><td>MsiSourceType</td><td>N</td><td>-1</td><td>4</td><td/><td/><td/><td/><td>MSI media source type.</td></row>
+		<row><td>ISRelease</td><td>PackageName</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Package name.</td></row>
+		<row><td>ISRelease</td><td>Password</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Password.</td></row>
+		<row><td>ISRelease</td><td>Platforms</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Platforms supported (Intel, Alpha, etc.).</td></row>
+		<row><td>ISRelease</td><td>ReleaseFlags</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Release flags.</td></row>
+		<row><td>ISRelease</td><td>ReleaseType</td><td>N</td><td/><td/><td/><td/><td/><td>1;2;4</td><td>Release type (single, uncompressed, etc.).</td></row>
+		<row><td>ISRelease</td><td>SupportedLanguagesData</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Languages supported (for component filtering).</td></row>
+		<row><td>ISRelease</td><td>SupportedLanguagesUI</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>UI languages supported.</td></row>
+		<row><td>ISRelease</td><td>SupportedOSs</td><td>N</td><td/><td/><td/><td/><td/><td/><td>Indicate which operating systmes are supported.</td></row>
+		<row><td>ISRelease</td><td>SynchMsi</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>MSI file to synchronize file keys and other data with (patch-like functionality).</td></row>
+		<row><td>ISRelease</td><td>Type</td><td>N</td><td>0</td><td>6</td><td/><td/><td/><td/><td>Release type (CDROM, Network, etc.).</td></row>
+		<row><td>ISRelease</td><td>URLLocation</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Media location via URL.</td></row>
+		<row><td>ISRelease</td><td>VersionCopyright</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Version stamp information.</td></row>
+		<row><td>ISReleaseASPublishInfo</td><td>ISProductConfiguration_</td><td>N</td><td/><td/><td>ISProductConfiguration</td><td>1</td><td>Text</td><td/><td>Foreign key into the ISProductConfiguration table.</td></row>
+		<row><td>ISReleaseASPublishInfo</td><td>ISRelease_</td><td>N</td><td/><td/><td>ISRelease</td><td>1</td><td>Text</td><td/><td>Foreign key into the ISRelease table.</td></row>
+		<row><td>ISReleaseASPublishInfo</td><td>Property</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>AS Repository property name</td></row>
+		<row><td>ISReleaseASPublishInfo</td><td>Value</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>AS Repository property value</td></row>
+		<row><td>ISReleaseExtended</td><td>Attributes</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>Bitfield holding boolean values for various release attributes.</td></row>
+		<row><td>ISReleaseExtended</td><td>CertPassword</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Digital certificate password</td></row>
+		<row><td>ISReleaseExtended</td><td>DigitalCertificateDBaseNS</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Path to cerificate database for Netscape digital  signature</td></row>
+		<row><td>ISReleaseExtended</td><td>DigitalCertificateIdNS</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Path to cerificate ID for Netscape digital  signature</td></row>
+		<row><td>ISReleaseExtended</td><td>DigitalCertificatePasswordNS</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Password for Netscape digital  signature</td></row>
+		<row><td>ISReleaseExtended</td><td>DotNetBaseLanguage</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Base Languge of .NET Redist</td></row>
+		<row><td>ISReleaseExtended</td><td>DotNetFxCmdLine</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Command Line to pass to DotNetFx.exe</td></row>
+		<row><td>ISReleaseExtended</td><td>DotNetLangPackCmdLine</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Command Line to pass to LangPack.exe</td></row>
+		<row><td>ISReleaseExtended</td><td>DotNetLangaugePacks</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>.NET Redist language packs to include</td></row>
+		<row><td>ISReleaseExtended</td><td>DotNetRedistLocation</td><td>Y</td><td>0</td><td>3</td><td/><td/><td/><td/><td>Location of .NET framework Redist (Web, SetupExe, Source, None)</td></row>
+		<row><td>ISReleaseExtended</td><td>DotNetRedistURL</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>URL to .NET framework Redist</td></row>
+		<row><td>ISReleaseExtended</td><td>DotNetVersion</td><td>Y</td><td>0</td><td>2</td><td/><td/><td/><td/><td>Version of .NET framework Redist (1.0, 1.1)</td></row>
+		<row><td>ISReleaseExtended</td><td>EngineLocation</td><td>Y</td><td>0</td><td>2</td><td/><td/><td/><td/><td>Location of msi engine (Web, SetupExe...)</td></row>
+		<row><td>ISReleaseExtended</td><td>ISEngineLocation</td><td>Y</td><td>0</td><td>2</td><td/><td/><td/><td/><td>Location of ISScript  engine (Web, SetupExe...)</td></row>
+		<row><td>ISReleaseExtended</td><td>ISEngineURL</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>URL to InstallShield scripting engine</td></row>
+		<row><td>ISReleaseExtended</td><td>ISProductConfiguration_</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Foreign key into the ISProductConfiguration table.</td></row>
+		<row><td>ISReleaseExtended</td><td>ISRelease_</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>The name of the release.</td></row>
+		<row><td>ISReleaseExtended</td><td>JSharpCmdLine</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Command Line to pass to vjredist.exe</td></row>
+		<row><td>ISReleaseExtended</td><td>JSharpRedistLocation</td><td>Y</td><td>0</td><td>3</td><td/><td/><td/><td/><td>Location of J# framework Redist (Web, SetupExe, Source, None)</td></row>
+		<row><td>ISReleaseExtended</td><td>MsiEngineVersion</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>Bitfield holding selected MSI engine versions included in this release</td></row>
+		<row><td>ISReleaseExtended</td><td>OneClickCabName</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>File name of generated cabfile</td></row>
+		<row><td>ISReleaseExtended</td><td>OneClickHtmlName</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>File name of generated html page</td></row>
+		<row><td>ISReleaseExtended</td><td>OneClickTargetBrowser</td><td>Y</td><td>0</td><td>2</td><td/><td/><td/><td/><td>Target browser (IE, Netscape, both...)</td></row>
+		<row><td>ISReleaseExtended</td><td>WebCabSize</td><td>Y</td><td>0</td><td>2147483647</td><td/><td/><td/><td/><td>Size of the cabfile</td></row>
+		<row><td>ISReleaseExtended</td><td>WebLocalCachePath</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Directory to cache downloaded package</td></row>
+		<row><td>ISReleaseExtended</td><td>WebType</td><td>Y</td><td>0</td><td>2</td><td/><td/><td/><td/><td>Type of web install (One Executable, Downloader...)</td></row>
+		<row><td>ISReleaseExtended</td><td>WebURL</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>URL to .msi package</td></row>
+		<row><td>ISReleaseExtended</td><td>Win9xMsiUrl</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>URL to Ansi MSI engine</td></row>
+		<row><td>ISReleaseExtended</td><td>WinMsi30Url</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>URL to MSI 3.0 engine</td></row>
+		<row><td>ISReleaseExtended</td><td>WinNTMsiUrl</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>URL to Unicode MSI engine</td></row>
+		<row><td>ISReleasePublishInfo</td><td>Description</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Repository item description</td></row>
+		<row><td>ISReleasePublishInfo</td><td>DisplayName</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Repository item display name</td></row>
+		<row><td>ISReleasePublishInfo</td><td>ISAttributes</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>Bitfield holding various attributes</td></row>
+		<row><td>ISReleasePublishInfo</td><td>ISProductConfiguration_</td><td>N</td><td/><td/><td>ISProductConfiguration</td><td>1</td><td>Text</td><td/><td>Foreign key into the ISProductConfiguration table.</td></row>
+		<row><td>ISReleasePublishInfo</td><td>ISRelease_</td><td>N</td><td/><td/><td>ISRelease</td><td>1</td><td>Text</td><td/><td>The name of the release.</td></row>
+		<row><td>ISReleasePublishInfo</td><td>Publisher</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Repository item publisher</td></row>
+		<row><td>ISReleasePublishInfo</td><td>Repository</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Repository which to  publish the built merge module</td></row>
+		<row><td>ISSQLConnection</td><td>Attributes</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLConnection</td><td>Authentication</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLConnection</td><td>BatchSeparator</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLConnection</td><td>CmdTimeout</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLConnection</td><td>Comments</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLConnection</td><td>Database</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLConnection</td><td>ISSQLConnection</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLConnection</td><td>Order</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLConnection</td><td>Password</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLConnection</td><td>Server</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLConnection</td><td>UserName</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLConnectionDBServer</td><td>ISSQLConnectionDBServer</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLConnectionDBServer</td><td>ISSQLConnection_</td><td>N</td><td/><td/><td>ISSQLConnection</td><td>1</td><td/><td/><td/></row>
+		<row><td>ISSQLConnectionDBServer</td><td>ISSQLDBMetaData_</td><td>N</td><td/><td/><td>ISSQLDBMetaData</td><td>1</td><td/><td/><td/></row>
+		<row><td>ISSQLConnectionDBServer</td><td>Order</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLConnectionScript</td><td>ISSQLConnection_</td><td>N</td><td/><td/><td>ISSQLConnection</td><td>1</td><td/><td/><td/></row>
+		<row><td>ISSQLConnectionScript</td><td>ISSQLScriptFile_</td><td>N</td><td/><td/><td>ISSQLScriptFile</td><td>1</td><td/><td/><td/></row>
+		<row><td>ISSQLConnectionScript</td><td>Order</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLDBMetaData</td><td>AdoCxnDatabase</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLDBMetaData</td><td>AdoCxnDriver</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLDBMetaData</td><td>AdoCxnNetLibrary</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLDBMetaData</td><td>AdoCxnPassword</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLDBMetaData</td><td>AdoCxnServer</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLDBMetaData</td><td>AdoCxnUserID</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLDBMetaData</td><td>AdoCxnWindowsSecurity</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLDBMetaData</td><td>AdoDriverName</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLDBMetaData</td><td>CreateDbCmd</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLDBMetaData</td><td>DisplayName</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLDBMetaData</td><td>DsnODBCName</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLDBMetaData</td><td>ISAttributes</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLDBMetaData</td><td>ISSQLDBMetaData</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLDBMetaData</td><td>LocalInstanceNames</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLDBMetaData</td><td>SwitchDbCmd</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLDBMetaData</td><td>TestDatabaseCmd</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLDBMetaData</td><td>TestTableCmd</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLDBMetaData</td><td>TestTableCmd2</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLDBMetaData</td><td>VersionBeginToken</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLDBMetaData</td><td>VersionEndToken</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLDBMetaData</td><td>VersionInfoCmd</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLDBMetaData</td><td>WinAuthentUserId</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLRequirement</td><td>Attributes</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLRequirement</td><td>ISSQLConnectionDBServer_</td><td>Y</td><td/><td/><td>ISSQLConnectionDBServer</td><td>1</td><td/><td/><td/></row>
+		<row><td>ISSQLRequirement</td><td>ISSQLConnection_</td><td>N</td><td/><td/><td>ISSQLConnection</td><td>1</td><td/><td/><td/></row>
+		<row><td>ISSQLRequirement</td><td>ISSQLRequirement</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLRequirement</td><td>MajorVersion</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLRequirement</td><td>ServicePackLevel</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLScriptError</td><td>Attributes</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLScriptError</td><td>ErrHandling</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLScriptError</td><td>ErrNumber</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLScriptError</td><td>ISSQLScriptFile_</td><td>Y</td><td/><td/><td>ISSQLScriptFile</td><td>1</td><td>Identifier</td><td/><td>Foreign key referencing ISSQLScriptFile</td></row>
+		<row><td>ISSQLScriptError</td><td>Message</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Custom end-user message. Reserved for future use.</td></row>
+		<row><td>ISSQLScriptFile</td><td>Attributes</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLScriptFile</td><td>Comments</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Comments</td></row>
+		<row><td>ISSQLScriptFile</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Foreign key referencing Component that controls the SQL script.</td></row>
+		<row><td>ISSQLScriptFile</td><td>ErrorHandling</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLScriptFile</td><td>ISBuildSourcePath</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Full path, the category is of Text instead of Path because of potential use of path variables.</td></row>
+		<row><td>ISSQLScriptFile</td><td>ISSQLScriptFile</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>This is the primary key to the ISSQLScriptFile table</td></row>
+		<row><td>ISSQLScriptFile</td><td>InstallText</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Feedback end-user text at install</td></row>
+		<row><td>ISSQLScriptFile</td><td>Scheduling</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLScriptFile</td><td>UninstallText</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Feedback end-user text at Uninstall</td></row>
+		<row><td>ISSQLScriptFile</td><td>Version</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Schema Version (####.#####.####)</td></row>
+		<row><td>ISSQLScriptImport</td><td>Attributes</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLScriptImport</td><td>Authentication</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLScriptImport</td><td>Database</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLScriptImport</td><td>ExcludeTables</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLScriptImport</td><td>ISSQLScriptFile_</td><td>N</td><td/><td/><td>ISSQLScriptFile</td><td>1</td><td/><td/><td/></row>
+		<row><td>ISSQLScriptImport</td><td>IncludeTables</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLScriptImport</td><td>Password</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLScriptImport</td><td>Server</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLScriptImport</td><td>UserName</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLScriptReplace</td><td>Attributes</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLScriptReplace</td><td>ISSQLScriptFile_</td><td>N</td><td/><td/><td>ISSQLScriptFile</td><td>1</td><td/><td/><td/></row>
+		<row><td>ISSQLScriptReplace</td><td>ISSQLScriptReplace</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLScriptReplace</td><td>Replace</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSQLScriptReplace</td><td>Search</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISScriptFile</td><td>ISScriptFile</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>This is the full path of the script file. The path portion may be expressed in path variable form.
+</td></row>
+		<row><td>ISSelfReg</td><td>CmdLine</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSelfReg</td><td>Cost</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSelfReg</td><td>FileKey</td><td>N</td><td/><td/><td>File</td><td>1</td><td>Identifier</td><td/><td>Foreign key to the file table</td></row>
+		<row><td>ISSelfReg</td><td>Order</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSetupFile</td><td>FileName</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>This is the file name to use when streaming the file to the support files location</td></row>
+		<row><td>ISSetupFile</td><td>ISSetupFile</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>This is the primary key to the ISSetupFile table</td></row>
+		<row><td>ISSetupFile</td><td>Language</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Four digit language identifier.  0 for Language Neutral</td></row>
+		<row><td>ISSetupFile</td><td>Path</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Link to the source file on the build machine</td></row>
+		<row><td>ISSetupFile</td><td>Splash</td><td>Y</td><td/><td/><td/><td/><td>Short</td><td/><td>Boolean value indication whether his setup file entry belongs in the Splasc Screen section</td></row>
+		<row><td>ISSetupFile</td><td>Stream</td><td>Y</td><td/><td/><td/><td/><td>Binary</td><td/><td>Binary stream. The bits to stream to the support location</td></row>
+		<row><td>ISSetupPrerequisites</td><td>ISBuildSourcePath</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSetupPrerequisites</td><td>ISSetupPrerequisites</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSetupPrerequisites</td><td>Order</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISSetupType</td><td>Comments</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>User Comments.</td></row>
+		<row><td>ISSetupType</td><td>Description</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Longer descriptive text describing a visible feature item.</td></row>
+		<row><td>ISSetupType</td><td>Display</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>Numeric sort order, used to force a specific display ordering.</td></row>
+		<row><td>ISSetupType</td><td>Display_Name</td><td>Y</td><td/><td/><td/><td/><td>Formatted</td><td/><td>A string used to set the initial text contained within a control (if appropriate).</td></row>
+		<row><td>ISSetupType</td><td>ISSetupType</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Primary key used to identify a particular feature record.</td></row>
+		<row><td>ISSetupTypeFeatures</td><td>Feature_</td><td>N</td><td/><td/><td>Feature</td><td>1</td><td>Identifier</td><td/><td>Foreign key into Feature table.</td></row>
+		<row><td>ISSetupTypeFeatures</td><td>ISSetupType_</td><td>N</td><td/><td/><td>ISSetupType</td><td>1</td><td>Identifier</td><td/><td>Foreign key into ISSetupType table.</td></row>
+		<row><td>ISStorages</td><td>ISBuildSourcePath</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>Path to the file to stream into sub-storage</td></row>
+		<row><td>ISStorages</td><td>Name</td><td>N</td><td/><td/><td/><td/><td/><td/><td>Name of the sub-storage key</td></row>
+		<row><td>ISString</td><td>Comment</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Comment</td></row>
+		<row><td>ISString</td><td>Encoded</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>Encoding for multi-byte strings.</td></row>
+		<row><td>ISString</td><td>ISLanguage_</td><td>N</td><td/><td/><td/><td/><td>Language</td><td/><td>This is a foreign key to the ISLanguage table.</td></row>
+		<row><td>ISString</td><td>ISString</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>String id.</td></row>
+		<row><td>ISString</td><td>TimeStamp</td><td>Y</td><td/><td/><td/><td/><td>Time/Date</td><td/><td>Time Stamp. MSI's Time/Date column type is just an int, with bits packed in a certain order.</td></row>
+		<row><td>ISString</td><td>Value</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>real string value.</td></row>
+		<row><td>ISTargetImage</td><td>Flags</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>relative order of the target image</td></row>
+		<row><td>ISTargetImage</td><td>IgnoreMissingFiles</td><td>N</td><td/><td/><td/><td/><td/><td/><td>If true, ignore missing source files when creating patch</td></row>
+		<row><td>ISTargetImage</td><td>MsiPath</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Path to the target image</td></row>
+		<row><td>ISTargetImage</td><td>Name</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Name of the TargetImage</td></row>
+		<row><td>ISTargetImage</td><td>Order</td><td>N</td><td/><td/><td/><td/><td/><td/><td>relative order of the target image</td></row>
+		<row><td>ISTargetImage</td><td>UpgradedImage_</td><td>N</td><td/><td/><td>ISUpgradedImage</td><td>1</td><td>Text</td><td/><td>foreign key to the upgraded Image table</td></row>
+		<row><td>ISUpgradeMsiItem</td><td>ISAttributes</td><td>N</td><td/><td/><td/><td/><td/><td>0;1</td><td/></row>
+		<row><td>ISUpgradeMsiItem</td><td>ISReleaseFlags</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISUpgradeMsiItem</td><td>ObjectSetupPath</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>The path to the setup you want to upgrade.</td></row>
+		<row><td>ISUpgradeMsiItem</td><td>UpgradeItem</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>The name of the Upgrade Item.</td></row>
+		<row><td>ISUpgradedImage</td><td>Family</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Name of the image family</td></row>
+		<row><td>ISUpgradedImage</td><td>MsiPath</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Path to the upgraded image</td></row>
+		<row><td>ISUpgradedImage</td><td>Name</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Name of the UpgradedImage</td></row>
+		<row><td>ISVRoot</td><td>AnonyPasswrd</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Obsolete column.  Moved to IISCommon</td></row>
+		<row><td>ISVRoot</td><td>AnonyUserName</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Obsolete column.  Moved to IISCommon</td></row>
+		<row><td>ISVRoot</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Text</td><td/><td>Foreign key into Component table</td></row>
+		<row><td>ISVRoot</td><td>Condition</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Obsolete column.  Moved to Component</td></row>
+		<row><td>ISVRoot</td><td>ScriptTimeout</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>Obsolete column.  Moved to IISCommon</td></row>
+		<row><td>ISVRoot</td><td>SessionTimeout</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>Obsolete column.  Moved to IISCommon</td></row>
+		<row><td>ISVRoot</td><td>VRootAppName</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>AppName of this VRoot</td></row>
+		<row><td>ISVRoot</td><td>VRootDefDoc</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Obsolete column.  Moved to IISCommon</td></row>
+		<row><td>ISVRoot</td><td>VRootDir</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Obsolete column.  Moved to IISCommon</td></row>
+		<row><td>ISVRoot</td><td>VRootKey</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Foreign key into ISIISCommon table</td></row>
+		<row><td>ISVRoot</td><td>VRootName</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Obsolete column.  Moved to IISCommon</td></row>
+		<row><td>ISVRoot</td><td>VRootProps</td><td>N</td><td/><td/><td/><td/><td/><td/><td>Properties of this VRoot</td></row>
+		<row><td>ISVRootAppMaps</td><td>AppMapProps</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISVRootAppMaps</td><td>ExecPath</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Localizable Exec Path</td></row>
+		<row><td>ISVRootAppMaps</td><td>Extension</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Localizable Extension</td></row>
+		<row><td>ISVRootAppMaps</td><td>VRootAppMapKey</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISVRootAppMaps</td><td>VRootKey</td><td>N</td><td/><td/><td>ISIISCommon</td><td>1</td><td/><td/><td/></row>
+		<row><td>ISVRootAppMaps</td><td>Verb</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Localizable Verb</td></row>
+		<row><td>ISWebSite</td><td>IP</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISWebSite</td><td>ISIISCommon_</td><td>N</td><td/><td/><td>ISIISCommon</td><td>1</td><td>Text</td><td/><td>Foreign key into ISIISCommon table</td></row>
+		<row><td>ISWebSite</td><td>Port</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISWebSite</td><td>SiteNumber</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISWebSite</td><td>WebSiteProps</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>ISXmlElement</td><td>Content</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Element contents</td></row>
+		<row><td>ISXmlElement</td><td>ISAttributes</td><td>Y</td><td/><td/><td/><td/><td>Number</td><td/><td>Internal XML element attributes</td></row>
+		<row><td>ISXmlElement</td><td>ISXmlElement</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Primary key, non-localized, internal token for Xml element</td></row>
+		<row><td>ISXmlElement</td><td>ISXmlElement_Parent</td><td>Y</td><td/><td/><td>ISXmlElement</td><td>1</td><td>Identifier</td><td/><td>Foreign key into ISXMLElement table.</td></row>
+		<row><td>ISXmlElement</td><td>ISXmlFile_</td><td>N</td><td/><td/><td>ISXmlFile</td><td>1</td><td>Identifier</td><td/><td>Foreign key into XmlFile table.</td></row>
+		<row><td>ISXmlElement</td><td>XPath</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>XPath fragment including any operators</td></row>
+		<row><td>ISXmlElementAttrib</td><td>ISAttributes</td><td>Y</td><td/><td/><td/><td/><td>Number</td><td/><td>Internal XML elementattib attributes</td></row>
+		<row><td>ISXmlElementAttrib</td><td>ISXmlElementAttrib</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Primary key, non-localized, internal token for Xml element attribute</td></row>
+		<row><td>ISXmlElementAttrib</td><td>ISXmlElement_</td><td>N</td><td/><td/><td>ISXmlElement</td><td>1</td><td>Identifier</td><td/><td>Foreign key into ISXMLElement table.</td></row>
+		<row><td>ISXmlElementAttrib</td><td>Name</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Localized attribute name</td></row>
+		<row><td>ISXmlElementAttrib</td><td>Value</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Localized attribute value</td></row>
+		<row><td>ISXmlFile</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Foreign key into Component table.</td></row>
+		<row><td>ISXmlFile</td><td>Directory</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Foreign key into Directory table.</td></row>
+		<row><td>ISXmlFile</td><td>FileName</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Localized XML file name</td></row>
+		<row><td>ISXmlFile</td><td>ISAttributes</td><td>Y</td><td/><td/><td/><td/><td>Number</td><td/><td>Internal XML file attributes</td></row>
+		<row><td>ISXmlFile</td><td>ISXmlFile</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Primary key, non-localized,internal token for Xml file</td></row>
+		<row><td>ISXmlFile</td><td>SelectionNamespaces</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Selection namespaces</td></row>
+		<row><td>ISXmlLocator</td><td>Attribute</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>The name of an attribute within the XML element.</td></row>
+		<row><td>ISXmlLocator</td><td>Element</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>XPath query that will locate an element in an XML file.</td></row>
+		<row><td>ISXmlLocator</td><td>ISAttributes</td><td>Y</td><td/><td/><td/><td/><td/><td>0;1;2</td><td/></row>
+		<row><td>ISXmlLocator</td><td>Parent</td><td>Y</td><td/><td/><td/><td/><td>Identifier</td><td/><td>The parent file signature. It is also a foreign key in the Signature table.</td></row>
+		<row><td>ISXmlLocator</td><td>Signature_</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>The Signature_ represents a unique file signature and is also the foreign key in the Signature,  RegLocator, IniLocator, ISXmlLocator, CompLocator and the DrLocator tables.</td></row>
+		<row><td>Icon</td><td>Data</td><td>Y</td><td/><td/><td/><td/><td>Binary</td><td/><td>Binary stream. The binary icon data in PE (.DLL or .EXE) or icon (.ICO) format.</td></row>
+		<row><td>Icon</td><td>ISBuildSourcePath</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Full path to the ICO or EXE file.</td></row>
+		<row><td>Icon</td><td>ISIconIndex</td><td>Y</td><td>-32767</td><td>32767</td><td/><td/><td/><td/><td>Optional icon index to be extracted.</td></row>
+		<row><td>Icon</td><td>Name</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Primary key. Name of the icon file.</td></row>
+		<row><td>IniFile</td><td>Action</td><td>N</td><td/><td/><td/><td/><td/><td>0;1;3</td><td>The type of modification to be made, one of iifEnum</td></row>
+		<row><td>IniFile</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Foreign key into the Component table referencing component that controls the installing of the .INI value.</td></row>
+		<row><td>IniFile</td><td>DirProperty</td><td>Y</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Foreign key into the Directory table denoting the directory where the .INI file is.</td></row>
+		<row><td>IniFile</td><td>FileName</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>The .INI file name in which to write the information</td></row>
+		<row><td>IniFile</td><td>IniFile</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Primary key, non-localized token.</td></row>
+		<row><td>IniFile</td><td>Key</td><td>N</td><td/><td/><td/><td/><td>Formatted</td><td/><td>The .INI file key below Section.</td></row>
+		<row><td>IniFile</td><td>Section</td><td>N</td><td/><td/><td/><td/><td>Formatted</td><td/><td>The .INI file Section.</td></row>
+		<row><td>IniFile</td><td>Value</td><td>N</td><td/><td/><td/><td/><td>Formatted</td><td/><td>The value to be written.</td></row>
+		<row><td>IniLocator</td><td>Field</td><td>Y</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>The field in the .INI line. If Field is null or 0 the entire line is read.</td></row>
+		<row><td>IniLocator</td><td>FileName</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>The .INI file name.</td></row>
+		<row><td>IniLocator</td><td>Key</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Key value (followed by an equals sign in INI file).</td></row>
+		<row><td>IniLocator</td><td>Section</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Section name within in file (within square brackets in INI file).</td></row>
+		<row><td>IniLocator</td><td>Signature_</td><td>N</td><td/><td/><td>Signature</td><td>1</td><td>Identifier</td><td/><td>The table key. The Signature_ represents a unique file signature and is also the foreign key in the Signature table.</td></row>
+		<row><td>IniLocator</td><td>Type</td><td>Y</td><td>0</td><td>2</td><td/><td/><td/><td/><td>An integer value that determines if the .INI value read is a filename or a directory location or to be used as is w/o interpretation.</td></row>
+		<row><td>InstallExecuteSequence</td><td>Action</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Name of action to invoke, either in the engine or the handler DLL.</td></row>
+		<row><td>InstallExecuteSequence</td><td>Condition</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Optional expression which skips the action if evaluates to expFalse.If the expression syntax is invalid, the engine will terminate, returning iesBadActionData.</td></row>
+		<row><td>InstallExecuteSequence</td><td>ISAttributes</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>This is used to store MM Custom Action Types</td></row>
+		<row><td>InstallExecuteSequence</td><td>ISComments</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Author’s comments on this Sequence.</td></row>
+		<row><td>InstallExecuteSequence</td><td>Sequence</td><td>Y</td><td>-4</td><td>32767</td><td/><td/><td/><td/><td>Number that determines the sort order in which the actions are to be executed.  Leave blank to suppress action.</td></row>
+		<row><td>InstallShield</td><td>Property</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Name of property, uppercase if settable by launcher or loader.</td></row>
+		<row><td>InstallShield</td><td>Value</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>String value for property.</td></row>
+		<row><td>InstallUISequence</td><td>Action</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Name of action to invoke, either in the engine or the handler DLL.</td></row>
+		<row><td>InstallUISequence</td><td>Condition</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Optional expression which skips the action if evaluates to expFalse.If the expression syntax is invalid, the engine will terminate, returning iesBadActionData.</td></row>
+		<row><td>InstallUISequence</td><td>ISAttributes</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>This is used to store MM Custom Action Types</td></row>
+		<row><td>InstallUISequence</td><td>ISComments</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Author’s comments on this Sequence.</td></row>
+		<row><td>InstallUISequence</td><td>Sequence</td><td>Y</td><td>-4</td><td>32767</td><td/><td/><td/><td/><td>Number that determines the sort order in which the actions are to be executed.  Leave blank to suppress action.</td></row>
+		<row><td>IsolatedComponent</td><td>Component_Application</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Key to Component table item for application</td></row>
+		<row><td>IsolatedComponent</td><td>Component_Shared</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Key to Component table item to be isolated</td></row>
+		<row><td>LaunchCondition</td><td>Condition</td><td>N</td><td/><td/><td/><td/><td>Condition</td><td/><td>Expression which must evaluate to TRUE in order for install to commence.</td></row>
+		<row><td>LaunchCondition</td><td>Description</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Localizable text to display when condition fails and install must abort.</td></row>
+		<row><td>ListBox</td><td>Order</td><td>N</td><td>1</td><td>32767</td><td/><td/><td/><td/><td>A positive integer used to determine the ordering of the items within one list..The integers do not have to be consecutive.</td></row>
+		<row><td>ListBox</td><td>Property</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>A named property to be tied to this item. All the items tied to the same property become part of the same listbox.</td></row>
+		<row><td>ListBox</td><td>Text</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>The visible text to be assigned to the item. Optional. If this entry or the entire column is missing, the text is the same as the value.</td></row>
+		<row><td>ListBox</td><td>Value</td><td>N</td><td/><td/><td/><td/><td>Formatted</td><td/><td>The value string associated with this item. Selecting the line will set the associated property to this value.</td></row>
+		<row><td>ListView</td><td>Binary_</td><td>Y</td><td/><td/><td>Binary</td><td>1</td><td>Identifier</td><td/><td>The name of the icon to be displayed with the icon. The binary information is looked up from the Binary Table.</td></row>
+		<row><td>ListView</td><td>Order</td><td>N</td><td>1</td><td>32767</td><td/><td/><td/><td/><td>A positive integer used to determine the ordering of the items within one list..The integers do not have to be consecutive.</td></row>
+		<row><td>ListView</td><td>Property</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>A named property to be tied to this item. All the items tied to the same property become part of the same listview.</td></row>
+		<row><td>ListView</td><td>Text</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>The visible text to be assigned to the item. Optional. If this entry or the entire column is missing, the text is the same as the value.</td></row>
+		<row><td>ListView</td><td>Value</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>The value string associated with this item. Selecting the line will set the associated property to this value.</td></row>
+		<row><td>LockPermissions</td><td>Domain</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Domain name for user whose permissions are being set. (usually a property)</td></row>
+		<row><td>LockPermissions</td><td>LockObject</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Foreign key into Registry or File table</td></row>
+		<row><td>LockPermissions</td><td>Permission</td><td>Y</td><td>-2147483647</td><td>2147483647</td><td/><td/><td/><td/><td>Permission Access mask.  Full Control = 268435456 (GENERIC_ALL = 0x10000000)</td></row>
+		<row><td>LockPermissions</td><td>Table</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td>Directory;File;Registry</td><td>Reference to another table name</td></row>
+		<row><td>LockPermissions</td><td>User</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>User for permissions to be set.  (usually a property)</td></row>
+		<row><td>MIME</td><td>CLSID</td><td>Y</td><td/><td/><td>Class</td><td>1</td><td>Guid</td><td/><td>Optional associated CLSID.</td></row>
+		<row><td>MIME</td><td>ContentType</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Primary key. Context identifier, typically "type/format".</td></row>
+		<row><td>MIME</td><td>Extension_</td><td>N</td><td/><td/><td>Extension</td><td>1</td><td>Text</td><td/><td>Optional associated extension (without dot)</td></row>
+		<row><td>Media</td><td>Cabinet</td><td>Y</td><td/><td/><td/><td/><td>Cabinet</td><td/><td>If some or all of the files stored on the media are compressed in a cabinet, the name of that cabinet.</td></row>
+		<row><td>Media</td><td>DiskId</td><td>N</td><td>1</td><td>32767</td><td/><td/><td/><td/><td>Primary key, integer to determine sort order for table.</td></row>
+		<row><td>Media</td><td>DiskPrompt</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Disk name: the visible text actually printed on the disk.  This will be used to prompt the user when this disk needs to be inserted.</td></row>
+		<row><td>Media</td><td>LastSequence</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>File sequence number for the last file for this media.</td></row>
+		<row><td>Media</td><td>Source</td><td>Y</td><td/><td/><td/><td/><td>Property</td><td/><td>The property defining the location of the cabinet file.</td></row>
+		<row><td>Media</td><td>VolumeLabel</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>The label attributed to the volume.</td></row>
+		<row><td>MoveFile</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>If this component is not "selected" for installation or removal, no action will be taken on the associated MoveFile entry</td></row>
+		<row><td>MoveFile</td><td>DestFolder</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Name of a property whose value is assumed to resolve to the full path to the destination directory</td></row>
+		<row><td>MoveFile</td><td>DestName</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Name to be given to the original file after it is moved or copied.  If blank, the destination file will be given the same name as the source file</td></row>
+		<row><td>MoveFile</td><td>FileKey</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Primary key that uniquely identifies a particular MoveFile record</td></row>
+		<row><td>MoveFile</td><td>Options</td><td>N</td><td>0</td><td>1</td><td/><td/><td/><td/><td>Integer value specifying the MoveFile operating mode, one of imfoEnum</td></row>
+		<row><td>MoveFile</td><td>SourceFolder</td><td>Y</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Name of a property whose value is assumed to resolve to the full path to the source directory</td></row>
+		<row><td>MoveFile</td><td>SourceName</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Name of the source file(s) to be moved or copied.  Can contain the '*' or '?' wildcards.</td></row>
+		<row><td>MsiAssembly</td><td>Attributes</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>Assembly attributes</td></row>
+		<row><td>MsiAssembly</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Foreign key into Component table.</td></row>
+		<row><td>MsiAssembly</td><td>Feature_</td><td>N</td><td/><td/><td>Feature</td><td>1</td><td>Identifier</td><td/><td>Foreign key into Feature table.</td></row>
+		<row><td>MsiAssembly</td><td>File_Application</td><td>Y</td><td/><td/><td>File</td><td>1</td><td>Identifier</td><td/><td>Foreign key into File table, denoting the application context for private assemblies. Null for global assemblies.</td></row>
+		<row><td>MsiAssembly</td><td>File_Manifest</td><td>Y</td><td/><td/><td>File</td><td>1</td><td>Identifier</td><td/><td>Foreign key into the File table denoting the manifest file for the assembly.</td></row>
+		<row><td>MsiAssemblyName</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Foreign key into Component table.</td></row>
+		<row><td>MsiAssemblyName</td><td>Name</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>The name part of the name-value pairs for the assembly name.</td></row>
+		<row><td>MsiAssemblyName</td><td>Value</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>The value part of the name-value pairs for the assembly name.</td></row>
+		<row><td>MsiDigitalCertificate</td><td>CertData</td><td>N</td><td/><td/><td/><td/><td>Binary</td><td/><td>A certificate context blob for a signer certificate</td></row>
+		<row><td>MsiDigitalCertificate</td><td>DigitalCertificate</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>A unique identifier for the row</td></row>
+		<row><td>MsiDigitalSignature</td><td>DigitalCertificate_</td><td>N</td><td/><td/><td>MsiDigitalCertificate</td><td>1</td><td>Identifier</td><td/><td>Foreign key to MsiDigitalCertificate table identifying the signer certificate</td></row>
+		<row><td>MsiDigitalSignature</td><td>Hash</td><td>Y</td><td/><td/><td/><td/><td>Binary</td><td/><td>The encoded hash blob from the digital signature</td></row>
+		<row><td>MsiDigitalSignature</td><td>SignObject</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Foreign key to Media table</td></row>
+		<row><td>MsiDigitalSignature</td><td>Table</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Reference to another table name (only Media table is supported)</td></row>
+		<row><td>MsiDriverPackages</td><td>Component</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Primary key used to identify a particular component record.</td></row>
+		<row><td>MsiDriverPackages</td><td>Flags</td><td>N</td><td/><td/><td/><td/><td/><td/><td>Driver package flags</td></row>
+		<row><td>MsiDriverPackages</td><td>ReferenceComponents</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>MsiDriverPackages</td><td>Sequence</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>Installation sequence number</td></row>
+		<row><td>MsiFileHash</td><td>File_</td><td>N</td><td/><td/><td>File</td><td>1</td><td>Identifier</td><td/><td>Primary key, foreign key into File table referencing file with this hash</td></row>
+		<row><td>MsiFileHash</td><td>HashPart1</td><td>N</td><td/><td/><td/><td/><td/><td/><td>Size of file in bytes (long integer).</td></row>
+		<row><td>MsiFileHash</td><td>HashPart2</td><td>N</td><td/><td/><td/><td/><td/><td/><td>Size of file in bytes (long integer).</td></row>
+		<row><td>MsiFileHash</td><td>HashPart3</td><td>N</td><td/><td/><td/><td/><td/><td/><td>Size of file in bytes (long integer).</td></row>
+		<row><td>MsiFileHash</td><td>HashPart4</td><td>N</td><td/><td/><td/><td/><td/><td/><td>Size of file in bytes (long integer).</td></row>
+		<row><td>MsiFileHash</td><td>Options</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>Various options and attributes for this hash.</td></row>
+		<row><td>MsiPatchCertificate</td><td>DigitalCertificate_</td><td>N</td><td/><td/><td>MsiDigitalCertificate</td><td>1</td><td>Identifier</td><td/><td>A foreign key to the digital certificate table</td></row>
+		<row><td>MsiPatchCertificate</td><td>PatchCertificate</td><td>Y</td><td/><td/><td/><td/><td>Identifier</td><td/><td>A unique identifier for the row</td></row>
+		<row><td>MsiPatchMetadata</td><td>Company</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Optional company name</td></row>
+		<row><td>MsiPatchMetadata</td><td>PatchConfiguration_</td><td>N</td><td/><td/><td>ISPatchConfiguration</td><td>1</td><td>Text</td><td/><td>Foreign key to the ISPatchConfiguration table</td></row>
+		<row><td>MsiPatchMetadata</td><td>Property</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Name of the metadata</td></row>
+		<row><td>MsiPatchMetadata</td><td>Value</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Value of the metadata</td></row>
+		<row><td>MsiPatchOldAssemblyFile</td><td>Assembly_</td><td>Y</td><td/><td/><td>MsiPatchOldAssemblyName</td><td>1</td><td/><td/><td/></row>
+		<row><td>MsiPatchOldAssemblyFile</td><td>File_</td><td>N</td><td/><td/><td>File</td><td>1</td><td/><td/><td/></row>
+		<row><td>MsiPatchOldAssemblyName</td><td>Assembly_</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>MsiPatchOldAssemblyName</td><td>Name</td><td>N</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>MsiPatchOldAssemblyName</td><td>Value</td><td>Y</td><td/><td/><td/><td/><td/><td/><td/></row>
+		<row><td>MsiPatchSequence</td><td>PatchConfiguration_</td><td>N</td><td/><td/><td>ISPatchConfiguration</td><td>1</td><td>Text</td><td/><td>Foreign key to the patch configuration table</td></row>
+		<row><td>MsiPatchSequence</td><td>PatchFamily</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Name of the family to which this patch belongs</td></row>
+		<row><td>MsiPatchSequence</td><td>Sequence</td><td>N</td><td/><td/><td/><td/><td>Version</td><td/><td>The version of this patch in this family</td></row>
+		<row><td>MsiPatchSequence</td><td>Supersede</td><td>N</td><td/><td/><td/><td/><td>Integer</td><td/><td>Supersede</td></row>
+		<row><td>MsiPatchSequence</td><td>Target</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Target product codes for this patch family</td></row>
+		<row><td>ODBCAttribute</td><td>Attribute</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Name of ODBC driver attribute</td></row>
+		<row><td>ODBCAttribute</td><td>Driver_</td><td>N</td><td/><td/><td>ODBCDriver</td><td>1</td><td>Identifier</td><td/><td>Reference to ODBC driver in ODBCDriver table</td></row>
+		<row><td>ODBCAttribute</td><td>Value</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Value for ODBC driver attribute</td></row>
+		<row><td>ODBCDataSource</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Reference to associated component</td></row>
+		<row><td>ODBCDataSource</td><td>DataSource</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Primary key, non-localized.internal token for data source</td></row>
+		<row><td>ODBCDataSource</td><td>Description</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Text used as registered name for data source</td></row>
+		<row><td>ODBCDataSource</td><td>DriverDescription</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Reference to driver description, may be existing driver</td></row>
+		<row><td>ODBCDataSource</td><td>Registration</td><td>N</td><td>0</td><td>1</td><td/><td/><td/><td/><td>Registration option: 0=machine, 1=user, others t.b.d.</td></row>
+		<row><td>ODBCDriver</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Reference to associated component</td></row>
+		<row><td>ODBCDriver</td><td>Description</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Text used as registered name for driver, non-localized</td></row>
+		<row><td>ODBCDriver</td><td>Driver</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Primary key, non-localized.internal token for driver</td></row>
+		<row><td>ODBCDriver</td><td>File_</td><td>N</td><td/><td/><td>File</td><td>1</td><td>Identifier</td><td/><td>Reference to key driver file</td></row>
+		<row><td>ODBCDriver</td><td>File_Setup</td><td>Y</td><td/><td/><td>File</td><td>1</td><td>Identifier</td><td/><td>Optional reference to key driver setup DLL</td></row>
+		<row><td>ODBCSourceAttribute</td><td>Attribute</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Name of ODBC data source attribute</td></row>
+		<row><td>ODBCSourceAttribute</td><td>DataSource_</td><td>N</td><td/><td/><td>ODBCDataSource</td><td>1</td><td>Identifier</td><td/><td>Reference to ODBC data source in ODBCDataSource table</td></row>
+		<row><td>ODBCSourceAttribute</td><td>Value</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Value for ODBC data source attribute</td></row>
+		<row><td>ODBCTranslator</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Reference to associated component</td></row>
+		<row><td>ODBCTranslator</td><td>Description</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>Text used as registered name for translator</td></row>
+		<row><td>ODBCTranslator</td><td>File_</td><td>N</td><td/><td/><td>File</td><td>1</td><td>Identifier</td><td/><td>Reference to key translator file</td></row>
+		<row><td>ODBCTranslator</td><td>File_Setup</td><td>Y</td><td/><td/><td>File</td><td>1</td><td>Identifier</td><td/><td>Optional reference to key translator setup DLL</td></row>
+		<row><td>ODBCTranslator</td><td>Translator</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Primary key, non-localized.internal token for translator</td></row>
+		<row><td>Patch</td><td>Attributes</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>Integer containing bit flags representing patch attributes</td></row>
+		<row><td>Patch</td><td>File_</td><td>N</td><td/><td/><td>File</td><td>1</td><td>Identifier</td><td/><td>Primary key, non-localized token, foreign key to File table, must match identifier in cabinet.</td></row>
+		<row><td>Patch</td><td>Header</td><td>Y</td><td/><td/><td/><td/><td>Binary</td><td/><td>Binary stream. The patch header, used for patch validation.</td></row>
+		<row><td>Patch</td><td>ISBuildSourcePath</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Full path to patch header.</td></row>
+		<row><td>Patch</td><td>PatchSize</td><td>N</td><td>0</td><td>2147483647</td><td/><td/><td/><td/><td>Size of patch in bytes (long integer).</td></row>
+		<row><td>Patch</td><td>Sequence</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>Primary key, sequence with respect to the media images; order must track cabinet order.</td></row>
+		<row><td>PatchPackage</td><td>Media_</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>Foreign key to DiskId column of Media table. Indicates the disk containing the patch package.</td></row>
+		<row><td>PatchPackage</td><td>PatchId</td><td>N</td><td/><td/><td/><td/><td>Guid</td><td/><td>A unique string GUID representing this patch.</td></row>
+		<row><td>ProgId</td><td>Class_</td><td>Y</td><td/><td/><td>Class</td><td>1</td><td>Guid</td><td/><td>The CLSID of an OLE factory corresponding to the ProgId.</td></row>
+		<row><td>ProgId</td><td>Description</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Localized description for the Program identifier.</td></row>
+		<row><td>ProgId</td><td>ISAttributes</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>This is used to store Installshield custom properties of a component, like ExtractIcon, etc.</td></row>
+		<row><td>ProgId</td><td>IconIndex</td><td>Y</td><td>-32767</td><td>32767</td><td/><td/><td/><td/><td>Optional icon index.</td></row>
+		<row><td>ProgId</td><td>Icon_</td><td>Y</td><td/><td/><td>Icon</td><td>1</td><td>Identifier</td><td/><td>Optional foreign key into the Icon Table, specifying the icon file associated with this ProgId. Will be written under the DefaultIcon key.</td></row>
+		<row><td>ProgId</td><td>ProgId</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>The Program Identifier. Primary key.</td></row>
+		<row><td>ProgId</td><td>ProgId_Parent</td><td>Y</td><td/><td/><td>ProgId</td><td>1</td><td>Text</td><td/><td>The Parent Program Identifier. If specified, the ProgId column becomes a version independent prog id.</td></row>
+		<row><td>Property</td><td>ISComments</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>User Comments.</td></row>
+		<row><td>Property</td><td>Property</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Name of property, uppercase if settable by launcher or loader.</td></row>
+		<row><td>Property</td><td>Value</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>String value for property.</td></row>
+		<row><td>PublishComponent</td><td>AppData</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>This is localisable Application specific data that can be associated with a Qualified Component.</td></row>
+		<row><td>PublishComponent</td><td>ComponentId</td><td>N</td><td/><td/><td/><td/><td>Guid</td><td/><td>A string GUID that represents the component id that will be requested by the alien product.</td></row>
+		<row><td>PublishComponent</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Foreign key into the Component table.</td></row>
+		<row><td>PublishComponent</td><td>Feature_</td><td>N</td><td/><td/><td>Feature</td><td>1</td><td>Identifier</td><td/><td>Foreign key into the Feature table.</td></row>
+		<row><td>PublishComponent</td><td>Qualifier</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>This is defined only when the ComponentId column is an Qualified Component Id. This is the Qualifier for ProvideComponentIndirect.</td></row>
+		<row><td>RadioButton</td><td>Height</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>The height of the button.</td></row>
+		<row><td>RadioButton</td><td>Help</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>The help strings used with the button. The text is optional.</td></row>
+		<row><td>RadioButton</td><td>ISControlId</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>A number used to represent the control ID of the Control, Used in Dialog export</td></row>
+		<row><td>RadioButton</td><td>Order</td><td>N</td><td>1</td><td>32767</td><td/><td/><td/><td/><td>A positive integer used to determine the ordering of the items within one list..The integers do not have to be consecutive.</td></row>
+		<row><td>RadioButton</td><td>Property</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>A named property to be tied to this radio button. All the buttons tied to the same property become part of the same group.</td></row>
+		<row><td>RadioButton</td><td>Text</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>The visible title to be assigned to the radio button.</td></row>
+		<row><td>RadioButton</td><td>Value</td><td>N</td><td/><td/><td/><td/><td>Formatted</td><td/><td>The value string associated with this button. Selecting the button will set the associated property to this value.</td></row>
+		<row><td>RadioButton</td><td>Width</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>The width of the button.</td></row>
+		<row><td>RadioButton</td><td>X</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>The horizontal coordinate of the upper left corner of the bounding rectangle of the radio button.</td></row>
+		<row><td>RadioButton</td><td>Y</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>The vertical coordinate of the upper left corner of the bounding rectangle of the radio button.</td></row>
+		<row><td>RegLocator</td><td>Key</td><td>N</td><td/><td/><td/><td/><td>RegPath</td><td/><td>The key for the registry value.</td></row>
+		<row><td>RegLocator</td><td>Name</td><td>Y</td><td/><td/><td/><td/><td>Formatted</td><td/><td>The registry value name.</td></row>
+		<row><td>RegLocator</td><td>Root</td><td>N</td><td>0</td><td>3</td><td/><td/><td/><td/><td>The predefined root key for the registry value, one of rrkEnum.</td></row>
+		<row><td>RegLocator</td><td>Signature_</td><td>N</td><td/><td/><td>Signature</td><td>1</td><td>Identifier</td><td/><td>The table key. The Signature_ represents a unique file signature and is also the foreign key in the Signature table. If the type is 0, the registry values refers a directory, and _Signature is not a foreign key.</td></row>
+		<row><td>RegLocator</td><td>Type</td><td>Y</td><td>0</td><td>18</td><td/><td/><td/><td/><td>An integer value that determines if the registry value is a filename or a directory location or to be used as is w/o interpretation.</td></row>
+		<row><td>Registry</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Foreign key into the Component table referencing component that controls the installing of the registry value.</td></row>
+		<row><td>Registry</td><td>ISAttributes</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>This is used to store Installshield custom properties of a registry item.  Currently the only one is Automatic.</td></row>
+		<row><td>Registry</td><td>Key</td><td>N</td><td/><td/><td/><td/><td>RegPath</td><td/><td>The key for the registry value.</td></row>
+		<row><td>Registry</td><td>Name</td><td>Y</td><td/><td/><td/><td/><td>Formatted</td><td/><td>The registry value name.</td></row>
+		<row><td>Registry</td><td>Registry</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Primary key, non-localized token.</td></row>
+		<row><td>Registry</td><td>Root</td><td>N</td><td>-1</td><td>3</td><td/><td/><td/><td/><td>The predefined root key for the registry value, one of rrkEnum.</td></row>
+		<row><td>Registry</td><td>Value</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>The registry value.</td></row>
+		<row><td>RemoveFile</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Foreign key referencing Component that controls the file to be removed.</td></row>
+		<row><td>RemoveFile</td><td>DirProperty</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Name of a property whose value is assumed to resolve to the full pathname to the folder of the file to be removed.</td></row>
+		<row><td>RemoveFile</td><td>FileKey</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Primary key used to identify a particular file entry</td></row>
+		<row><td>RemoveFile</td><td>FileName</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Name of the file to be removed.</td></row>
+		<row><td>RemoveFile</td><td>InstallMode</td><td>N</td><td/><td/><td/><td/><td/><td>1;2;3</td><td>Installation option, one of iimEnum.</td></row>
+		<row><td>RemoveIniFile</td><td>Action</td><td>N</td><td/><td/><td/><td/><td/><td>2;4</td><td>The type of modification to be made, one of iifEnum.</td></row>
+		<row><td>RemoveIniFile</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Foreign key into the Component table referencing component that controls the deletion of the .INI value.</td></row>
+		<row><td>RemoveIniFile</td><td>DirProperty</td><td>Y</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Foreign key into the Directory table denoting the directory where the .INI file is.</td></row>
+		<row><td>RemoveIniFile</td><td>FileName</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>The .INI file name in which to delete the information</td></row>
+		<row><td>RemoveIniFile</td><td>Key</td><td>N</td><td/><td/><td/><td/><td>Formatted</td><td/><td>The .INI file key below Section.</td></row>
+		<row><td>RemoveIniFile</td><td>RemoveIniFile</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Primary key, non-localized token.</td></row>
+		<row><td>RemoveIniFile</td><td>Section</td><td>N</td><td/><td/><td/><td/><td>Formatted</td><td/><td>The .INI file Section.</td></row>
+		<row><td>RemoveIniFile</td><td>Value</td><td>Y</td><td/><td/><td/><td/><td>Formatted</td><td/><td>The value to be deleted. The value is required when Action is iifIniRemoveTag</td></row>
+		<row><td>RemoveRegistry</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Foreign key into the Component table referencing component that controls the deletion of the registry value.</td></row>
+		<row><td>RemoveRegistry</td><td>Key</td><td>N</td><td/><td/><td/><td/><td>RegPath</td><td/><td>The key for the registry value.</td></row>
+		<row><td>RemoveRegistry</td><td>Name</td><td>Y</td><td/><td/><td/><td/><td>Formatted</td><td/><td>The registry value name.</td></row>
+		<row><td>RemoveRegistry</td><td>RemoveRegistry</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Primary key, non-localized token.</td></row>
+		<row><td>RemoveRegistry</td><td>Root</td><td>N</td><td>-1</td><td>3</td><td/><td/><td/><td/><td>The predefined root key for the registry value, one of rrkEnum</td></row>
+		<row><td>ReserveCost</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Reserve a specified amount of space if this component is to be installed.</td></row>
+		<row><td>ReserveCost</td><td>ReserveFolder</td><td>Y</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Name of a property whose value is assumed to resolve to the full path to the destination directory</td></row>
+		<row><td>ReserveCost</td><td>ReserveKey</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Primary key that uniquely identifies a particular ReserveCost record</td></row>
+		<row><td>ReserveCost</td><td>ReserveLocal</td><td>N</td><td>0</td><td>2147483647</td><td/><td/><td/><td/><td>Disk space to reserve if linked component is installed locally.</td></row>
+		<row><td>ReserveCost</td><td>ReserveSource</td><td>N</td><td>0</td><td>2147483647</td><td/><td/><td/><td/><td>Disk space to reserve if linked component is installed to run from the source location.</td></row>
+		<row><td>SFPCatalog</td><td>Catalog</td><td>Y</td><td/><td/><td/><td/><td>Binary</td><td/><td>SFP Catalog</td></row>
+		<row><td>SFPCatalog</td><td>Dependency</td><td>Y</td><td/><td/><td/><td/><td>Formatted</td><td/><td>Parent catalog - only used by SFP</td></row>
+		<row><td>SFPCatalog</td><td>SFPCatalog</td><td>N</td><td/><td/><td/><td/><td>Filename</td><td/><td>File name for the catalog.</td></row>
+		<row><td>SelfReg</td><td>Cost</td><td>Y</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>The cost of registering the module.</td></row>
+		<row><td>SelfReg</td><td>File_</td><td>N</td><td/><td/><td>File</td><td>1</td><td>Identifier</td><td/><td>Foreign key into the File table denoting the module that needs to be registered.</td></row>
+		<row><td>ServiceControl</td><td>Arguments</td><td>Y</td><td/><td/><td/><td/><td>Formatted</td><td/><td>Arguments for the service.  Separate by [~].</td></row>
+		<row><td>ServiceControl</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Required foreign key into the Component Table that controls the startup of the service</td></row>
+		<row><td>ServiceControl</td><td>Event</td><td>N</td><td>0</td><td>187</td><td/><td/><td/><td/><td>Bit field:  Install:  0x1 = Start, 0x2 = Stop, 0x8 = Delete, Uninstall: 0x10 = Start, 0x20 = Stop, 0x80 = Delete</td></row>
+		<row><td>ServiceControl</td><td>Name</td><td>N</td><td/><td/><td/><td/><td>Formatted</td><td/><td>Name of a service. /, \, comma and space are invalid</td></row>
+		<row><td>ServiceControl</td><td>ServiceControl</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Primary key, non-localized token.</td></row>
+		<row><td>ServiceControl</td><td>Wait</td><td>Y</td><td>0</td><td>1</td><td/><td/><td/><td/><td>Boolean for whether to wait for the service to fully start</td></row>
+		<row><td>ServiceInstall</td><td>Arguments</td><td>Y</td><td/><td/><td/><td/><td>Formatted</td><td/><td>Arguments to include in every start of the service, passed to WinMain</td></row>
+		<row><td>ServiceInstall</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Required foreign key into the Component Table that controls the startup of the service</td></row>
+		<row><td>ServiceInstall</td><td>Dependencies</td><td>Y</td><td/><td/><td/><td/><td>Formatted</td><td/><td>Other services this depends on to start.  Separate by [~], and end with [~][~]</td></row>
+		<row><td>ServiceInstall</td><td>Description</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Description of service.</td></row>
+		<row><td>ServiceInstall</td><td>DisplayName</td><td>Y</td><td/><td/><td/><td/><td>Formatted</td><td/><td>External Name of the Service</td></row>
+		<row><td>ServiceInstall</td><td>ErrorControl</td><td>N</td><td>-2147483647</td><td>2147483647</td><td/><td/><td/><td/><td>Severity of error if service fails to start</td></row>
+		<row><td>ServiceInstall</td><td>LoadOrderGroup</td><td>Y</td><td/><td/><td/><td/><td>Formatted</td><td/><td>LoadOrderGroup</td></row>
+		<row><td>ServiceInstall</td><td>Name</td><td>N</td><td/><td/><td/><td/><td>Formatted</td><td/><td>Internal Name of the Service</td></row>
+		<row><td>ServiceInstall</td><td>Password</td><td>Y</td><td/><td/><td/><td/><td>Formatted</td><td/><td>password to run service with.  (with StartName)</td></row>
+		<row><td>ServiceInstall</td><td>ServiceInstall</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Primary key, non-localized token.</td></row>
+		<row><td>ServiceInstall</td><td>ServiceType</td><td>N</td><td>-2147483647</td><td>2147483647</td><td/><td/><td/><td/><td>Type of the service</td></row>
+		<row><td>ServiceInstall</td><td>StartName</td><td>Y</td><td/><td/><td/><td/><td>Formatted</td><td/><td>User or object name to run service as</td></row>
+		<row><td>ServiceInstall</td><td>StartType</td><td>N</td><td>0</td><td>4</td><td/><td/><td/><td/><td>Type of the service</td></row>
+		<row><td>Shortcut</td><td>Arguments</td><td>Y</td><td/><td/><td/><td/><td>Formatted</td><td/><td>The command-line arguments for the shortcut.</td></row>
+		<row><td>Shortcut</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Foreign key into the Component table denoting the component whose selection gates the the shortcut creation/deletion.</td></row>
+		<row><td>Shortcut</td><td>Description</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>The description for the shortcut.</td></row>
+		<row><td>Shortcut</td><td>Directory_</td><td>N</td><td/><td/><td>Directory</td><td>1</td><td>Identifier</td><td/><td>Foreign key into the Directory table denoting the directory where the shortcut file is created.</td></row>
+		<row><td>Shortcut</td><td>Hotkey</td><td>Y</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>The hotkey for the shortcut. It has the virtual-key code for the key in the low-order byte, and the modifier flags in the high-order byte. </td></row>
+		<row><td>Shortcut</td><td>ISAttributes</td><td>Y</td><td/><td/><td/><td/><td/><td/><td>This is used to store Installshield custom properties of a shortcut.  Mainly used in pro project types.</td></row>
+		<row><td>Shortcut</td><td>ISComments</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Author’s comments on this Shortcut.</td></row>
+		<row><td>Shortcut</td><td>ISShortcutName</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>A non-unique name for the shortcut.  Mainly used by pro pro project types.</td></row>
+		<row><td>Shortcut</td><td>IconIndex</td><td>Y</td><td>-32767</td><td>32767</td><td/><td/><td/><td/><td>The icon index for the shortcut.</td></row>
+		<row><td>Shortcut</td><td>Icon_</td><td>Y</td><td/><td/><td>Icon</td><td>1</td><td>Identifier</td><td/><td>Foreign key into the File table denoting the external icon file for the shortcut.</td></row>
+		<row><td>Shortcut</td><td>Name</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>The name of the shortcut to be created.</td></row>
+		<row><td>Shortcut</td><td>Shortcut</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Primary key, non-localized token.</td></row>
+		<row><td>Shortcut</td><td>ShowCmd</td><td>Y</td><td/><td/><td/><td/><td/><td>1;3;7</td><td>The show command for the application window.The following values may be used.</td></row>
+		<row><td>Shortcut</td><td>Target</td><td>N</td><td/><td/><td/><td/><td>Shortcut</td><td/><td>The shortcut target. This is usually a property that is expanded to a file or a folder that the shortcut points to.</td></row>
+		<row><td>Shortcut</td><td>WkDir</td><td>Y</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Name of property defining location of working directory.</td></row>
+		<row><td>Signature</td><td>FileName</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>The name of the file. This may contain a "short name|long name" pair.</td></row>
+		<row><td>Signature</td><td>Languages</td><td>Y</td><td/><td/><td/><td/><td>Language</td><td/><td>The languages supported by the file.</td></row>
+		<row><td>Signature</td><td>MaxDate</td><td>Y</td><td>0</td><td>2147483647</td><td/><td/><td/><td/><td>The maximum creation date of the file.</td></row>
+		<row><td>Signature</td><td>MaxSize</td><td>Y</td><td>0</td><td>2147483647</td><td/><td/><td/><td/><td>The maximum size of the file. </td></row>
+		<row><td>Signature</td><td>MaxVersion</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>The maximum version of the file.</td></row>
+		<row><td>Signature</td><td>MinDate</td><td>Y</td><td>0</td><td>2147483647</td><td/><td/><td/><td/><td>The minimum creation date of the file.</td></row>
+		<row><td>Signature</td><td>MinSize</td><td>Y</td><td>0</td><td>2147483647</td><td/><td/><td/><td/><td>The minimum size of the file.</td></row>
+		<row><td>Signature</td><td>MinVersion</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>The minimum version of the file.</td></row>
+		<row><td>Signature</td><td>Signature</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>The table key. The Signature represents a unique file signature.</td></row>
+		<row><td>TextStyle</td><td>Color</td><td>Y</td><td>0</td><td>16777215</td><td/><td/><td/><td/><td>A long integer indicating the color of the string in the RGB format (Red, Green, Blue each 0-255, RGB = R + 256*G + 256^2*B).</td></row>
+		<row><td>TextStyle</td><td>FaceName</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>A string indicating the name of the font used. Required. The string must be at most 31 characters long.</td></row>
+		<row><td>TextStyle</td><td>Size</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>The size of the font used. This size is given in our units (1/12 of the system font height). Assuming that the system font is set to 12 point size, this is equivalent to the point size.</td></row>
+		<row><td>TextStyle</td><td>StyleBits</td><td>Y</td><td>0</td><td>15</td><td/><td/><td/><td/><td>A combination of style bits.</td></row>
+		<row><td>TextStyle</td><td>TextStyle</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Name of the style. The primary key of this table. This name is embedded in the texts to indicate a style change.</td></row>
+		<row><td>TypeLib</td><td>Component_</td><td>N</td><td/><td/><td>Component</td><td>1</td><td>Identifier</td><td/><td>Required foreign key into the Component Table, specifying the component for which to return a path when called through LocateComponent.</td></row>
+		<row><td>TypeLib</td><td>Cost</td><td>Y</td><td>0</td><td>2147483647</td><td/><td/><td/><td/><td>The cost associated with the registration of the typelib. This column is currently optional.</td></row>
+		<row><td>TypeLib</td><td>Description</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td/></row>
+		<row><td>TypeLib</td><td>Directory_</td><td>Y</td><td/><td/><td>Directory</td><td>1</td><td>Identifier</td><td/><td>Optional. The foreign key into the Directory table denoting the path to the help file for the type library.</td></row>
+		<row><td>TypeLib</td><td>Feature_</td><td>N</td><td/><td/><td>Feature</td><td>1</td><td>Identifier</td><td/><td>Required foreign key into the Feature Table, specifying the feature to validate or install in order for the type library to be operational.</td></row>
+		<row><td>TypeLib</td><td>Language</td><td>N</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>The language of the library.</td></row>
+		<row><td>TypeLib</td><td>LibID</td><td>N</td><td/><td/><td/><td/><td>Guid</td><td/><td>The GUID that represents the library.</td></row>
+		<row><td>TypeLib</td><td>Version</td><td>Y</td><td>0</td><td>2147483647</td><td/><td/><td/><td/><td>The version of the library. The major version is in the upper 8 bits of the short integer. The minor version is in the lower 8 bits.</td></row>
+		<row><td>UIText</td><td>Key</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>A unique key that identifies the particular string.</td></row>
+		<row><td>UIText</td><td>Text</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>The localized version of the string.</td></row>
+		<row><td>Upgrade</td><td>ActionProperty</td><td>N</td><td/><td/><td/><td/><td>UpperCase</td><td/><td>The property to set when a product in this set is found.</td></row>
+		<row><td>Upgrade</td><td>Attributes</td><td>N</td><td>0</td><td>2147483647</td><td/><td/><td/><td/><td>The attributes of this product set.</td></row>
+		<row><td>Upgrade</td><td>ISDisplayName</td><td>Y</td><td/><td/><td>ISUpgradeMsiItem</td><td>1</td><td/><td/><td/></row>
+		<row><td>Upgrade</td><td>Language</td><td>Y</td><td/><td/><td/><td/><td>Language</td><td/><td>A comma-separated list of languages for either products in this set or products not in this set.</td></row>
+		<row><td>Upgrade</td><td>Remove</td><td>Y</td><td/><td/><td/><td/><td>Formatted</td><td/><td>The list of features to remove when uninstalling a product from this set.  The default is "ALL".</td></row>
+		<row><td>Upgrade</td><td>UpgradeCode</td><td>N</td><td/><td/><td/><td/><td>Guid</td><td/><td>The UpgradeCode GUID belonging to the products in this set.</td></row>
+		<row><td>Upgrade</td><td>VersionMax</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>The maximum ProductVersion of the products in this set.  The set may or may not include products with this particular version.</td></row>
+		<row><td>Upgrade</td><td>VersionMin</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>The minimum ProductVersion of the products in this set.  The set may or may not include products with this particular version.</td></row>
+		<row><td>Verb</td><td>Argument</td><td>Y</td><td/><td/><td/><td/><td>Formatted</td><td/><td>Optional value for the command arguments.</td></row>
+		<row><td>Verb</td><td>Command</td><td>Y</td><td/><td/><td/><td/><td>Formatted</td><td/><td>The command text.</td></row>
+		<row><td>Verb</td><td>Extension_</td><td>N</td><td/><td/><td>Extension</td><td>1</td><td>Text</td><td/><td>The extension associated with the table row.</td></row>
+		<row><td>Verb</td><td>Sequence</td><td>Y</td><td>0</td><td>32767</td><td/><td/><td/><td/><td>Order within the verbs for a particular extension. Also used simply to specify the default verb.</td></row>
+		<row><td>Verb</td><td>Verb</td><td>N</td><td/><td/><td/><td/><td>Text</td><td/><td>The verb for the command.</td></row>
+		<row><td>_Validation</td><td>Category</td><td>Y</td><td/><td/><td/><td/><td/><td>"Text";"Formatted";"Template";"Condition";"Guid";"Path";"Version";"Language";"Identifier";"Binary";"UpperCase";"LowerCase";"Filename";"Paths";"AnyPath";"WildCardFilename";"RegPath";"KeyFormatted";"CustomSource";"Property";"Cabinet";"Shortcut";"URL";"DefaultDir"</td><td>String category</td></row>
+		<row><td>_Validation</td><td>Column</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Name of column</td></row>
+		<row><td>_Validation</td><td>Description</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Description of column</td></row>
+		<row><td>_Validation</td><td>KeyColumn</td><td>Y</td><td>1</td><td>32</td><td/><td/><td/><td/><td>Column to which foreign key connects</td></row>
+		<row><td>_Validation</td><td>KeyTable</td><td>Y</td><td/><td/><td/><td/><td>Identifier</td><td/><td>For foreign key, Name of table to which data must link</td></row>
+		<row><td>_Validation</td><td>MaxValue</td><td>Y</td><td>-2147483647</td><td>2147483647</td><td/><td/><td/><td/><td>Maximum value allowed</td></row>
+		<row><td>_Validation</td><td>MinValue</td><td>Y</td><td>-2147483647</td><td>2147483647</td><td/><td/><td/><td/><td>Minimum value allowed</td></row>
+		<row><td>_Validation</td><td>Nullable</td><td>N</td><td/><td/><td/><td/><td/><td>Y;N;@</td><td>Whether the column is nullable</td></row>
+		<row><td>_Validation</td><td>Set</td><td>Y</td><td/><td/><td/><td/><td>Text</td><td/><td>Set of values that are permitted</td></row>
+		<row><td>_Validation</td><td>Table</td><td>N</td><td/><td/><td/><td/><td>Identifier</td><td/><td>Name of table</td></row>
+	</table>
+</msi>
diff --git a/connectors/jk/native/iis/installer/log/README b/connectors/jk/native/iis/installer/log/README
new file mode 100644
index 0000000..f14507e
--- /dev/null
+++ b/connectors/jk/native/iis/installer/log/README
@@ -0,0 +1 @@
+Jakarta Isapi Redirector log files
\ No newline at end of file
diff --git a/connectors/jk/native/iis/installer/tomcat.ico b/connectors/jk/native/iis/installer/tomcat.ico
new file mode 100644
index 0000000..6c5bd2c
--- /dev/null
+++ b/connectors/jk/native/iis/installer/tomcat.ico
Binary files differ
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..4e34aef
--- /dev/null
+++ b/connectors/jk/native/iis/isapi.dsp
@@ -0,0 +1,299 @@
+# 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" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /Zi /O2 /I "..\common" /I "pcre" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "ISAPI_EXPORTS" /D "HAS_PCRE" /D "PCRE_STATIC" /Fd"Release/isapi_redirector_src" /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 kernel32.lib user32.lib advapi32.lib ws2_32.lib mswsock.lib strsafe.lib /nologo /base:"0x6A6B0000" /dll /debug /machine:I386 /out:"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 "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 /MDd /W3 /GX /Zi /Od /I "..\common" /I "pcre" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "ISAPI_EXPORTS" /D "HAS_PCRE" /D "PCRE_STATIC" /Fd"Debug/isapi_redirector_src" /FD /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 kernel32.lib user32.lib advapi32.lib ws2_32.lib mswsock.lib strsafe.lib /nologo /base:"0x6A6B0000" /dll /incremental:no /debug /machine:I386 /out:"Debug\isapi_redirect.dll"
+
+!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=.\isapi_redirect.rc
+# 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_nwmain.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_pool.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_shm.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_sockbuf.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_status.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_shm.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_status.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.dsw b/connectors/jk/native/iis/isapi.dsw
new file mode 100644
index 0000000..8a4aa5b
--- /dev/null
+++ b/connectors/jk/native/iis/isapi.dsw
@@ -0,0 +1,59 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "dftables"=".\pcre\dftables.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 pcre
+    End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "pcre"=".\pcre\pcre.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name dftables
+    End Project Dependency
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
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.rc b/connectors/jk/native/iis/isapi_redirect.rc
new file mode 100644
index 0000000..a1a6075
--- /dev/null
+++ b/connectors/jk/native/iis/isapi_redirect.rc
@@ -0,0 +1,53 @@
+#define JK_COPYRIGHT "Copyright 2000-2007 The Apache Software " \
+                     "Foundation or its licensors, as applicable."
+
+#define JK_LICENSE "Licensed under the Apache License, Version 2.0 " \
+                    "(the ""License""); you may not use this file except " \
+                    "in compliance with the License.  You may obtain a " \
+                    "copy of the License at\r\n\r\n" \
+                    "http://www.apache.org/licenses/LICENSE-2.0\r\n\r\n" \
+                    "Unless required by applicable law or agreed to in " \
+                    "writing, software distributed under the License is " \
+                    "distributed on an ""AS IS"" BASIS, WITHOUT " \
+                    "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 JK_VERSION_STR  "1.2.24"
+#define JK_DLL_BASENAME "isapi_redirect-" JK_VERSION_STR
+
+
+1 VERSIONINFO
+ FILEVERSION 1,2,24,0
+ PRODUCTVERSION 1,2,24,0
+ FILEFLAGSMASK 0x3fL
+#if defined(_DEBUG)
+ FILEFLAGS 0x01L
+#else
+ FILEFLAGS 0x00L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+  BLOCK "StringFileInfo"
+  BEGIN
+    BLOCK "040904b0"
+    BEGIN
+    VALUE "Comments", JK_LICENSE "\0"
+      VALUE "CompanyName", "Apache Software Foundation\0"
+      VALUE "FileDescription", "Apache Tomcat IIS Redirector\0"
+      VALUE "FileVersion", JK_VERSION_STR "\0"
+      VALUE "InternalName", JK_DLL_BASENAME "\0"
+      VALUE "LegalCopyright", JK_COPYRIGHT "\0"
+      VALUE "OriginalFilename", JK_DLL_BASENAME ".dll\0"
+      VALUE "ProductName", "Apache Tomcat Connectors project\0"
+      VALUE "ProductVersion", JK_VERSION_STR "\0"
+    END
+  END
+  BLOCK "VarFileInfo"
+  BEGIN
+    VALUE "Translation", 0x409, 1200
+  END
+END
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..9bb7455
--- /dev/null
+++ b/connectors/jk/native/iis/jk_isapi_plugin.c
@@ -0,0 +1,2297 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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>                       *
+ * Author:      Mladen Turk <mturk@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"
+#include "jk_shm.h"
+#include "pcre.h"
+
+#ifndef POSIX_MALLOC_THRESHOLD
+#define POSIX_MALLOC_THRESHOLD (10)
+#endif
+
+#include <strsafe.h>
+
+#define VERSION_STRING "Jakarta/ISAPI/" JK_VERSTRING
+#define SHM_DEF_NAME   "JKISAPISHMEM"
+#define DEFAULT_WORKER_NAME ("ajp13")
+
+/*
+ * This is default value found inside httpd.conf
+ * for MaxClients
+ */
+#define DEFAULT_WORKER_THREADS  250
+
+/*
+ * 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_BASE              ("TOMCATURI")
+#define QUERY_HEADER_NAME_BASE            ("TOMCATQUERY")
+#define WORKER_HEADER_NAME_BASE           ("TOMCATWORKER")
+#define TOMCAT_TRANSLATE_HEADER_NAME_BASE ("TOMCATTRANSLATE")
+#define CONTENT_LENGTH                    ("CONTENT_LENGTH:")
+/* The template used to construct our unique headers
+ * from the base name and module instance
+ */
+#define HEADER_TEMPLATE      ("%s%p:")
+#define HTTP_HEADER_TEMPLATE ("HTTP_%s%p")
+
+static char URI_HEADER_NAME[MAX_PATH];
+static char QUERY_HEADER_NAME[MAX_PATH];
+static char WORKER_HEADER_NAME[MAX_PATH];
+static char TOMCAT_TRANSLATE_HEADER_NAME[MAX_PATH];
+
+static char HTTP_URI_HEADER_NAME[MAX_PATH];
+static char HTTP_QUERY_HEADER_NAME[MAX_PATH];
+static char HTTP_WORKER_HEADER_NAME[MAX_PATH];
+
+#define REGISTRY_LOCATION       ("Software\\Apache Software Foundation\\Jakarta Isapi Redirector\\1.0")
+#define W3SVC_REGISTRY_KEY      ("SYSTEM\\CurrentControlSet\\Services\\W3SVC\\Parameters")
+#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 URI_REWRITE_TAG             ("rewrite_rule_file")
+#define SHM_SIZE_TAG                ("shm_size")
+#define WORKER_MOUNT_RELOAD_TAG     ("worker_mount_reload")
+#define STRIP_SESSION_TAG           ("strip_session")
+#define AUTH_COMPLETE_TAG           ("auth_complete")
+
+
+#define TRANSLATE_HEADER            ("Translate:")
+#define TRANSLATE_HEADER_NAME       ("Translate")
+#define TRANSLATE_HEADER_NAME_LC    ("translate")
+
+#define BAD_REQUEST     -1
+#define BAD_PATH        -2
+#define MAX_SERVERNAME  128
+
+char HTML_ERROR_400[] =         "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n"
+                                "<HTML><HEAD><TITLE>Bad request!</TITLE></HEAD>\n"
+                                "<BODY><H1>Bad request!</H1>\n<P>"
+                                "Your browser (or proxy) sent a request that "
+                                "this server could not understand.</P></BODY></HTML>";
+
+char HTML_ERROR_404[] =         "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n"
+                                "<HTML><HEAD><TITLE>Object not found!</TITLE></HEAD>\n"
+                                "<BODY><H1>The requested URL was not found on this server"
+                                "</H1>\n<P>If you entered the URL manually please check your"
+                                "spelling and try again.</P></BODY></HTML>";
+
+char HTML_ERROR_500[] =         "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n"
+                                "<HTML><HEAD><TITLE>Server error!</TITLE></HEAD>\n"
+                                "<BODY><H1>Internal server error!</H1>\n<P>"
+                                "The server encountered an internal error and was "
+                                "unable to complete your request.</P></BODY></HTML>";
+
+char HTML_ERROR_503[] =         "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n"
+                                "<HTML><HEAD><TITLE>Service unavailable!</TITLE></HEAD>\n"
+                                "<BODY><H1>Service temporary unavailable!</H1>\n<P>"
+                                "The server is temporarily unable to service your "
+                                "request due to maintenance downtime or capacity problems. "
+                                "Please try again later.</P></BODY></HTML>";
+
+
+#define JK_TOLOWER(x)   ((char)tolower((BYTE)(x)))
+
+#define GET_SERVER_VARIABLE_VALUE(name, place)          \
+  do {                                                  \
+    (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);             \
+  } } while(0)
+
+#define GET_SERVER_VARIABLE_VALUE_INT(name, place, def)     \
+  do {                                                      \
+    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;                                      \
+  } } while(0)
+
+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 jk_uri_worker_map_t *uw_map = NULL;
+static jk_map_t *workers_map = NULL;
+static jk_map_t *rewrite_map = NULL;
+static jk_map_t *rregexp_map = NULL;
+
+static jk_logger_t *logger = NULL;
+static char *SERVER_NAME = "SERVER_NAME";
+static char *SERVER_SOFTWARE = "SERVER_SOFTWARE";
+static char *CONTENT_TYPE = "Content-Type:text/html\r\n\r\n";
+
+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_DEF_LEVEL;
+static char worker_file[MAX_PATH * 2];
+static char worker_mount_file[MAX_PATH * 2] = {0};
+static int  worker_mount_reload = JK_URIMAP_DEF_RELOAD;
+static char rewrite_rule_file[MAX_PATH * 2] = {0};
+static int shm_config_size = JK_SHM_DEF_SIZE;
+static int strip_session = 0;
+static DWORD auth_notification_flags = 0;
+static int   use_auth_notification_flags = 1;
+
+#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;
+
+typedef struct isapi_private_data_t isapi_private_data_t;
+struct isapi_private_data_t
+{
+    jk_pool_t p;
+
+    int request_started;
+    unsigned int bytes_read_so_far;
+    LPEXTENSION_CONTROL_BLOCK lpEcb;
+};
+
+typedef struct isapi_log_data_t isapi_log_data_t;
+struct isapi_log_data_t {
+    char uri[INTERNET_MAX_URL_LENGTH];
+    char query[INTERNET_MAX_URL_LENGTH];
+};
+
+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 int num_of_headers);
+
+static int JK_METHOD read(jk_ws_service_t *s,
+                          void *b, unsigned int l, unsigned int *a);
+
+static int JK_METHOD write(jk_ws_service_t *s, const void *b, unsigned int 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_config_parameter(LPVOID src, const char *tag,
+                                char *val, DWORD sz);
+
+static int get_config_bool(LPVOID src, const char *tag, int def);
+
+static int get_config_int(LPVOID src, const char *tag, int def);
+
+static int get_registry_config_parameter(HKEY hkey,
+                                         const char *tag, char *b, DWORD sz);
+
+static int get_registry_config_number(HKEY hkey, const char *tag,
+                                         int *val);
+
+
+static int get_server_value(LPEXTENSION_CONTROL_BLOCK lpEcb,
+                            char *name,
+                            char *buf, DWORD bufsz);
+
+static int base64_encode_cert_len(int len);
+
+static int base64_encode_cert(char *encoded,
+                              const char *string, int len);
+
+static int get_auth_flags();
+
+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]) != '\0') {
+                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 BYTE 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 int)(c)] & (f))
+
+static const char c2x_table[] = "0123456789abcdef";
+
+static BYTE *c2x(unsigned int what, BYTE *where)
+{
+    *where++ = '%';
+    *where++ = c2x_table[what >> 4];
+    *where++ = c2x_table[what & 0xf];
+    return where;
+}
+
+static char *status_reason(int status)
+{
+    static struct reasons {
+        int status;
+        char *reason;
+    } *r, reasons[] = {
+        { 100, "Continue" },
+        { 101, "Switching Protocols" },
+        { 200, "OK" },
+        { 201, "Created" },
+        { 202, "Accepted" },
+        { 203, "Non-Authoritative Information" },
+        { 204, "No Content" },
+        { 205, "Reset Content" },
+        { 206, "Partial Content" },
+        { 300, "Multiple Choices" },
+        { 301, "Moved Permanently" },
+        { 302, "Moved Temporarily" },
+        { 303, "See Other" },
+        { 304, "Not Modified" },
+        { 305, "Use Proxy" },
+        { 400, "Bad Request" },
+        { 401, "Unauthorized" },
+        { 402, "Payment Required" },
+        { 403, "Forbidden" },
+        { 404, "Not Found" },
+        { 405, "Method Not Allowed" },
+        { 406, "Not Acceptable" },
+        { 407, "Proxy Authentication Required" },
+        { 408, "Request Timeout" },
+        { 409, "Conflict" },
+        { 410, "Gone" },
+        { 411, "Length Required" },
+        { 412, "Precondition Failed" },
+        { 413, "Request Entity Too Large" },
+        { 414, "Request-URI Too Long" },
+        { 415, "Unsupported Media Type" },
+        { 500, "Internal Server Error" },
+        { 501, "Not Implemented" },
+        { 502, "Bad Gateway" },
+        { 503, "Service Unavailable" },
+        { 504, "Gateway Timeout" },
+        { 505, "HTTP Version Not Supported" },
+        { 000, NULL}
+    };
+
+    r = reasons;
+    while (r->status <= status)
+        if (r->status == status)
+            return r->reason;
+        else
+            r++;
+    return "No Reason";
+}
+
+static int escape_url(const char *path, char *dest, int destsize)
+{
+    const BYTE *s = (const BYTE *)path;
+    BYTE *d = (BYTE *)dest;
+    BYTE *e = d + destsize - 1;
+    BYTE *ee = d + destsize - 3;
+
+    while (*s) {
+        if (TEST_CHAR(*s, T_OS_ESCAPE_PATH)) {
+            if (d >= ee)
+                return JK_FALSE;
+            d = c2x(*s, d);
+        }
+        else {
+            if (d >= e)
+                return JK_FALSE;
+            *d++ = *s;
+        }
+        ++s;
+    }
+    *d = '\0';
+    return JK_TRUE;
+}
+
+/*
+ * Find the first occurrence of find in s.
+ */
+static char *stristr(const char *s, const char *find)
+{
+    char c, sc;
+    size_t len;
+
+    if ((c = tolower((unsigned char)(*find++))) != 0) {
+        len = strlen(find);
+        do {
+            do {
+                if ((sc = tolower((unsigned char)(*s++))) == 0)
+                    return (NULL);
+            } while (sc != c);
+        } while (strnicmp(s, find, len) != 0);
+        s--;
+    }
+    return ((char *)s);
+}
+
+static int uri_is_web_inf(const char *uri)
+{
+    if (stristr(uri, "/web-inf")) {
+        return JK_TRUE;
+    }
+    if (stristr(uri, "/meta-inf")) {
+        return JK_TRUE;
+    }
+
+    return JK_FALSE;
+}
+
+static void write_error_response(PHTTP_FILTER_CONTEXT pfc, char *status,
+                                 char *msg)
+{
+    DWORD len = (DWORD)strlen(msg);
+
+    /* reject !!! */
+    pfc->AddResponseHeaders(pfc, CONTENT_TYPE, 0);
+    pfc->ServerSupportFunction(pfc,
+                               SF_REQ_SEND_RESPONSE_HEADER,
+                               status, 0, 0);
+    pfc->WriteClient(pfc, msg, &len, 0);
+}
+
+static void write_error_message(LPEXTENSION_CONTROL_BLOCK lpEcb, int err)
+{
+    DWORD len;
+    if (err = 500) {
+        lpEcb->ServerSupportFunction(lpEcb->ConnID,
+                                     HSE_REQ_SEND_RESPONSE_HEADER,
+                                     "500 Internal Server Error",
+                                     0,
+                                     (LPDWORD)CONTENT_TYPE);
+        len = (DWORD)(sizeof(HTML_ERROR_500) - 1);
+        lpEcb->WriteClient(lpEcb->ConnID,
+                           HTML_ERROR_503, &len, 0);
+    }
+    else if (err == 503) {
+        lpEcb->ServerSupportFunction(lpEcb->ConnID,
+                                     HSE_REQ_SEND_RESPONSE_HEADER,
+                                     "503 Service Unavailable",
+                                     0,
+                                     (LPDWORD)CONTENT_TYPE);
+        len = (DWORD)(sizeof(HTML_ERROR_503) - 1);
+        lpEcb->WriteClient(lpEcb->ConnID,
+                           HTML_ERROR_503, &len, 0);
+    }
+    else {
+        return;
+    }
+}
+
+
+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 int num_of_headers)
+{
+    static char crlf[3] = { (char)13, (char)10, '\0' };
+
+    JK_TRACE_ENTER(logger);
+    if (status < 100 || status > 1000) {
+        jk_log(logger, JK_LOG_ERROR,
+               "invalid status %d",
+               status);
+        JK_TRACE_EXIT(logger);
+        return JK_FALSE;
+    }
+
+    if (s && s->ws_private) {
+        int rv = JK_TRUE;
+        isapi_private_data_t *p = s->ws_private;
+        if (!p->request_started) {
+            char *status_str;
+            DWORD status_str_len;
+            char *headers_str = NULL;
+            BOOL keep_alive = FALSE;
+            p->request_started = JK_TRUE;
+
+            /*
+             * Create the status line
+             */
+            if (!reason) {
+                reason = status_reason(status);
+            }
+            status_str = (char *)malloc((6 + strlen(reason)));
+            StringCbPrintf(status_str, 6 + strlen(reason), "%d %s", status, reason);
+            status_str_len = (DWORD)strlen(status_str);
+
+            /*
+             * Create response headers string
+             */
+            if (num_of_headers) {
+                size_t i, len_of_headers = 0;
+                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 *)malloc(len_of_headers);
+                headers_str[0] = '\0';
+
+                for (i = 0; i < num_of_headers; i++) {
+                    StringCbCat(headers_str, len_of_headers, header_names[i]);
+                    StringCbCat(headers_str, len_of_headers, ": ");
+                    StringCbCat(headers_str, len_of_headers, header_values[i]);
+                    StringCbCat(headers_str, len_of_headers, crlf);
+                }
+                StringCbCat(headers_str, len_of_headers, crlf);
+            }
+            else {
+                headers_str = crlf;
+            }
+
+            if (!p->lpEcb->ServerSupportFunction(p->lpEcb->ConnID,
+                                                 HSE_REQ_SEND_RESPONSE_HEADER,
+                                                 status_str,
+                                                 &status_str_len,
+                                                 (LPDWORD)headers_str)) {
+
+                jk_log(logger, JK_LOG_ERROR,
+                       "HSE_REQ_SEND_RESPONSE_HEADER failed with error=%08x",
+                       GetLastError());
+                rv = JK_FALSE;
+            }
+            if (headers_str)
+                free(headers_str);
+            if (status_str)
+                free(status_str);
+        }
+        JK_TRACE_EXIT(logger);
+        return rv;
+    }
+
+    JK_LOG_NULL_PARAMS(logger);
+    JK_TRACE_EXIT(logger);
+    return JK_FALSE;
+}
+
+static int JK_METHOD read(jk_ws_service_t *s,
+                          void *b, unsigned int l, unsigned int *a)
+{
+    JK_TRACE_ENTER(logger);
+
+    if (s && s->ws_private && b && a) {
+        isapi_private_data_t *p = s->ws_private;
+
+        if (JK_IS_DEBUG_LEVEL(logger)) {
+            jk_log(logger, JK_LOG_DEBUG,
+                   "Preparing to read %d bytes. "
+                   "ECB reports %d bytes total, with %d available.",
+                   l, p->lpEcb->cbTotalBytes, p->lpEcb->cbAvailable);
+        }
+
+        *a = 0;
+        if (l) {
+            char *buf = b;
+            DWORD already_read = p->lpEcb->cbAvailable - p->bytes_read_so_far;
+
+            if (already_read >= l) {
+                if (JK_IS_DEBUG_LEVEL(logger)) {
+                    jk_log(logger, JK_LOG_DEBUG,
+                           "Already read %d bytes - supplying %d bytes from buffer",
+                           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) {
+                    if (JK_IS_DEBUG_LEVEL(logger)) {
+                        jk_log(logger, JK_LOG_DEBUG,
+                               "Supplying %d bytes from buffer",
+                               already_read);
+                    }
+                    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 (JK_IS_DEBUG_LEVEL(logger)) {
+                    jk_log(logger, JK_LOG_DEBUG,
+                           "Attempting to read %d bytes from client", l);
+                }
+                if (p->lpEcb->ReadClient(p->lpEcb->ConnID, buf, (LPDWORD)&l)) {
+                    *a += l;
+                }
+                else {
+                    jk_log(logger, JK_LOG_ERROR,
+                           "ReadClient failed with %08x", GetLastError());
+                    JK_TRACE_EXIT(logger);
+                    return JK_FALSE;
+                }
+            }
+        }
+        JK_TRACE_EXIT(logger);
+        return JK_TRUE;
+    }
+
+    JK_LOG_NULL_PARAMS(logger);
+    JK_TRACE_EXIT(logger);
+    return JK_FALSE;
+}
+
+static int JK_METHOD write(jk_ws_service_t *s, const void *b, unsigned int l)
+{
+    JK_TRACE_ENTER(logger);
+
+    if (s && s->ws_private && b) {
+        isapi_private_data_t *p = s->ws_private;
+
+        if (l) {
+            unsigned int 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,
+                           "WriteClient failed with %08x", GetLastError());
+                    JK_TRACE_EXIT(logger);
+                    return JK_FALSE;
+                }
+                written += try_to_write;
+            }
+        }
+
+        JK_TRACE_EXIT(logger);
+        return JK_TRUE;
+
+    }
+
+    JK_LOG_NULL_PARAMS(logger);
+    JK_TRACE_EXIT(logger);
+    return JK_FALSE;
+}
+
+BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pVer)
+{
+    BOOL rv = TRUE;
+    ULONG http_filter_revision = HTTP_FILTER_REVISION;
+
+    pVer->dwFilterVersion = pVer->dwServerFilterVersion;
+
+    if (pVer->dwFilterVersion > http_filter_revision) {
+        pVer->dwFilterVersion = http_filter_revision;
+    }
+    if (!is_inited) {
+        rv = initialize_extension();
+    }
+    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_LOG |
+                        SF_NOTIFY_AUTH_COMPLETE;
+    }
+    else {
+        pVer->dwFlags = SF_NOTIFY_ORDER_HIGH |
+                        SF_NOTIFY_SECURE_PORT |
+                        SF_NOTIFY_NONSECURE_PORT |
+                        SF_NOTIFY_PREPROC_HEADERS;
+    }
+
+    StringCbCopy(pVer->lpszFilterDesc, SF_MAX_FILTER_DESC_LEN, VERSION_STRING);
+    return rv;
+}
+
+
+#define AP_REG_ICASE    0x01 /** use a case-insensitive match */
+#define AP_REG_NEWLINE  0x02 /** don't match newlines against '.' etc */
+#define AP_REG_NOTBOL   0x04 /** ^ will not match against start-of-string */
+#define AP_REG_NOTEOL   0x08 /** $ will not match against end-of-string */
+
+#define AP_REG_EXTENDED (0)  /** unused */
+#define AP_REG_NOSUB    (0)  /** unused */
+/** The max number of regex captures that can be expanded by ap_pregsub */
+#define AP_MAX_REG_MATCH 10
+
+/* Error values: */
+enum {
+    AP_REG_ASSERT = 1,  /** internal error ? */
+    AP_REG_ESPACE,      /** failed to get memory */
+    AP_REG_INVARG,      /** invalid argument */
+    AP_REG_NOMATCH      /** match failed */
+};
+
+/* The structure representing a compiled regular expression. */
+typedef struct {
+    void *re_pcre;
+    size_t re_nsub;
+    size_t re_erroffset;
+    const char *real;
+    const char *fake;
+} ap_regex_t;
+
+/* The structure in which a captured offset is returned. */
+typedef struct {
+    int rm_so;
+    int rm_eo;
+} ap_regmatch_t;
+
+
+/* Table of error strings corresponding to POSIX error codes; must be
+ * kept in synch with include/ap_regex.h's AP_REG_E* definitions. */
+
+static const char *const pstring[] = {
+  "",                                /* Dummy for value 0 */
+  "internal error",                  /* AP_REG_ASSERT */
+  "failed to get memory",            /* AP_REG_ESPACE */
+  "bad argument",                    /* AP_REG_INVARG */
+  "match failed"                     /* AP_REG_NOMATCH */
+};
+
+static size_t ap_regerror(int errcode, const ap_regex_t *preg,
+                          char *errbuf, size_t errbuf_size)
+{
+    const char *message, *addmessage;
+    size_t length, addlength;
+
+    message = (errcode >= (int)(sizeof(pstring)/sizeof(char *))) ?
+                                "unknown error code" : pstring[errcode];
+    length = strlen(message) + 1;
+
+    addmessage = " at offset ";
+    addlength = (preg != NULL && (int)preg->re_erroffset != -1)?
+                                        strlen(addmessage) + 6 : 0;
+
+    if (errbuf_size > 0) {
+        if (addlength > 0 && errbuf_size >= length + addlength)
+            StringCbPrintf(errbuf, sizeof(errbuf), "%s%s%-6d",
+                          message, addmessage,
+                          (int)preg->re_erroffset);
+        else {
+            strncpy(errbuf, message, errbuf_size - 1);
+            errbuf[errbuf_size-1] = 0;
+        }
+    }
+
+    return length + addlength;
+}
+
+/*************************************************
+ *           Free store held by a regex          *
+ *************************************************/
+
+static void ap_regfree(ap_regex_t *preg)
+{
+    (pcre_free)(preg->re_pcre);
+}
+
+
+
+
+/*************************************************
+ *            Compile a regular expression       *
+ *************************************************/
+
+/*
+Arguments:
+  preg        points to a structure for recording the compiled expression
+  pattern     the pattern to compile
+  cflags      compilation flags
+
+Returns:      0 on success
+              various non-zero codes on failure
+*/
+
+static int ap_regcomp(ap_regex_t *preg, const char *pattern, int cflags)
+{
+    const char *errorptr;
+    int erroffset;
+    int options = 0;
+
+    if ((cflags & AP_REG_ICASE) != 0) options |= PCRE_CASELESS;
+    if ((cflags & AP_REG_NEWLINE) != 0) options |= PCRE_MULTILINE;
+
+    preg->re_pcre = pcre_compile(pattern, options, &errorptr, &erroffset, NULL);
+    preg->re_erroffset = erroffset;
+
+    if (preg->re_pcre == NULL) return AP_REG_INVARG;
+
+    preg->re_nsub = pcre_info((const pcre *)preg->re_pcre, NULL, NULL);
+    return 0;
+}
+
+/*************************************************
+ *              Match a regular expression       *
+ *************************************************/
+
+/* Unfortunately, PCRE requires 3 ints of working space for each captured
+substring, so we have to get and release working store instead of just using
+the POSIX structures as was done in earlier releases when PCRE needed only 2
+ints. However, if the number of possible capturing brackets is small, use a
+block of store on the stack, to reduce the use of malloc/free. The threshold is
+in a macro that can be changed at configure time. */
+
+static int ap_regexec(const ap_regex_t *preg, const char *string,
+                      int nmatch, ap_regmatch_t pmatch[],
+                      int eflags)
+{
+    int rc;
+    int options = 0;
+    int *ovector = NULL;
+    int small_ovector[POSIX_MALLOC_THRESHOLD * 3];
+    int allocated_ovector = 0;
+
+    if ((eflags & AP_REG_NOTBOL) != 0) options |= PCRE_NOTBOL;
+    if ((eflags & AP_REG_NOTEOL) != 0) options |= PCRE_NOTEOL;
+
+    ((ap_regex_t *)preg)->re_erroffset = (size_t)(-1);  /* Only has meaning after compile */
+
+    if (nmatch > 0) {
+        if (nmatch <= POSIX_MALLOC_THRESHOLD) {
+            ovector = &(small_ovector[0]);
+        }
+        else {
+            ovector = (int *)malloc(sizeof(int) * nmatch * 3);
+            if (ovector == NULL)
+                return AP_REG_ESPACE;
+            allocated_ovector = 1;
+        }
+    }
+
+    rc = pcre_exec((const pcre *)preg->re_pcre, NULL, string,
+                   (int)strlen(string),
+                    0, options, ovector, nmatch * 3);
+
+    if (rc == 0)
+        rc = nmatch;    /* All captured slots were filled in */
+    if (rc >= 0) {
+        int i;
+        for (i = 0; i < rc; i++) {
+            pmatch[i].rm_so = ovector[i*2];
+            pmatch[i].rm_eo = ovector[i*2+1];
+        }
+        if (allocated_ovector)
+            free(ovector);
+        for (; i < nmatch; i++)
+            pmatch[i].rm_so = pmatch[i].rm_eo = -1;
+        return 0;
+    }
+    else {
+        if (allocated_ovector)
+            free(ovector);
+        switch(rc) {
+            case PCRE_ERROR_NOMATCH: return AP_REG_NOMATCH;
+            case PCRE_ERROR_NULL: return AP_REG_INVARG;
+            case PCRE_ERROR_BADOPTION: return AP_REG_INVARG;
+            case PCRE_ERROR_BADMAGIC: return AP_REG_INVARG;
+            case PCRE_ERROR_UNKNOWN_NODE: return AP_REG_ASSERT;
+            case PCRE_ERROR_NOMEMORY: return AP_REG_ESPACE;
+#ifdef PCRE_ERROR_MATCHLIMIT
+            case PCRE_ERROR_MATCHLIMIT: return AP_REG_ESPACE;
+#endif
+#ifdef PCRE_ERROR_BADUTF8
+            case PCRE_ERROR_BADUTF8: return AP_REG_INVARG;
+#endif
+#ifdef PCRE_ERROR_BADUTF8_OFFSET
+            case PCRE_ERROR_BADUTF8_OFFSET: return AP_REG_INVARG;
+#endif
+            default: return AP_REG_ASSERT;
+        }
+    }
+}
+
+/* This function substitutes for $0-$9, filling in regular expression
+ * submatches. Pass it the same nmatch and pmatch arguments that you
+ * passed ap_regexec(). pmatch should not be greater than the maximum number
+ * of subexpressions - i.e. one more than the re_nsub member of ap_regex_t.
+ *
+ * input should be the string with the $-expressions, source should be the
+ * string that was matched against.
+ *
+ * It returns the substituted string, or NULL on error.
+ *
+ * Parts of this code are based on Henry Spencer's regsub(), from his
+ * AT&T V8 regexp package.
+ */
+
+static char *ap_pregsub(const char *input,
+                        const char *source, size_t nmatch,
+                        ap_regmatch_t pmatch[])
+{
+    const char *src = input;
+    char *dest, *dst;
+    char c;
+    size_t no;
+    int len;
+
+    if (!source)
+        return NULL;
+    if (!nmatch)
+        return strdup(src);
+
+    /* First pass, find the size */
+    len = 0;
+
+    while ((c = *src++) != '\0') {
+        if (c == '&')
+            no = 0;
+        else if (c == '$' && isdigit((unsigned char)*src))
+            no = *src++ - '0';
+        else
+            no = 10;
+
+        if (no > 9) {                /* Ordinary character. */
+            if (c == '\\' && (*src == '$' || *src == '&'))
+                c = *src++;
+            len++;
+        }
+        else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
+            len += pmatch[no].rm_eo - pmatch[no].rm_so;
+        }
+
+    }
+
+    dest = dst = calloc(1, len + 1);
+
+    /* Now actually fill in the string */
+
+    src = input;
+
+    while ((c = *src++) != '\0') {
+        if (c == '&')
+            no = 0;
+        else if (c == '$' && isdigit((unsigned char)*src))
+            no = *src++ - '0';
+        else
+            no = 10;
+
+        if (no > 9) {                /* Ordinary character. */
+            if (c == '\\' && (*src == '$' || *src == '&'))
+                c = *src++;
+            *dst++ = c;
+        }
+        else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
+            len = pmatch[no].rm_eo - pmatch[no].rm_so;
+            memcpy(dst, source + pmatch[no].rm_so, len);
+            dst += len;
+        }
+
+    }
+    *dst = '\0';
+    return dest;
+}
+
+static int simple_rewrite(char *uri)
+{
+    if (rewrite_map) {
+        int i;
+        char buf[INTERNET_MAX_URL_LENGTH];
+        for (i = 0; i < jk_map_size(rewrite_map); i++) {
+            const char *src = jk_map_name_at(rewrite_map, i);
+            if (*src == '~')
+                continue;   /* Skip regexp rewrites */
+            if (strncmp(uri, src, strlen(src)) == 0) {
+                StringCbCopy(buf, INTERNET_MAX_URL_LENGTH, jk_map_value_at(rewrite_map, i));
+                StringCbCat(buf,  INTERNET_MAX_URL_LENGTH, uri + strlen(src));
+                StringCbCopy(uri, INTERNET_MAX_URL_LENGTH, buf);
+                return 1;
+            }
+        }
+    }
+    return 0;
+}
+
+static int rregex_rewrite(char *uri)
+{
+    ap_regmatch_t regm[AP_MAX_REG_MATCH];
+
+    if (rregexp_map) {
+        int i;
+        for (i = 0; i < jk_map_size(rregexp_map); i++) {
+            ap_regex_t *regexp = (ap_regex_t *)jk_map_value_at(rregexp_map, i);
+            if (!ap_regexec(regexp, uri, AP_MAX_REG_MATCH, regm, 0)) {
+                char *subs = ap_pregsub(regexp->fake, uri,
+                                       AP_MAX_REG_MATCH, regm);
+                if (subs) {
+                    char buf[INTERNET_MAX_URL_LENGTH];
+                    size_t diffsz = strlen(subs) - (regm[0].rm_eo - regm[0].rm_so);
+                    memcpy(&buf[0], uri, regm[0].rm_so);
+                    StringCbCopy(&buf[regm[0].rm_so], INTERNET_MAX_URL_LENGTH - regm[0].rm_so, subs);
+                    StringCbCat(&buf[0], INTERNET_MAX_URL_LENGTH, uri + regm[0].rm_eo);
+                    StringCbCopy(uri, INTERNET_MAX_URL_LENGTH, &buf[0]);
+                    free(subs);
+                    return 1;
+                }
+            }
+        }
+    }
+    return 0;
+}
+
+
+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 (auth_notification_flags == dwNotificationType) {
+        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];
+        char squery[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 (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;
+        }
+
+        if (JK_IS_DEBUG_LEVEL(logger))
+            jk_log(logger, JK_LOG_DEBUG, "Filter started");
+
+        /*
+         * 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,
+                   "error while getting the url");
+            return SF_STATUS_REQ_ERROR;
+        }
+
+        if (strlen(uri)) {
+            int rc;
+            const char *worker = NULL;
+            query = strchr(uri, '?');
+            if (query) {
+                *query++ = '\0';
+                StringCbCopy(squery, INTERNET_MAX_URL_LENGTH, query);
+            }
+
+            rc = unescape_url(uri);
+            if (rc == BAD_REQUEST) {
+                jk_log(logger, JK_LOG_ERROR,
+                       "[%s] contains one or more invalid escape sequences.",
+                       uri);
+                write_error_response(pfc, "400 Bad Request",
+                                     HTML_ERROR_400);
+                return SF_STATUS_REQ_FINISHED;
+            }
+            else if (rc == BAD_PATH) {
+                jk_log(logger, JK_LOG_EMERG,
+                       "[%s] contains forbidden escape sequences.",
+                       uri);
+                write_error_response(pfc, "404 Not Found",
+                                     HTML_ERROR_404);
+                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) {
+                StringCbCat(Host, INTERNET_MAX_URL_LENGTH, ":");
+                StringCbCat(Host, INTERNET_MAX_URL_LENGTH, Port);
+            }
+            if (szHost > 0) {
+                StringCbCat(snuri, INTERNET_MAX_URL_LENGTH, Host);
+                StringCbCat(snuri, INTERNET_MAX_URL_LENGTH, uri);
+                if (JK_IS_DEBUG_LEVEL(logger))
+                    jk_log(logger, JK_LOG_DEBUG,
+                           "Virtual Host redirection of %s",
+                           snuri);
+                worker = map_uri_to_worker(uw_map, snuri, logger);
+            }
+            if (!worker) {
+                if (JK_IS_DEBUG_LEVEL(logger))
+                    jk_log(logger, JK_LOG_DEBUG,
+                           "Default redirection of %s",
+                           uri);
+                worker = map_uri_to_worker(uw_map, uri, logger);
+            }
+            /*
+             * Check if somebody is feading us with his own TOMCAT data headers.
+             * We reject such postings !
+             */
+            if (worker) {
+                char *forwardURI;
+
+                if (JK_IS_DEBUG_LEVEL(logger))
+                    jk_log(logger, JK_LOG_DEBUG,
+                           "check if [%s] is points to the web-inf directory",
+                        uri);
+
+                if (uri_is_web_inf(uri)) {
+                    jk_log(logger, JK_LOG_EMERG,
+                           "[%s] points to the web-inf or meta-inf directory. "
+                           "Somebody try to hack into the site!!!",
+                           uri);
+
+                    write_error_response(pfc, "404 Not Found",
+                                         HTML_ERROR_404);
+                    return SF_STATUS_REQ_FINISHED;
+                }
+
+                /* This is a servlet, should redirect ... */
+                if (JK_IS_DEBUG_LEVEL(logger))
+                    jk_log(logger, JK_LOG_DEBUG,
+                        "[%s] is a servlet url - should redirect to %s",
+                        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';
+                    if (JK_IS_DEBUG_LEVEL(logger))
+                        jk_log(logger, JK_LOG_DEBUG,
+                               "fowarding original URI [%s]",
+                               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,
+                               "[%s] re-encoding request exceeds maximum buffer size.",
+                               uri);
+                        write_error_response(pfc, "400 Bad Request",
+                                             HTML_ERROR_400);
+                        return SF_STATUS_REQ_FINISHED;
+                    }
+                    if (JK_IS_DEBUG_LEVEL(logger))
+                        jk_log(logger, JK_LOG_DEBUG,
+                               "fowarding escaped URI [%s]",
+                               snuri);
+                    forwardURI = snuri;
+                }
+                else {
+                    forwardURI = uri;
+                }
+                /* Do a simple rewrite .
+                 * Note that URI can be escaped, so thus the rule has
+                 * to be in that case.
+                 *
+                 * TODO: Add more advanced regexp rewrite.
+                 */
+                if (JK_IS_DEBUG_LEVEL(logger)) {
+                    char duri[INTERNET_MAX_URL_LENGTH];
+                    StringCbCopy(duri, INTERNET_MAX_URL_LENGTH, forwardURI);
+                    if (simple_rewrite(forwardURI)) {
+                        jk_log(logger, JK_LOG_DEBUG,
+                               "rewriten URI [%s]->[%s]",
+                               duri, forwardURI);
+                    }
+                    else if (rregex_rewrite(forwardURI)) {
+                        jk_log(logger, JK_LOG_DEBUG,
+                               "rewriten URI [%s]->[%s]",
+                               duri, forwardURI);
+                    }
+                }
+                else {
+                    if (!simple_rewrite(forwardURI))
+                        rregex_rewrite(forwardURI);
+                }
+
+                if (!AddHeader(pfc, URI_HEADER_NAME, forwardURI) ||
+                    ((strlen(squery) > 0)
+                     ? !AddHeader(pfc, QUERY_HEADER_NAME, squery) : FALSE) ||
+                    !AddHeader(pfc, WORKER_HEADER_NAME, (LPSTR)worker) ||
+                    !SetHeader(pfc, "url", extension_uri)) {
+                    jk_log(logger, JK_LOG_ERROR,
+                           "error while adding request headers");
+                    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_HEADER, (LPVOID) Translate,
+                     (LPDWORD) & szTranslate) && Translate != NULL
+                    && szTranslate > 0) {
+                    if (!AddHeader
+                        (pfc, TOMCAT_TRANSLATE_HEADER_NAME, Translate)) {
+                        jk_log(logger, JK_LOG_ERROR,
+                               "error while adding Tomcat-Translate headers");
+                        return SF_STATUS_REQ_ERROR;
+                    }
+                    SetHeader(pfc, "Translate:", NULL);
+                }
+                if (!pfc->pFilterContext) {
+                    isapi_log_data_t *ld = (isapi_log_data_t *)pfc->AllocMem(pfc, sizeof(isapi_log_data_t), 0);
+                    if (!ld) {
+                        jk_log(logger, JK_LOG_ERROR,
+                               "error while allocating memory");
+                        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+                        return SF_STATUS_REQ_ERROR;
+                    }
+                    memset(ld, 0, sizeof(isapi_log_data_t));
+                    StringCbCopy(ld->uri, INTERNET_MAX_URL_LENGTH, forwardURI);
+                    StringCbCopy(ld->query, INTERNET_MAX_URL_LENGTH, squery);
+                    pfc->pFilterContext = ld;
+                } else {
+                    isapi_log_data_t *ld = (isapi_log_data_t *)pfc->pFilterContext;
+                    memset(ld, 0, sizeof(isapi_log_data_t));
+                    StringCbCopy(ld->uri, INTERNET_MAX_URL_LENGTH, forwardURI);
+                    StringCbCopy(ld->query, INTERNET_MAX_URL_LENGTH, squery);
+                }
+            }
+            else {
+                if (JK_IS_DEBUG_LEVEL(logger))
+                    jk_log(logger, JK_LOG_DEBUG,
+                           "[%s] is not a servlet url", uri);
+                if (strip_session) {
+                    char *jsessionid = strstr(uri, JK_PATH_SESSION_IDENTIFIER);
+                    if (jsessionid) {
+                        if (JK_IS_DEBUG_LEVEL(logger))
+                            jk_log(logger, JK_LOG_DEBUG,
+                                   "removing session identifier [%s] for non servlet url [%s]",
+                                   jsessionid, uri);
+                        *jsessionid = '\0';
+                        SetHeader(pfc, "url", uri);
+                    }
+                }
+            }
+        }
+    }
+    else if (is_inited && (dwNotificationType == SF_NOTIFY_LOG)) {
+        if (pfc->pFilterContext) {
+            isapi_log_data_t *ld = (isapi_log_data_t *)pfc->pFilterContext;
+            HTTP_FILTER_LOG  *pl = (HTTP_FILTER_LOG *)pvNotification;
+            pl->pszTarget = ld->uri;
+            pl->pszParameters = ld->query;
+        }
+    }
+    return SF_STATUS_REQ_NEXT_NOTIFICATION;
+}
+
+
+BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO * pVer)
+{
+    pVer->dwExtensionVersion = MAKELONG(HSE_VERSION_MINOR, HSE_VERSION_MAJOR);
+
+    StringCbCopy(pVer->lpszExtensionDesc, HSE_MAX_EXT_DLL_NAME_LEN, 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_TRACE_ENTER(logger);
+
+    /* Initialise jk */
+    if (is_inited && !is_mapread) {
+        char serverName[MAX_SERVERNAME] = { 0 };
+        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;
+
+        wc_maintain(logger);
+        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);
+
+            if (JK_IS_DEBUG_LEVEL(logger))
+                jk_log(logger, JK_LOG_DEBUG,
+                       "%s a worker for name %s",
+                       worker ? "got" : "could not get", worker_name);
+
+            if (worker) {
+                jk_endpoint_t *e = NULL;
+                /* Update retries for this worker */
+                s.retries = worker->retries;
+                if (worker->get_endpoint(worker, &e, logger)) {
+                    int is_error = JK_HTTP_SERVER_ERROR;
+                    if (e->service(e, &s, logger, &is_error)) {
+                        rc = HSE_STATUS_SUCCESS;
+                        lpEcb->dwHttpStatusCode = HTTP_STATUS_OK;
+                        if (JK_IS_DEBUG_LEVEL(logger))
+                            jk_log(logger, JK_LOG_DEBUG,
+                                   "service() returned OK");
+                    }
+                    else {
+                        jk_log(logger, JK_LOG_ERROR,
+                               "service() failed with http error %d", is_error);
+                        lpEcb->dwHttpStatusCode = is_error;
+                        write_error_message(lpEcb, is_error);
+                    }
+                    e->done(&e, logger);
+                }
+                else {
+                    jk_log(logger, JK_LOG_ERROR,
+                        "Failed to obtain an endpoint to service request - "
+                        "your connection_pool_size is probably less than the threads in your web server!");
+                }
+            }
+            else {
+                jk_log(logger, JK_LOG_ERROR,
+                       "could not get a worker for name %s",
+                       worker_name);
+            }
+        }
+        else {
+            jk_log(logger, JK_LOG_ERROR,
+                "failed to init service for request.");
+         }
+        jk_close_pool(&private_data.p);
+    }
+    else {
+        jk_log(logger, JK_LOG_ERROR,
+               "not initialized");
+    }
+
+    JK_TRACE_EXIT(logger);
+    return rc;
+}
+
+
+
+BOOL WINAPI TerminateExtension(DWORD dwFlags)
+{
+    return TerminateFilter(dwFlags);
+}
+
+BOOL WINAPI TerminateFilter(DWORD dwFlags)
+{
+    UNREFERENCED_PARAMETER(dwFlags);
+
+    if (is_inited) {
+        is_inited = JK_FALSE;
+
+        if (is_mapread) {
+            uri_worker_map_free(&uw_map, logger);
+            is_mapread = JK_FALSE;
+        }
+        if (workers_map) {
+            jk_map_free(&workers_map);
+        }
+        if (rewrite_map) {
+            jk_map_free(&rewrite_map);
+        }
+        if (rregexp_map) {
+            int i;
+            for (i = 0; i < jk_map_size(rregexp_map); i++) {
+                ap_regex_t *regexp = (ap_regex_t *)jk_map_value_at(rregexp_map, i);
+                if (regexp) {
+                    ap_regfree(regexp);
+                    free(regexp);
+                }
+            }
+            jk_map_free(&rregexp_map);
+        }
+        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_PATH];
+    char file_name[MAX_PATH];
+
+    UNREFERENCED_PARAMETER(lpReserved);
+
+    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;
+        }
+        /* Construct redirector headers to use for this redirector instance */
+        StringCbPrintf(URI_HEADER_NAME, MAX_PATH, HEADER_TEMPLATE, URI_HEADER_NAME_BASE, hInst);
+        StringCbPrintf(QUERY_HEADER_NAME, MAX_PATH, HEADER_TEMPLATE, QUERY_HEADER_NAME_BASE, hInst);
+        StringCbPrintf(WORKER_HEADER_NAME, MAX_PATH, HEADER_TEMPLATE, WORKER_HEADER_NAME_BASE, hInst);
+        StringCbPrintf(TOMCAT_TRANSLATE_HEADER_NAME, MAX_PATH, HEADER_TEMPLATE, TOMCAT_TRANSLATE_HEADER_NAME_BASE, hInst);
+
+        StringCbPrintf(HTTP_URI_HEADER_NAME, MAX_PATH, HTTP_HEADER_TEMPLATE, URI_HEADER_NAME_BASE, hInst);
+        StringCbPrintf(HTTP_QUERY_HEADER_NAME, MAX_PATH, HTTP_HEADER_TEMPLATE, QUERY_HEADER_NAME_BASE, hInst);
+        StringCbPrintf(HTTP_WORKER_HEADER_NAME, MAX_PATH, HTTP_HEADER_TEMPLATE, WORKER_HEADER_NAME_BASE, hInst);
+
+    break;
+    case DLL_PROCESS_DETACH:
+        __try {
+            TerminateFilter(HSE_TERM_MUST_UNLOAD);
+        }
+        __except(1) {
+        }
+        break;
+
+    default:
+        break;
+    }
+
+    return fReturn;
+}
+
+static int init_jk(char *serverName)
+{
+    char shm_name[MAX_PATH];
+    int rc = JK_FALSE;
+
+    if (!jk_open_file_logger(&logger, log_file, log_level)) {
+        logger = NULL;
+    }
+    StringCbCopy(shm_name, MAX_PATH, SHM_DEF_NAME);
+    if (*serverName) {
+        size_t i;
+        StringCbCat(shm_name, MAX_PATH, "_");
+        StringCbCat(shm_name, MAX_PATH, serverName);
+        for(i = 0; i < strlen(shm_name); i++) {
+            shm_name[i] = toupper(shm_name[i]);
+            if (!isalnum(shm_name[i]))
+                shm_name[i] = '_';
+        }
+    }
+    /*
+     * Create named shared memory for each server
+     */
+    jk_shm_open(shm_name, shm_config_size, logger);
+
+    jk_set_worker_def_cache_size(DEFAULT_WORKER_THREADS);
+
+    /* Logging the initialization type: registry or properties file in virtual dir
+     */
+    if (JK_IS_DEBUG_LEVEL(logger)) {
+        if (using_ini_file) {
+            jk_log(logger, JK_LOG_DEBUG, "Using ini file %s.", ini_file_name);
+        }
+        else {
+            jk_log(logger, JK_LOG_DEBUG, "Using registry.");
+        }
+
+        jk_log(logger, JK_LOG_DEBUG, "Using log file %s.", log_file);
+        jk_log(logger, JK_LOG_DEBUG, "Using log level %d.", log_level);
+        jk_log(logger, JK_LOG_DEBUG, "Using extension uri %s.", extension_uri);
+        jk_log(logger, JK_LOG_DEBUG, "Using worker file %s.", worker_file);
+        jk_log(logger, JK_LOG_DEBUG, "Using worker mount file %s.",
+               worker_mount_file);
+        jk_log(logger, JK_LOG_DEBUG, "Using rewrite rule file %s.",
+               rewrite_rule_file);
+        jk_log(logger, JK_LOG_DEBUG, "Using uri select %d.", uri_select_option);
+    }
+
+    if (rewrite_rule_file[0] && jk_map_alloc(&rewrite_map)) {
+        if (jk_map_load_properties(rewrite_map, rewrite_rule_file, NULL, logger)) {
+            int i;
+            if (JK_IS_DEBUG_LEVEL(logger)) {
+                jk_log(logger, JK_LOG_DEBUG, "Loaded rewrite rule file %s.",
+                       rewrite_rule_file);
+
+            }
+            jk_map_alloc(&rregexp_map);
+            for (i = 0; i < jk_map_size(rewrite_map); i++) {
+                const char *src = jk_map_name_at(rewrite_map, i);
+                if (*src == '~') {
+                    ap_regex_t *regexp = malloc(sizeof(ap_regex_t));
+                    const char *val = jk_map_value_at(rewrite_map, i);
+                    /* Skip leading tilde */
+                    regexp->real = src + 1;
+                    regexp->fake = val;
+                    if (!ap_regcomp(regexp, regexp->real, AP_REG_EXTENDED)) {
+                        jk_map_add(rregexp_map, regexp->real, regexp);
+                        if (JK_IS_DEBUG_LEVEL(logger)) {
+                            jk_log(logger, JK_LOG_DEBUG,
+                                   "Added regular expression rule %s -> %s",
+                                   regexp->real, regexp->fake);
+                        }
+                    }
+                    else {
+                        jk_log(logger, JK_LOG_ERROR,
+                               "Unable to compile regular expression %s",
+                               regexp->real);
+                        free(regexp);
+                    }
+                }
+            }
+        }
+        else {
+            jk_map_free(&rewrite_map);
+            rewrite_map = NULL;
+        }
+    }
+
+    if (uri_worker_map_alloc(&uw_map, NULL, logger)) {
+        rc = JK_FALSE;
+        uw_map->fname = worker_mount_file;
+        uw_map->reload = worker_mount_reload;
+        if (worker_mount_file[0])
+            rc = uri_worker_map_load(uw_map, logger);
+    }
+    if (rc) {
+        rc = JK_FALSE;
+        if (jk_map_alloc(&workers_map)) {
+            if (jk_map_read_properties(workers_map, worker_file, NULL, 1, logger)) {
+                /* we add the URI->WORKER MAP since workers using AJP14 will feed it */
+
+                if (jk_map_resolve_references(workers_map, "worker.", 1, 1, logger) == JK_FALSE) {
+                    jk_log(logger, JK_LOG_ERROR, "Error in resolving configuration references");
+                }
+
+                worker_env.uri_to_worker = uw_map;
+                worker_env.server_name = serverName;
+
+                if (wc_open(workers_map, &worker_env, logger)) {
+                    rc = JK_TRUE;
+                }
+            }
+            else {
+                jk_log(logger, JK_LOG_EMERG,
+                       "Unable to read worker file %s.", worker_file);
+            }
+            if (rc != JK_TRUE) {
+                jk_map_free(&workers_map);
+                workers_map = NULL;
+            }
+        }
+    }
+    if (rc) {
+        jk_log(logger, JK_LOG_INFO,
+               "isapi_redirect/%s initialized",
+               JK_VERSTRING);
+    }
+    return rc;
+}
+
+static int initialize_extension(void)
+{
+
+    if (read_registry_init_data()) {
+        auth_notification_flags = get_auth_flags();
+        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[MAX_PATH];
+    int ok = JK_TRUE;
+    LPVOID src;
+    HKEY hkey;
+    jk_map_t *map = NULL;
+
+    if (jk_map_alloc(&map)) {
+        if (jk_map_read_properties(map, ini_file_name, NULL, 1, logger)) {
+            using_ini_file = JK_TRUE;
+            src = map;
+        }
+        else {
+            jk_map_free(&map);
+        }
+    }
+    if (!using_ini_file) {
+        long rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGISTRY_LOCATION,
+                               (DWORD)0, KEY_READ, &hkey);
+        if (ERROR_SUCCESS != rc) {
+            return JK_FALSE;
+        }
+        else {
+            src = &hkey;
+        }
+    }
+    ok = ok && get_config_parameter(src, JK_LOG_FILE_TAG, log_file, sizeof(log_file));
+    if (get_config_parameter(src, JK_LOG_LEVEL_TAG, tmpbuf, sizeof(tmpbuf))) {
+        log_level = jk_parse_log_level(tmpbuf);
+    }
+    ok = ok && get_config_parameter(src, EXTENSION_URI_TAG, extension_uri, sizeof(extension_uri));
+    ok = ok && get_config_parameter(src, JK_WORKER_FILE_TAG, worker_file, sizeof(worker_file));
+    ok = ok && get_config_parameter(src, JK_MOUNT_FILE_TAG, worker_mount_file, sizeof(worker_mount_file));
+    get_config_parameter(src, URI_REWRITE_TAG, rewrite_rule_file, sizeof(rewrite_rule_file));
+    if (get_config_parameter(src, URI_SELECT_TAG, tmpbuf, sizeof(tmpbuf))) {
+        int opt = parse_uri_select(tmpbuf);
+        if (opt >= 0) {
+            uri_select_option = opt;
+        }
+        else {
+            ok = JK_FALSE;
+        }
+    }
+    shm_config_size = get_config_int(src, SHM_SIZE_TAG, JK_SHM_DEF_SIZE);
+    worker_mount_reload = get_config_int(src, WORKER_MOUNT_RELOAD_TAG, JK_URIMAP_DEF_RELOAD);
+    strip_session = get_config_bool(src, STRIP_SESSION_TAG, JK_FALSE);
+    use_auth_notification_flags = get_config_int(src, AUTH_COMPLETE_TAG, 1);
+    if (using_ini_file) {
+        jk_map_free(&map);
+    }
+    else {
+        RegCloseKey(hkey);
+    }
+    return ok;
+}
+
+static int get_config_parameter(LPVOID src, const char *tag,
+                                char *val, DWORD sz)
+{
+    const char *tmp = NULL;
+    if (using_ini_file) {
+        tmp = jk_map_get_string((jk_map_t*)src, tag, NULL);
+        if (tmp && (strlen(tmp) < sz)) {
+            StringCbCopy(val, sz, tmp);
+            return JK_TRUE;
+        }
+        else {
+            return JK_FALSE;
+        }
+    } else {
+        return get_registry_config_parameter(*((HKEY*)src), tag, val, sz);
+    }
+}
+
+static int get_config_int(LPVOID src, const char *tag, int def)
+{
+    if (using_ini_file) {
+        return jk_map_get_int((jk_map_t*)src, tag, def);
+    } else {
+        int val;
+        if (get_registry_config_number(*((HKEY*)src), tag, &val) ) {
+            return val;
+        }
+        else {
+            return def;
+        }
+    }
+}
+
+static int get_config_bool(LPVOID src, const char *tag, int def)
+{
+    if (using_ini_file) {
+        return jk_map_get_bool((jk_map_t*)src, tag, def);
+    } else {
+        char tmpbuf[128];
+        if (get_registry_config_parameter(*((HKEY*)src), tag,
+                                          tmpbuf, sizeof(tmpbuf))) {
+            return jk_get_bool_code(tmpbuf, def);
+        }
+        else {
+            return def;
+        }
+    }
+}
+
+static int get_registry_config_parameter(HKEY hkey,
+                                         const char *tag, char *b, DWORD sz)
+{
+    DWORD type = 0;
+    LONG lrc;
+
+    sz = sz - 1; /* Reserve space for RegQueryValueEx to add null terminator */
+    b[sz] = '\0'; /* Null terminate in case RegQueryValueEx doesn't */
+
+    lrc = RegQueryValueEx(hkey, tag, (LPDWORD) 0, &type, (LPBYTE) b, &sz);
+    if ((ERROR_SUCCESS != lrc) || (type != REG_SZ)) {
+        return JK_FALSE;
+    }
+
+    return JK_TRUE;
+}
+
+static int get_registry_config_number(HKEY hkey,
+                                      const char *tag, int *val)
+{
+    DWORD type = 0;
+    DWORD data = 0;
+    DWORD sz   = sizeof(DWORD);
+    LONG lrc;
+
+    lrc = RegQueryValueEx(hkey, tag, (LPDWORD)0, &type, (LPBYTE)&data, &sz);
+    if ((ERROR_SUCCESS != lrc) || (type != REG_DWORD)) {
+        return JK_FALSE;
+    }
+
+    *val = data;
+
+    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->route = NULL;
+
+    s->start_response = start_response;
+    s->read = read;
+    s->write = write;
+
+    /* Yes we do want to reuse AJP connections */
+    s->disable_reuse = JK_FALSE;
+
+    s->flush = NULL;
+    s->flush_packets = JK_FALSE;
+    s->flush_header = JK_FALSE;
+
+    /* 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;
+    s->uw_map = uw_map;
+    /*
+     * 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 int i;
+        unsigned int 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 int 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;
+                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",
+                           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 int 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 int i;
+            size_t len_of_http_prefix = strlen("HTTP_");
+            BOOL need_content_length_header = (s->content_length == 0);
+
+            cnt -= 2;           /* For our two special headers:
+                                 * HTTP_TOMCATURI_XXXXXXXX
+                                 * HTTP_TOMCATWORKER_XXXXXXXX
+                                 */
+            /* 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 (!strnicmp(tmp, QUERY_HEADER_NAME,
+                                   strlen(QUERY_HEADER_NAME))) {
+                    /* HTTP_TOMCATQUERY_XXXXXXXX was supplied,
+                     * remove it from the count and skip
+                     */
+                    cnt--;
+                    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))) {
+                    s->headers_names[i] = TRANSLATE_HEADER_NAME_LC;
+                }
+                else {
+                    s->headers_names[i] = tmp;
+                }
+
+                while (':' != *tmp && *tmp) {
+                    if ('_' == *tmp) {
+                        *tmp = '-';
+                    }
+                    else {
+                        *tmp = JK_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)
+{
+    DWORD sz = bufsz;
+    buf[0]   = '\0';
+    if (!lpEcb->GetServerVariable(lpEcb->ConnID, name,
+                                  buf, (LPDWORD) &sz))
+        return JK_FALSE;
+
+    if (sz <= bufsz)
+        buf[sz-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 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 (int)(p - encoded);
+}
+
+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/native/iis/pcre/AUTHORS b/connectors/jk/native/iis/pcre/AUTHORS
new file mode 100644
index 0000000..00bd1d0
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/AUTHORS
@@ -0,0 +1,6 @@
+Written by: Philip Hazel <ph10@cam.ac.uk>
+
+University of Cambridge Computing Service,
+Cambridge, England. Phone: +44 1223 334714.
+
+Copyright (c) 1997-2004 University of Cambridge
diff --git a/connectors/jk/native/iis/pcre/COPYING b/connectors/jk/native/iis/pcre/COPYING
new file mode 100644
index 0000000..1573583
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/COPYING
@@ -0,0 +1,45 @@
+PCRE LICENCE
+------------
+
+PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+Release 5 of PCRE is distributed under the terms of the "BSD" licence, as
+specified below. The documentation for PCRE, supplied in the "doc"
+directory, is distributed under the same terms as the software itself.
+
+Written by: Philip Hazel <ph10@cam.ac.uk>
+
+University of Cambridge Computing Service,
+Cambridge, England. Phone: +44 1223 334714.
+
+Copyright (c) 1997-2004 University of Cambridge
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+    * Neither the name of the University of Cambridge nor the names of its
+      contributors may be used to endorse or promote products derived from
+      this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+End
diff --git a/connectors/jk/native/iis/pcre/ChangeLog b/connectors/jk/native/iis/pcre/ChangeLog
new file mode 100644
index 0000000..266cb18
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/ChangeLog
@@ -0,0 +1,1650 @@
+ChangeLog for PCRE
+------------------
+
+Version 5.0 13-Sep-04
+---------------------
+
+ 1. Internal change: literal characters are no longer packed up into items
+    containing multiple characters in a single byte-string. Each character
+    is now matched using a separate opcode. However, there may be more than one
+    byte in the character in UTF-8 mode.
+
+ 2. The pcre_callout_block structure has two new fields: pattern_position and
+    next_item_length. These contain the offset in the pattern to the next match
+    item, and its length, respectively.
+
+ 3. The PCRE_AUTO_CALLOUT option for pcre_compile() requests the automatic
+    insertion of callouts before each pattern item. Added the /C option to
+    pcretest to make use of this.
+
+ 4. On the advice of a Windows user, the lines
+
+      #if defined(_WIN32) || defined(WIN32)
+      _setmode( _fileno( stdout ), 0x8000 );
+      #endif  /* defined(_WIN32) || defined(WIN32) */
+
+    have been added to the source of pcretest. This apparently does useful
+    magic in relation to line terminators.
+
+ 5. Changed "r" and "w" in the calls to fopen() in pcretest to "rb" and "wb"
+    for the benefit of those environments where the "b" makes a difference.
+
+ 6. The icc compiler has the same options as gcc, but "configure" doesn't seem
+    to know about it. I have put a hack into configure.in that adds in code
+    to set GCC=yes if CC=icc. This seems to end up at a point in the
+    generated configure script that is early enough to affect the setting of
+    compiler options, which is what is needed, but I have no means of testing
+    whether it really works. (The user who reported this had patched the
+    generated configure script, which of course I cannot do.)
+
+    LATER: After change 22 below (new libtool files), the configure script
+    seems to know about icc (and also ecc). Therefore, I have commented out
+    this hack in configure.in.
+
+ 7. Added support for pkg-config (2 patches were sent in).
+
+ 8. Negated POSIX character classes that used a combination of internal tables
+    were completely broken. These were [[:^alpha:]], [[:^alnum:]], and
+    [[:^ascii]]. Typically, they would match almost any characters. The other
+    POSIX classes were not broken in this way.
+
+ 9. Matching the pattern "\b.*?" against "ab cd", starting at offset 1, failed
+    to find the match, as PCRE was deluded into thinking that the match had to
+    start at the start point or following a newline. The same bug applied to
+    patterns with negative forward assertions or any backward assertions
+    preceding ".*" at the start, unless the pattern required a fixed first
+    character. This was a failing pattern: "(?!.bcd).*". The bug is now fixed.
+
+10. In UTF-8 mode, when moving forwards in the subject after a failed match
+    starting at the last subject character, bytes beyond the end of the subject
+    string were read.
+
+11. Renamed the variable "class" as "classbits" to make life easier for C++
+    users. (Previously there was a macro definition, but it apparently wasn't
+    enough.)
+
+12. Added the new field "tables" to the extra data so that tables can be passed
+    in at exec time, or the internal tables can be re-selected. This allows
+    a compiled regex to be saved and re-used at a later time by a different
+    program that might have everything at different addresses.
+
+13. Modified the pcre-config script so that, when run on Solaris, it shows a
+    -R library as well as a -L library.
+
+14. The debugging options of pcretest (-d on the command line or D on a
+    pattern) showed incorrect output for anything following an extended class
+    that contained multibyte characters and which was followed by a quantifier.
+
+15. Added optional support for general category Unicode character properties
+    via the \p, \P, and \X escapes. Unicode property support implies UTF-8
+    support. It adds about 90K to the size of the library. The meanings of the
+    inbuilt class escapes such as \d and \s have NOT been changed.
+
+16. Updated pcredemo.c to include calls to free() to release the memory for the
+    compiled pattern.
+
+17. The generated file chartables.c was being created in the source directory
+    instead of in the building directory. This caused the build to fail if the
+    source directory was different from the building directory, and was
+    read-only.
+
+18. Added some sample Win commands from Mark Tetrode into the NON-UNIX-USE
+    file. No doubt somebody will tell me if they don't make sense... Also added
+    Dan Mooney's comments about building on OpenVMS.
+
+19. Added support for partial matching via the PCRE_PARTIAL option for
+    pcre_exec() and the \P data escape in pcretest.
+
+20. Extended pcretest with 3 new pattern features:
+
+    (i)   A pattern option of the form ">rest-of-line" causes pcretest to
+          write the compiled pattern to the file whose name is "rest-of-line".
+          This is a straight binary dump of the data, with the saved pointer to
+          the character tables forced to be NULL. The study data, if any, is
+          written too. After writing, pcretest reads a new pattern.
+
+    (ii)  If, instead of a pattern, "<rest-of-line" is given, pcretest reads a
+          compiled pattern from the given file. There must not be any
+          occurrences of "<" in the file name (pretty unlikely); if there are,
+          pcretest will instead treat the initial "<" as a pattern delimiter.
+          After reading in the pattern, pcretest goes on to read data lines as
+          usual.
+
+    (iii) The F pattern option causes pcretest to flip the bytes in the 32-bit
+          and 16-bit fields in a compiled pattern, to simulate a pattern that
+          was compiled on a host of opposite endianness.
+
+21. The pcre-exec() function can now cope with patterns that were compiled on
+    hosts of opposite endianness, with this restriction:
+
+      As for any compiled expression that is saved and used later, the tables
+      pointer field cannot be preserved; the extra_data field in the arguments
+      to pcre_exec() should be used to pass in a tables address if a value
+      other than the default internal tables were used at compile time.
+
+22. Calling pcre_exec() with a negative value of the "ovecsize" parameter is
+    now diagnosed as an error. Previously, most of the time, a negative number
+    would have been treated as zero, but if in addition "ovector" was passed as
+    NULL, a crash could occur.
+
+23. Updated the files ltmain.sh, config.sub, config.guess, and aclocal.m4 with
+    new versions from the libtool 1.5 distribution (the last one is a copy of
+    a file called libtool.m4). This seems to have fixed the need to patch
+    "configure" to support Darwin 1.3 (which I used to do). However, I still
+    had to patch ltmain.sh to ensure that ${SED} is set (it isn't on my
+    workstation).
+
+24. Changed the PCRE licence to be the more standard "BSD" licence.
+
+
+Version 4.5 01-Dec-03
+---------------------
+
+ 1. There has been some re-arrangement of the code for the match() function so
+    that it can be compiled in a version that does not call itself recursively.
+    Instead, it keeps those local variables that need separate instances for
+    each "recursion" in a frame on the heap, and gets/frees frames whenever it
+    needs to "recurse". Keeping track of where control must go is done by means
+    of setjmp/longjmp. The whole thing is implemented by a set of macros that
+    hide most of the details from the main code, and operates only if
+    NO_RECURSE is defined while compiling pcre.c. If PCRE is built using the
+    "configure" mechanism, "--disable-stack-for-recursion" turns on this way of
+    operating.
+
+    To make it easier for callers to provide specially tailored get/free
+    functions for this usage, two new functions, pcre_stack_malloc, and
+    pcre_stack_free, are used. They are always called in strict stacking order,
+    and the size of block requested is always the same.
+
+    The PCRE_CONFIG_STACKRECURSE info parameter can be used to find out whether
+    PCRE has been compiled to use the stack or the heap for recursion. The
+    -C option of pcretest uses this to show which version is compiled.
+
+    A new data escape \S, is added to pcretest; it causes the amounts of store
+    obtained and freed by both kinds of malloc/free at match time to be added
+    to the output.
+
+ 2. Changed the locale test to use "fr_FR" instead of "fr" because that's
+    what's available on my current Linux desktop machine.
+
+ 3. When matching a UTF-8 string, the test for a valid string at the start has
+    been extended. If start_offset is not zero, PCRE now checks that it points
+    to a byte that is the start of a UTF-8 character. If not, it returns
+    PCRE_ERROR_BADUTF8_OFFSET (-11). Note: the whole string is still checked;
+    this is necessary because there may be backward assertions in the pattern.
+    When matching the same subject several times, it may save resources to use
+    PCRE_NO_UTF8_CHECK on all but the first call if the string is long.
+
+ 4. The code for checking the validity of UTF-8 strings has been tightened so
+    that it rejects (a) strings containing 0xfe or 0xff bytes and (b) strings
+    containing "overlong sequences".
+
+ 5. Fixed a bug (appearing twice) that I could not find any way of exploiting!
+    I had written "if ((digitab[*p++] && chtab_digit) == 0)" where the "&&"
+    should have been "&", but it just so happened that all the cases this let
+    through by mistake were picked up later in the function.
+
+ 6. I had used a variable called "isblank" - this is a C99 function, causing
+    some compilers to warn. To avoid this, I renamed it (as "blankclass").
+
+ 7. Cosmetic: (a) only output another newline at the end of pcretest if it is
+    prompting; (b) run "./pcretest /dev/null" at the start of the test script
+    so the version is shown; (c) stop "make test" echoing "./RunTest".
+
+ 8. Added patches from David Burgess to enable PCRE to run on EBCDIC systems.
+
+ 9. The prototype for memmove() for systems that don't have it was using
+    size_t, but the inclusion of the header that defines size_t was later. I've
+    moved the #includes for the C headers earlier to avoid this.
+
+10. Added some adjustments to the code to make it easier to compiler on certain
+    special systems:
+
+      (a) Some "const" qualifiers were missing.
+      (b) Added the macro EXPORT before all exported functions; by default this
+          is defined to be empty.
+      (c) Changed the dftables auxiliary program (that builds chartables.c) so
+          that it reads its output file name as an argument instead of writing
+          to the standard output and assuming this can be redirected.
+
+11. In UTF-8 mode, if a recursive reference (e.g. (?1)) followed a character
+    class containing characters with values greater than 255, PCRE compilation
+    went into a loop.
+
+12. A recursive reference to a subpattern that was within another subpattern
+    that had a minimum quantifier of zero caused PCRE to crash. For example,
+    (x(y(?2))z)? provoked this bug with a subject that got as far as the
+    recursion. If the recursively-called subpattern itself had a zero repeat,
+    that was OK.
+
+13. In pcretest, the buffer for reading a data line was set at 30K, but the
+    buffer into which it was copied (for escape processing) was still set at
+    1024, so long lines caused crashes.
+
+14. A pattern such as /[ab]{1,3}+/ failed to compile, giving the error
+    "internal error: code overflow...". This applied to any character class
+    that was followed by a possessive quantifier.
+
+15. Modified the Makefile to add libpcre.la as a prerequisite for
+    libpcreposix.la because I was told this is needed for a parallel build to
+    work.
+
+16. If a pattern that contained .* following optional items at the start was
+    studied, the wrong optimizing data was generated, leading to matching
+    errors. For example, studying /[ab]*.*c/ concluded, erroneously, that any
+    matching string must start with a or b or c. The correct conclusion for
+    this pattern is that a match can start with any character.
+
+
+Version 4.4 13-Aug-03
+---------------------
+
+ 1. In UTF-8 mode, a character class containing characters with values between
+    127 and 255 was not handled correctly if the compiled pattern was studied.
+    In fixing this, I have also improved the studying algorithm for such
+    classes (slightly).
+
+ 2. Three internal functions had redundant arguments passed to them. Removal
+    might give a very teeny performance improvement.
+
+ 3. Documentation bug: the value of the capture_top field in a callout is *one
+    more than* the number of the hightest numbered captured substring.
+
+ 4. The Makefile linked pcretest and pcregrep with -lpcre, which could result
+    in incorrectly linking with a previously installed version. They now link
+    explicitly with libpcre.la.
+
+ 5. configure.in no longer needs to recognize Cygwin specially.
+
+ 6. A problem in pcre.in for Windows platforms is fixed.
+
+ 7. If a pattern was successfully studied, and the -d (or /D) flag was given to
+    pcretest, it used to include the size of the study block as part of its
+    output. Unfortunately, the structure contains a field that has a different
+    size on different hardware architectures. This meant that the tests that
+    showed this size failed. As the block is currently always of a fixed size,
+    this information isn't actually particularly useful in pcretest output, so
+    I have just removed it.
+
+ 8. Three pre-processor statements accidentally did not start in column 1.
+    Sadly, there are *still* compilers around that complain, even though
+    standard C has not required this for well over a decade. Sigh.
+
+ 9. In pcretest, the code for checking callouts passed small integers in the
+    callout_data field, which is a void * field. However, some picky compilers
+    complained about the casts involved for this on 64-bit systems. Now
+    pcretest passes the address of the small integer instead, which should get
+    rid of the warnings.
+
+10. By default, when in UTF-8 mode, PCRE now checks for valid UTF-8 strings at
+    both compile and run time, and gives an error if an invalid UTF-8 sequence
+    is found. There is a option for disabling this check in cases where the
+    string is known to be correct and/or the maximum performance is wanted.
+
+11. In response to a bug report, I changed one line in Makefile.in from
+
+        -Wl,--out-implib,.libs/lib@WIN_PREFIX@pcreposix.dll.a \
+    to
+        -Wl,--out-implib,.libs/@WIN_PREFIX@libpcreposix.dll.a \
+
+    to look similar to other lines, but I have no way of telling whether this
+    is the right thing to do, as I do not use Windows. No doubt I'll get told
+    if it's wrong...
+
+
+Version 4.3 21-May-03
+---------------------
+
+1. Two instances of @WIN_PREFIX@ omitted from the Windows targets in the
+   Makefile.
+
+2. Some refactoring to improve the quality of the code:
+
+   (i)   The utf8_table... variables are now declared "const".
+
+   (ii)  The code for \cx, which used the "case flipping" table to upper case
+         lower case letters, now just substracts 32. This is ASCII-specific,
+         but the whole concept of \cx is ASCII-specific, so it seems
+         reasonable.
+
+   (iii) PCRE was using its character types table to recognize decimal and
+         hexadecimal digits in the pattern. This is silly, because it handles
+         only 0-9, a-f, and A-F, but the character types table is locale-
+         specific, which means strange things might happen. A private
+         table is now used for this - though it costs 256 bytes, a table is
+         much faster than multiple explicit tests. Of course, the standard
+         character types table is still used for matching digits in subject
+         strings against \d.
+
+   (iv)  Strictly, the identifier ESC_t is reserved by POSIX (all identifiers
+         ending in _t are). So I've renamed it as ESC_tee.
+
+3. The first argument for regexec() in the POSIX wrapper should have been
+   defined as "const".
+
+4. Changed pcretest to use malloc() for its buffers so that they can be
+   Electric Fenced for debugging.
+
+5. There were several places in the code where, in UTF-8 mode, PCRE would try
+   to read one or more bytes before the start of the subject string. Often this
+   had no effect on PCRE's behaviour, but in some circumstances it could
+   provoke a segmentation fault.
+
+6. A lookbehind at the start of a pattern in UTF-8 mode could also cause PCRE
+   to try to read one or more bytes before the start of the subject string.
+
+7. A lookbehind in a pattern matched in non-UTF-8 mode on a PCRE compiled with
+   UTF-8 support could misbehave in various ways if the subject string
+   contained bytes with the 0x80 bit set and the 0x40 bit unset in a lookbehind
+   area. (PCRE was not checking for the UTF-8 mode flag, and trying to move
+   back over UTF-8 characters.)
+
+
+Version 4.2 14-Apr-03
+---------------------
+
+1. Typo "#if SUPPORT_UTF8" instead of "#ifdef SUPPORT_UTF8" fixed.
+
+2. Changes to the building process, supplied by Ronald Landheer-Cieslak
+     [ON_WINDOWS]: new variable, "#" on non-Windows platforms
+     [NOT_ON_WINDOWS]: new variable, "#" on Windows platforms
+     [WIN_PREFIX]: new variable, "cyg" for Cygwin
+     * Makefile.in: use autoconf substitution for OBJEXT, EXEEXT, BUILD_OBJEXT
+       and BUILD_EXEEXT
+     Note: automatic setting of the BUILD variables is not yet working
+     set CPPFLAGS and BUILD_CPPFLAGS (but don't use yet) - should be used at
+       compile-time but not at link-time
+     [LINK]: use for linking executables only
+     make different versions for Windows and non-Windows
+     [LINKLIB]: new variable, copy of UNIX-style LINK, used for linking
+       libraries
+     [LINK_FOR_BUILD]: new variable
+     [OBJEXT]: use throughout
+     [EXEEXT]: use throughout
+     <winshared>: new target
+     <wininstall>: new target
+     <dftables.o>: use native compiler
+     <dftables>: use native linker
+     <install>: handle Windows platform correctly
+     <clean>: ditto
+     <check>: ditto
+     copy DLL to top builddir before testing
+
+   As part of these changes, -no-undefined was removed again. This was reported
+   to give trouble on HP-UX 11.0, so getting rid of it seems like a good idea
+   in any case.
+
+3. Some tidies to get rid of compiler warnings:
+
+   . In the match_data structure, match_limit was an unsigned long int, whereas
+     match_call_count was an int. I've made them both unsigned long ints.
+
+   . In pcretest the fact that a const uschar * doesn't automatically cast to
+     a void * provoked a warning.
+
+   . Turning on some more compiler warnings threw up some "shadow" variables
+     and a few more missing casts.
+
+4. If PCRE was complied with UTF-8 support, but called without the PCRE_UTF8
+   option, a class that contained a single character with a value between 128
+   and 255 (e.g. /[\xFF]/) caused PCRE to crash.
+
+5. If PCRE was compiled with UTF-8 support, but called without the PCRE_UTF8
+   option, a class that contained several characters, but with at least one
+   whose value was between 128 and 255 caused PCRE to crash.
+
+
+Version 4.1 12-Mar-03
+---------------------
+
+1. Compiling with gcc -pedantic found a couple of places where casts were
+needed, and a string in dftables.c that was longer than standard compilers are
+required to support.
+
+2. Compiling with Sun's compiler found a few more places where the code could
+be tidied up in order to avoid warnings.
+
+3. The variables for cross-compiling were called HOST_CC and HOST_CFLAGS; the
+first of these names is deprecated in the latest Autoconf in favour of the name
+CC_FOR_BUILD, because "host" is typically used to mean the system on which the
+compiled code will be run. I can't find a reference for HOST_CFLAGS, but by
+analogy I have changed it to CFLAGS_FOR_BUILD.
+
+4. Added -no-undefined to the linking command in the Makefile, because this is
+apparently helpful for Windows. To make it work, also added "-L. -lpcre" to the
+linking step for the pcreposix library.
+
+5. PCRE was failing to diagnose the case of two named groups with the same
+name.
+
+6. A problem with one of PCRE's optimizations was discovered. PCRE remembers a
+literal character that is needed in the subject for a match, and scans along to
+ensure that it is present before embarking on the full matching process. This
+saves time in cases of nested unlimited repeats that are never going to match.
+Problem: the scan can take a lot of time if the subject is very long (e.g.
+megabytes), thus penalizing straightforward matches. It is now done only if the
+amount of subject to be scanned is less than 1000 bytes.
+
+7. A lesser problem with the same optimization is that it was recording the
+first character of an anchored pattern as "needed", thus provoking a search
+right along the subject, even when the first match of the pattern was going to
+fail. The "needed" character is now not set for anchored patterns, unless it
+follows something in the pattern that is of non-fixed length. Thus, it still
+fulfils its original purpose of finding quick non-matches in cases of nested
+unlimited repeats, but isn't used for simple anchored patterns such as /^abc/.
+
+
+Version 4.0 17-Feb-03
+---------------------
+
+1. If a comment in an extended regex that started immediately after a meta-item
+extended to the end of string, PCRE compiled incorrect data. This could lead to
+all kinds of weird effects. Example: /#/ was bad; /()#/ was bad; /a#/ was not.
+
+2. Moved to autoconf 2.53 and libtool 1.4.2.
+
+3. Perl 5.8 no longer needs "use utf8" for doing UTF-8 things. Consequently,
+the special perltest8 script is no longer needed - all the tests can be run
+from a single perltest script.
+
+4. From 5.004, Perl has not included the VT character (0x0b) in the set defined
+by \s. It has now been removed in PCRE. This means it isn't recognized as
+whitespace in /x regexes too, which is the same as Perl. Note that the POSIX
+class [:space:] *does* include VT, thereby creating a mess.
+
+5. Added the class [:blank:] (a GNU extension from Perl 5.8) to match only
+space and tab.
+
+6. Perl 5.005 was a long time ago. It's time to amalgamate the tests that use
+its new features into the main test script, reducing the number of scripts.
+
+7. Perl 5.8 has changed the meaning of patterns like /a(?i)b/. Earlier versions
+were backward compatible, and made the (?i) apply to the whole pattern, as if
+/i were given. Now it behaves more logically, and applies the option setting
+only to what follows. PCRE has been changed to follow suit. However, if it
+finds options settings right at the start of the pattern, it extracts them into
+the global options, as before. Thus, they show up in the info data.
+
+8. Added support for the \Q...\E escape sequence. Characters in between are
+treated as literals. This is slightly different from Perl in that $ and @ are
+also handled as literals inside the quotes. In Perl, they will cause variable
+interpolation. Note the following examples:
+
+    Pattern            PCRE matches      Perl matches
+
+    \Qabc$xyz\E        abc$xyz           abc followed by the contents of $xyz
+    \Qabc\$xyz\E       abc\$xyz          abc\$xyz
+    \Qabc\E\$\Qxyz\E   abc$xyz           abc$xyz
+
+For compatibility with Perl, \Q...\E sequences are recognized inside character
+classes as well as outside them.
+
+9. Re-organized 3 code statements in pcretest to avoid "overflow in
+floating-point constant arithmetic" warnings from a Microsoft compiler. Added a
+(size_t) cast to one statement in pcretest and one in pcreposix to avoid
+signed/unsigned warnings.
+
+10. SunOS4 doesn't have strtoul(). This was used only for unpicking the -o
+option for pcretest, so I've replaced it by a simple function that does just
+that job.
+
+11. pcregrep was ending with code 0 instead of 2 for the commands "pcregrep" or
+"pcregrep -".
+
+12. Added "possessive quantifiers" ?+, *+, ++, and {,}+ which come from Sun's
+Java package. This provides some syntactic sugar for simple cases of what my
+documentation calls "once-only subpatterns". A pattern such as x*+ is the same
+as (?>x*). In other words, if what is inside (?>...) is just a single repeated
+item, you can use this simplified notation. Note that only makes sense with
+greedy quantifiers. Consequently, the use of the possessive quantifier forces
+greediness, whatever the setting of the PCRE_UNGREEDY option.
+
+13. A change of greediness default within a pattern was not taking effect at
+the current level for patterns like /(b+(?U)a+)/. It did apply to parenthesized
+subpatterns that followed. Patterns like /b+(?U)a+/ worked because the option
+was abstracted outside.
+
+14. PCRE now supports the \G assertion. It is true when the current matching
+position is at the start point of the match. This differs from \A when the
+starting offset is non-zero. Used with the /g option of pcretest (or similar
+code), it works in the same way as it does for Perl's /g option. If all
+alternatives of a regex begin with \G, the expression is anchored to the start
+match position, and the "anchored" flag is set in the compiled expression.
+
+15. Some bugs concerning the handling of certain option changes within patterns
+have been fixed. These applied to options other than (?ims). For example,
+"a(?x: b c )d" did not match "XabcdY" but did match "Xa b c dY". It should have
+been the other way round. Some of this was related to change 7 above.
+
+16. PCRE now gives errors for /[.x.]/ and /[=x=]/ as unsupported POSIX
+features, as Perl does. Previously, PCRE gave the warnings only for /[[.x.]]/
+and /[[=x=]]/. PCRE now also gives an error for /[:name:]/ because it supports
+POSIX classes only within a class (e.g. /[[:alpha:]]/).
+
+17. Added support for Perl's \C escape. This matches one byte, even in UTF8
+mode. Unlike ".", it always matches newline, whatever the setting of
+PCRE_DOTALL. However, PCRE does not permit \C to appear in lookbehind
+assertions. Perl allows it, but it doesn't (in general) work because it can't
+calculate the length of the lookbehind. At least, that's the case for Perl
+5.8.0 - I've been told they are going to document that it doesn't work in
+future.
+
+18. Added an error diagnosis for escapes that PCRE does not support: these are
+\L, \l, \N, \P, \p, \U, \u, and \X.
+
+19. Although correctly diagnosing a missing ']' in a character class, PCRE was
+reading past the end of the pattern in cases such as /[abcd/.
+
+20. PCRE was getting more memory than necessary for patterns with classes that
+contained both POSIX named classes and other characters, e.g. /[[:space:]abc/.
+
+21. Added some code, conditional on #ifdef VPCOMPAT, to make life easier for
+compiling PCRE for use with Virtual Pascal.
+
+22. Small fix to the Makefile to make it work properly if the build is done
+outside the source tree.
+
+23. Added a new extension: a condition to go with recursion. If a conditional
+subpattern starts with (?(R) the "true" branch is used if recursion has
+happened, whereas the "false" branch is used only at the top level.
+
+24. When there was a very long string of literal characters (over 255 bytes
+without UTF support, over 250 bytes with UTF support), the computation of how
+much memory was required could be incorrect, leading to segfaults or other
+strange effects.
+
+25. PCRE was incorrectly assuming anchoring (either to start of subject or to
+start of line for a non-DOTALL pattern) when a pattern started with (.*) and
+there was a subsequent back reference to those brackets. This meant that, for
+example, /(.*)\d+\1/ failed to match "abc123bc". Unfortunately, it isn't
+possible to check for precisely this case. All we can do is abandon the
+optimization if .* occurs inside capturing brackets when there are any back
+references whatsoever. (See below for a better fix that came later.)
+
+26. The handling of the optimization for finding the first character of a
+non-anchored pattern, and for finding a character that is required later in the
+match were failing in some cases. This didn't break the matching; it just
+failed to optimize when it could. The way this is done has been re-implemented.
+
+27. Fixed typo in error message for invalid (?R item (it said "(?p").
+
+28. Added a new feature that provides some of the functionality that Perl
+provides with (?{...}). The facility is termed a "callout". The way it is done
+in PCRE is for the caller to provide an optional function, by setting
+pcre_callout to its entry point. Like pcre_malloc and pcre_free, this is a
+global variable. By default it is unset, which disables all calling out. To get
+the function called, the regex must include (?C) at appropriate points. This
+is, in fact, equivalent to (?C0), and any number <= 255 may be given with (?C).
+This provides a means of identifying different callout points. When PCRE
+reaches such a point in the regex, if pcre_callout has been set, the external
+function is called. It is provided with data in a structure called
+pcre_callout_block, which is defined in pcre.h. If the function returns 0,
+matching continues; if it returns a non-zero value, the match at the current
+point fails. However, backtracking will occur if possible. [This was changed
+later and other features added - see item 49 below.]
+
+29. pcretest is upgraded to test the callout functionality. It provides a
+callout function that displays information. By default, it shows the start of
+the match and the current position in the text. There are some new data escapes
+to vary what happens:
+
+    \C+         in addition, show current contents of captured substrings
+    \C-         do not supply a callout function
+    \C!n        return 1 when callout number n is reached
+    \C!n!m      return 1 when callout number n is reached for the mth time
+
+30. If pcregrep was called with the -l option and just a single file name, it
+output "<stdin>" if a match was found, instead of the file name.
+
+31. Improve the efficiency of the POSIX API to PCRE. If the number of capturing
+slots is less than POSIX_MALLOC_THRESHOLD, use a block on the stack to pass to
+pcre_exec(). This saves a malloc/free per call. The default value of
+POSIX_MALLOC_THRESHOLD is 10; it can be changed by --with-posix-malloc-threshold
+when configuring.
+
+32. The default maximum size of a compiled pattern is 64K. There have been a
+few cases of people hitting this limit. The code now uses macros to handle the
+storing of links as offsets within the compiled pattern. It defaults to 2-byte
+links, but this can be changed to 3 or 4 bytes by --with-link-size when
+configuring. Tests 2 and 5 work only with 2-byte links because they output
+debugging information about compiled patterns.
+
+33. Internal code re-arrangements:
+
+(a) Moved the debugging function for printing out a compiled regex into
+    its own source file (printint.c) and used #include to pull it into
+    pcretest.c and, when DEBUG is defined, into pcre.c, instead of having two
+    separate copies.
+
+(b) Defined the list of op-code names for debugging as a macro in
+    internal.h so that it is next to the definition of the opcodes.
+
+(c) Defined a table of op-code lengths for simpler skipping along compiled
+    code. This is again a macro in internal.h so that it is next to the
+    definition of the opcodes.
+
+34. Added support for recursive calls to individual subpatterns, along the
+lines of Robin Houston's patch (but implemented somewhat differently).
+
+35. Further mods to the Makefile to help Win32. Also, added code to pcregrep to
+allow it to read and process whole directories in Win32. This code was
+contributed by Lionel Fourquaux; it has not been tested by me.
+
+36. Added support for named subpatterns. The Python syntax (?P<name>...) is
+used to name a group. Names consist of alphanumerics and underscores, and must
+be unique. Back references use the syntax (?P=name) and recursive calls use
+(?P>name) which is a PCRE extension to the Python extension. Groups still have
+numbers. The function pcre_fullinfo() can be used after compilation to extract
+a name/number map. There are three relevant calls:
+
+  PCRE_INFO_NAMEENTRYSIZE        yields the size of each entry in the map
+  PCRE_INFO_NAMECOUNT            yields the number of entries
+  PCRE_INFO_NAMETABLE            yields a pointer to the map.
+
+The map is a vector of fixed-size entries. The size of each entry depends on
+the length of the longest name used. The first two bytes of each entry are the
+group number, most significant byte first. There follows the corresponding
+name, zero terminated. The names are in alphabetical order.
+
+37. Make the maximum literal string in the compiled code 250 for the non-UTF-8
+case instead of 255. Making it the same both with and without UTF-8 support
+means that the same test output works with both.
+
+38. There was a case of malloc(0) in the POSIX testing code in pcretest. Avoid
+calling malloc() with a zero argument.
+
+39. Change 25 above had to resort to a heavy-handed test for the .* anchoring
+optimization. I've improved things by keeping a bitmap of backreferences with
+numbers 1-31 so that if .* occurs inside capturing brackets that are not in
+fact referenced, the optimization can be applied. It is unlikely that a
+relevant occurrence of .* (i.e. one which might indicate anchoring or forcing
+the match to follow \n) will appear inside brackets with a number greater than
+31, but if it does, any back reference > 31 suppresses the optimization.
+
+40. Added a new compile-time option PCRE_NO_AUTO_CAPTURE. This has the effect
+of disabling numbered capturing parentheses. Any opening parenthesis that is
+not followed by ? behaves as if it were followed by ?: but named parentheses
+can still be used for capturing (and they will acquire numbers in the usual
+way).
+
+41. Redesigned the return codes from the match() function into yes/no/error so
+that errors can be passed back from deep inside the nested calls. A malloc
+failure while inside a recursive subpattern call now causes the
+PCRE_ERROR_NOMEMORY return instead of quietly going wrong.
+
+42. It is now possible to set a limit on the number of times the match()
+function is called in a call to pcre_exec(). This facility makes it possible to
+limit the amount of recursion and backtracking, though not in a directly
+obvious way, because the match() function is used in a number of different
+circumstances. The count starts from zero for each position in the subject
+string (for non-anchored patterns). The default limit is, for compatibility, a
+large number, namely 10 000 000. You can change this in two ways:
+
+(a) When configuring PCRE before making, you can use --with-match-limit=n
+    to set a default value for the compiled library.
+
+(b) For each call to pcre_exec(), you can pass a pcre_extra block in which
+    a different value is set. See 45 below.
+
+If the limit is exceeded, pcre_exec() returns PCRE_ERROR_MATCHLIMIT.
+
+43. Added a new function pcre_config(int, void *) to enable run-time extraction
+of things that can be changed at compile time. The first argument specifies
+what is wanted and the second points to where the information is to be placed.
+The current list of available information is:
+
+  PCRE_CONFIG_UTF8
+
+The output is an integer that is set to one if UTF-8 support is available;
+otherwise it is set to zero.
+
+  PCRE_CONFIG_NEWLINE
+
+The output is an integer that it set to the value of the code that is used for
+newline. It is either LF (10) or CR (13).
+
+  PCRE_CONFIG_LINK_SIZE
+
+The output is an integer that contains the number of bytes used for internal
+linkage in compiled expressions. The value is 2, 3, or 4. See item 32 above.
+
+  PCRE_CONFIG_POSIX_MALLOC_THRESHOLD
+
+The output is an integer that contains the threshold above which the POSIX
+interface uses malloc() for output vectors. See item 31 above.
+
+  PCRE_CONFIG_MATCH_LIMIT
+
+The output is an unsigned integer that contains the default limit of the number
+of match() calls in a pcre_exec() execution. See 42 above.
+
+44. pcretest has been upgraded by the addition of the -C option. This causes it
+to extract all the available output from the new pcre_config() function, and to
+output it. The program then exits immediately.
+
+45. A need has arisen to pass over additional data with calls to pcre_exec() in
+order to support additional features. One way would have been to define
+pcre_exec2() (for example) with extra arguments, but this would not have been
+extensible, and would also have required all calls to the original function to
+be mapped to the new one. Instead, I have chosen to extend the mechanism that
+is used for passing in "extra" data from pcre_study().
+
+The pcre_extra structure is now exposed and defined in pcre.h. It currently
+contains the following fields:
+
+  flags         a bitmap indicating which of the following fields are set
+  study_data    opaque data from pcre_study()
+  match_limit   a way of specifying a limit on match() calls for a specific
+                  call to pcre_exec()
+  callout_data  data for callouts (see 49 below)
+
+The flag bits are also defined in pcre.h, and are
+
+  PCRE_EXTRA_STUDY_DATA
+  PCRE_EXTRA_MATCH_LIMIT
+  PCRE_EXTRA_CALLOUT_DATA
+
+The pcre_study() function now returns one of these new pcre_extra blocks, with
+the actual study data pointed to by the study_data field, and the
+PCRE_EXTRA_STUDY_DATA flag set. This can be passed directly to pcre_exec() as
+before. That is, this change is entirely upwards-compatible and requires no
+change to existing code.
+
+If you want to pass in additional data to pcre_exec(), you can either place it
+in a pcre_extra block provided by pcre_study(), or create your own pcre_extra
+block.
+
+46. pcretest has been extended to test the PCRE_EXTRA_MATCH_LIMIT feature. If a
+data string contains the escape sequence \M, pcretest calls pcre_exec() several
+times with different match limits, until it finds the minimum value needed for
+pcre_exec() to complete. The value is then output. This can be instructive; for
+most simple matches the number is quite small, but for pathological cases it
+gets very large very quickly.
+
+47. There's a new option for pcre_fullinfo() called PCRE_INFO_STUDYSIZE. It
+returns the size of the data block pointed to by the study_data field in a
+pcre_extra block, that is, the value that was passed as the argument to
+pcre_malloc() when PCRE was getting memory in which to place the information
+created by pcre_study(). The fourth argument should point to a size_t variable.
+pcretest has been extended so that this information is shown after a successful
+pcre_study() call when information about the compiled regex is being displayed.
+
+48. Cosmetic change to Makefile: there's no need to have / after $(DESTDIR)
+because what follows is always an absolute path. (Later: it turns out that this
+is more than cosmetic for MinGW, because it doesn't like empty path
+components.)
+
+49. Some changes have been made to the callout feature (see 28 above):
+
+(i)  A callout function now has three choices for what it returns:
+
+       0  =>  success, carry on matching
+     > 0  =>  failure at this point, but backtrack if possible
+     < 0  =>  serious error, return this value from pcre_exec()
+
+     Negative values should normally be chosen from the set of PCRE_ERROR_xxx
+     values. In particular, returning PCRE_ERROR_NOMATCH forces a standard
+     "match failed" error. The error number PCRE_ERROR_CALLOUT is reserved for
+     use by callout functions. It will never be used by PCRE itself.
+
+(ii) The pcre_extra structure (see 45 above) has a void * field called
+     callout_data, with corresponding flag bit PCRE_EXTRA_CALLOUT_DATA. The
+     pcre_callout_block structure has a field of the same name. The contents of
+     the field passed in the pcre_extra structure are passed to the callout
+     function in the corresponding field in the callout block. This makes it
+     easier to use the same callout-containing regex from multiple threads. For
+     testing, the pcretest program has a new data escape
+
+       \C*n        pass the number n (may be negative) as callout_data
+
+     If the callout function in pcretest receives a non-zero value as
+     callout_data, it returns that value.
+
+50. Makefile wasn't handling CFLAGS properly when compiling dftables. Also,
+there were some redundant $(CFLAGS) in commands that are now specified as
+$(LINK), which already includes $(CFLAGS).
+
+51. Extensions to UTF-8 support are listed below. These all apply when (a) PCRE
+has been compiled with UTF-8 support *and* pcre_compile() has been compiled
+with the PCRE_UTF8 flag. Patterns that are compiled without that flag assume
+one-byte characters throughout. Note that case-insensitive matching applies
+only to characters whose values are less than 256. PCRE doesn't support the
+notion of cases for higher-valued characters.
+
+(i)   A character class whose characters are all within 0-255 is handled as
+      a bit map, and the map is inverted for negative classes. Previously, a
+      character > 255 always failed to match such a class; however it should
+      match if the class was a negative one (e.g. [^ab]). This has been fixed.
+
+(ii)  A negated character class with a single character < 255 is coded as
+      "not this character" (OP_NOT). This wasn't working properly when the test
+      character was multibyte, either singly or repeated.
+
+(iii) Repeats of multibyte characters are now handled correctly in UTF-8
+      mode, for example: \x{100}{2,3}.
+
+(iv)  The character escapes \b, \B, \d, \D, \s, \S, \w, and \W (either
+      singly or repeated) now correctly test multibyte characters. However,
+      PCRE doesn't recognize any characters with values greater than 255 as
+      digits, spaces, or word characters. Such characters always match \D, \S,
+      and \W, and never match \d, \s, or \w.
+
+(v)   Classes may now contain characters and character ranges with values
+      greater than 255. For example: [ab\x{100}-\x{400}].
+
+(vi)  pcregrep now has a --utf-8 option (synonym -u) which makes it call
+      PCRE in UTF-8 mode.
+
+52. The info request value PCRE_INFO_FIRSTCHAR has been renamed
+PCRE_INFO_FIRSTBYTE because it is a byte value. However, the old name is
+retained for backwards compatibility. (Note that LASTLITERAL is also a byte
+value.)
+
+53. The single man page has become too large. I have therefore split it up into
+a number of separate man pages. These also give rise to individual HTML pages;
+these are now put in a separate directory, and there is an index.html page that
+lists them all. Some hyperlinking between the pages has been installed.
+
+54. Added convenience functions for handling named capturing parentheses.
+
+55. Unknown escapes inside character classes (e.g. [\M]) and escapes that
+aren't interpreted therein (e.g. [\C]) are literals in Perl. This is now also
+true in PCRE, except when the PCRE_EXTENDED option is set, in which case they
+are faulted.
+
+56. Introduced HOST_CC and HOST_CFLAGS which can be set in the environment when
+calling configure. These values are used when compiling the dftables.c program
+which is run to generate the source of the default character tables. They
+default to the values of CC and CFLAGS. If you are cross-compiling PCRE,
+you will need to set these values.
+
+57. Updated the building process for Windows DLL, as provided by Fred Cox.
+
+
+Version 3.9 02-Jan-02
+---------------------
+
+1. A bit of extraneous text had somehow crept into the pcregrep documentation.
+
+2. If --disable-static was given, the building process failed when trying to
+build pcretest and pcregrep. (For some reason it was using libtool to compile
+them, which is not right, as they aren't part of the library.)
+
+
+Version 3.8 18-Dec-01
+---------------------
+
+1. The experimental UTF-8 code was completely screwed up. It was packing the
+bytes in the wrong order. How dumb can you get?
+
+
+Version 3.7 29-Oct-01
+---------------------
+
+1. In updating pcretest to check change 1 of version 3.6, I screwed up.
+This caused pcretest, when used on the test data, to segfault. Unfortunately,
+this didn't happen under Solaris 8, where I normally test things.
+
+2. The Makefile had to be changed to make it work on BSD systems, where 'make'
+doesn't seem to recognize that ./xxx and xxx are the same file. (This entry
+isn't in ChangeLog distributed with 3.7 because I forgot when I hastily made
+this fix an hour or so after the initial 3.7 release.)
+
+
+Version 3.6 23-Oct-01
+---------------------
+
+1. Crashed with /(sens|respons)e and \1ibility/ and "sense and sensibility" if
+offsets passed as NULL with zero offset count.
+
+2. The config.guess and config.sub files had not been updated when I moved to
+the latest autoconf.
+
+
+Version 3.5 15-Aug-01
+---------------------
+
+1. Added some missing #if !defined NOPOSIX conditionals in pcretest.c that
+had been forgotten.
+
+2. By using declared but undefined structures, we can avoid using "void"
+definitions in pcre.h while keeping the internal definitions of the structures
+private.
+
+3. The distribution is now built using autoconf 2.50 and libtool 1.4. From a
+user point of view, this means that both static and shared libraries are built
+by default, but this can be individually controlled. More of the work of
+handling this static/shared cases is now inside libtool instead of PCRE's make
+file.
+
+4. The pcretest utility is now installed along with pcregrep because it is
+useful for users (to test regexs) and by doing this, it automatically gets
+relinked by libtool. The documentation has been turned into a man page, so
+there are now .1, .txt, and .html versions in /doc.
+
+5. Upgrades to pcregrep:
+   (i)   Added long-form option names like gnu grep.
+   (ii)  Added --help to list all options with an explanatory phrase.
+   (iii) Added -r, --recursive to recurse into sub-directories.
+   (iv)  Added -f, --file to read patterns from a file.
+
+6. pcre_exec() was referring to its "code" argument before testing that
+argument for NULL (and giving an error if it was NULL).
+
+7. Upgraded Makefile.in to allow for compiling in a different directory from
+the source directory.
+
+8. Tiny buglet in pcretest: when pcre_fullinfo() was called to retrieve the
+options bits, the pointer it was passed was to an int instead of to an unsigned
+long int. This mattered only on 64-bit systems.
+
+9. Fixed typo (3.4/1) in pcre.h again. Sigh. I had changed pcre.h (which is
+generated) instead of pcre.in, which it its source. Also made the same change
+in several of the .c files.
+
+10. A new release of gcc defines printf() as a macro, which broke pcretest
+because it had an ifdef in the middle of a string argument for printf(). Fixed
+by using separate calls to printf().
+
+11. Added --enable-newline-is-cr and --enable-newline-is-lf to the configure
+script, to force use of CR or LF instead of \n in the source. On non-Unix
+systems, the value can be set in config.h.
+
+12. The limit of 200 on non-capturing parentheses is a _nesting_ limit, not an
+absolute limit. Changed the text of the error message to make this clear, and
+likewise updated the man page.
+
+13. The limit of 99 on the number of capturing subpatterns has been removed.
+The new limit is 65535, which I hope will not be a "real" limit.
+
+
+Version 3.4 22-Aug-00
+---------------------
+
+1. Fixed typo in pcre.h: unsigned const char * changed to const unsigned char *.
+
+2. Diagnose condition (?(0) as an error instead of crashing on matching.
+
+
+Version 3.3 01-Aug-00
+---------------------
+
+1. If an octal character was given, but the value was greater than \377, it
+was not getting masked to the least significant bits, as documented. This could
+lead to crashes in some systems.
+
+2. Perl 5.6 (if not earlier versions) accepts classes like [a-\d] and treats
+the hyphen as a literal. PCRE used to give an error; it now behaves like Perl.
+
+3. Added the functions pcre_free_substring() and pcre_free_substring_list().
+These just pass their arguments on to (pcre_free)(), but they are provided
+because some uses of PCRE bind it to non-C systems that can call its functions,
+but cannot call free() or pcre_free() directly.
+
+4. Add "make test" as a synonym for "make check". Corrected some comments in
+the Makefile.
+
+5. Add $(DESTDIR)/ in front of all the paths in the "install" target in the
+Makefile.
+
+6. Changed the name of pgrep to pcregrep, because Solaris has introduced a
+command called pgrep for grepping around the active processes.
+
+7. Added the beginnings of support for UTF-8 character strings.
+
+8. Arranged for the Makefile to pass over the settings of CC, CFLAGS, and
+RANLIB to ./ltconfig so that they are used by libtool. I think these are all
+the relevant ones. (AR is not passed because ./ltconfig does its own figuring
+out for the ar command.)
+
+
+Version 3.2 12-May-00
+---------------------
+
+This is purely a bug fixing release.
+
+1. If the pattern /((Z)+|A)*/ was matched agained ZABCDEFG it matched Z instead
+of ZA. This was just one example of several cases that could provoke this bug,
+which was introduced by change 9 of version 2.00. The code for breaking
+infinite loops after an iteration that matches an empty string was't working
+correctly.
+
+2. The pcretest program was not imitating Perl correctly for the pattern /a*/g
+when matched against abbab (for example). After matching an empty string, it
+wasn't forcing anchoring when setting PCRE_NOTEMPTY for the next attempt; this
+caused it to match further down the string than it should.
+
+3. The code contained an inclusion of sys/types.h. It isn't clear why this
+was there because it doesn't seem to be needed, and it causes trouble on some
+systems, as it is not a Standard C header. It has been removed.
+
+4. Made 4 silly changes to the source to avoid stupid compiler warnings that
+were reported on the Macintosh. The changes were from
+
+  while ((c = *(++ptr)) != 0 && c != '\n');
+to
+  while ((c = *(++ptr)) != 0 && c != '\n') ;
+
+Totally extraordinary, but if that's what it takes...
+
+5. PCRE is being used in one environment where neither memmove() nor bcopy() is
+available. Added HAVE_BCOPY and an autoconf test for it; if neither
+HAVE_MEMMOVE nor HAVE_BCOPY is set, use a built-in emulation function which
+assumes the way PCRE uses memmove() (always moving upwards).
+
+6. PCRE is being used in one environment where strchr() is not available. There
+was only one use in pcre.c, and writing it out to avoid strchr() probably gives
+faster code anyway.
+
+
+Version 3.2 12-May-00
+---------------------
+
+This is purely a bug fixing release.
+
+1. If the pattern /((Z)+|A)*/ was matched agained ZABCDEFG it matched Z instead
+of ZA. This was just one example of several cases that could provoke this bug,
+which was introduced by change 9 of version 2.00. The code for breaking
+infinite loops after an iteration that matches an empty string was't working
+correctly.
+
+2. The pcretest program was not imitating Perl correctly for the pattern /a*/g
+when matched against abbab (for example). After matching an empty string, it
+wasn't forcing anchoring when setting PCRE_NOTEMPTY for the next attempt; this
+caused it to match further down the string than it should.
+
+3. The code contained an inclusion of sys/types.h. It isn't clear why this
+was there because it doesn't seem to be needed, and it causes trouble on some
+systems, as it is not a Standard C header. It has been removed.
+
+4. Made 4 silly changes to the source to avoid stupid compiler warnings that
+were reported on the Macintosh. The changes were from
+
+  while ((c = *(++ptr)) != 0 && c != '\n');
+to
+  while ((c = *(++ptr)) != 0 && c != '\n') ;
+
+Totally extraordinary, but if that's what it takes...
+
+5. PCRE is being used in one environment where neither memmove() nor bcopy() is
+available. Added HAVE_BCOPY and an autoconf test for it; if neither
+HAVE_MEMMOVE nor HAVE_BCOPY is set, use a built-in emulation function which
+assumes the way PCRE uses memmove() (always moving upwards).
+
+6. PCRE is being used in one environment where strchr() is not available. There
+was only one use in pcre.c, and writing it out to avoid strchr() probably gives
+faster code anyway.
+
+
+Version 3.1 09-Feb-00
+---------------------
+
+The only change in this release is the fixing of some bugs in Makefile.in for
+the "install" target:
+
+(1) It was failing to install pcreposix.h.
+
+(2) It was overwriting the pcre.3 man page with the pcreposix.3 man page.
+
+
+Version 3.0 01-Feb-00
+---------------------
+
+1. Add support for the /+ modifier to perltest (to output $` like it does in
+pcretest).
+
+2. Add support for the /g modifier to perltest.
+
+3. Fix pcretest so that it behaves even more like Perl for /g when the pattern
+matches null strings.
+
+4. Fix perltest so that it doesn't do unwanted things when fed an empty
+pattern. Perl treats empty patterns specially - it reuses the most recent
+pattern, which is not what we want. Replace // by /(?#)/ in order to avoid this
+effect.
+
+5. The POSIX interface was broken in that it was just handing over the POSIX
+captured string vector to pcre_exec(), but (since release 2.00) PCRE has
+required a bigger vector, with some working space on the end. This means that
+the POSIX wrapper now has to get and free some memory, and copy the results.
+
+6. Added some simple autoconf support, placing the test data and the
+documentation in separate directories, re-organizing some of the
+information files, and making it build pcre-config (a GNU standard). Also added
+libtool support for building PCRE as a shared library, which is now the
+default.
+
+7. Got rid of the leading zero in the definition of PCRE_MINOR because 08 and
+09 are not valid octal constants. Single digits will be used for minor values
+less than 10.
+
+8. Defined REG_EXTENDED and REG_NOSUB as zero in the POSIX header, so that
+existing programs that set these in the POSIX interface can use PCRE without
+modification.
+
+9. Added a new function, pcre_fullinfo() with an extensible interface. It can
+return all that pcre_info() returns, plus additional data. The pcre_info()
+function is retained for compatibility, but is considered to be obsolete.
+
+10. Added experimental recursion feature (?R) to handle one common case that
+Perl 5.6 will be able to do with (?p{...}).
+
+11. Added support for POSIX character classes like [:alpha:], which Perl is
+adopting.
+
+
+Version 2.08 31-Aug-99
+----------------------
+
+1. When startoffset was not zero and the pattern began with ".*", PCRE was not
+trying to match at the startoffset position, but instead was moving forward to
+the next newline as if a previous match had failed.
+
+2. pcretest was not making use of PCRE_NOTEMPTY when repeating for /g and /G,
+and could get into a loop if a null string was matched other than at the start
+of the subject.
+
+3. Added definitions of PCRE_MAJOR and PCRE_MINOR to pcre.h so the version can
+be distinguished at compile time, and for completeness also added PCRE_DATE.
+
+5. Added Paul Sokolovsky's minor changes to make it easy to compile a Win32 DLL
+in GnuWin32 environments.
+
+
+Version 2.07 29-Jul-99
+----------------------
+
+1. The documentation is now supplied in plain text form and HTML as well as in
+the form of man page sources.
+
+2. C++ compilers don't like assigning (void *) values to other pointer types.
+In particular this affects malloc(). Although there is no problem in Standard
+C, I've put in casts to keep C++ compilers happy.
+
+3. Typo on pcretest.c; a cast of (unsigned char *) in the POSIX regexec() call
+should be (const char *).
+
+4. If NOPOSIX is defined, pcretest.c compiles without POSIX support. This may
+be useful for non-Unix systems who don't want to bother with the POSIX stuff.
+However, I haven't made this a standard facility. The documentation doesn't
+mention it, and the Makefile doesn't support it.
+
+5. The Makefile now contains an "install" target, with editable destinations at
+the top of the file. The pcretest program is not installed.
+
+6. pgrep -V now gives the PCRE version number and date.
+
+7. Fixed bug: a zero repetition after a literal string (e.g. /abcde{0}/) was
+causing the entire string to be ignored, instead of just the last character.
+
+8. If a pattern like /"([^\\"]+|\\.)*"/ is applied in the normal way to a
+non-matching string, it can take a very, very long time, even for strings of
+quite modest length, because of the nested recursion. PCRE now does better in
+some of these cases. It does this by remembering the last required literal
+character in the pattern, and pre-searching the subject to ensure it is present
+before running the real match. In other words, it applies a heuristic to detect
+some types of certain failure quickly, and in the above example, if presented
+with a string that has no trailing " it gives "no match" very quickly.
+
+9. A new runtime option PCRE_NOTEMPTY causes null string matches to be ignored;
+other alternatives are tried instead.
+
+
+Version 2.06 09-Jun-99
+----------------------
+
+1. Change pcretest's output for amount of store used to show just the code
+space, because the remainder (the data block) varies in size between 32-bit and
+64-bit systems.
+
+2. Added an extra argument to pcre_exec() to supply an offset in the subject to
+start matching at. This allows lookbehinds to work when searching for multiple
+occurrences in a string.
+
+3. Added additional options to pcretest for testing multiple occurrences:
+
+   /+   outputs the rest of the string that follows a match
+   /g   loops for multiple occurrences, using the new startoffset argument
+   /G   loops for multiple occurrences by passing an incremented pointer
+
+4. PCRE wasn't doing the "first character" optimization for patterns starting
+with \b or \B, though it was doing it for other lookbehind assertions. That is,
+it wasn't noticing that a match for a pattern such as /\bxyz/ has to start with
+the letter 'x'. On long subject strings, this gives a significant speed-up.
+
+
+Version 2.05 21-Apr-99
+----------------------
+
+1. Changed the type of magic_number from int to long int so that it works
+properly on 16-bit systems.
+
+2. Fixed a bug which caused patterns starting with .* not to work correctly
+when the subject string contained newline characters. PCRE was assuming
+anchoring for such patterns in all cases, which is not correct because .* will
+not pass a newline unless PCRE_DOTALL is set. It now assumes anchoring only if
+DOTALL is set at top level; otherwise it knows that patterns starting with .*
+must be retried after every newline in the subject.
+
+
+Version 2.04 18-Feb-99
+----------------------
+
+1. For parenthesized subpatterns with repeats whose minimum was zero, the
+computation of the store needed to hold the pattern was incorrect (too large).
+If such patterns were nested a few deep, this could multiply and become a real
+problem.
+
+2. Added /M option to pcretest to show the memory requirement of a specific
+pattern. Made -m a synonym of -s (which does this globally) for compatibility.
+
+3. Subpatterns of the form (regex){n,m} (i.e. limited maximum) were being
+compiled in such a way that the backtracking after subsequent failure was
+pessimal. Something like (a){0,3} was compiled as (a)?(a)?(a)? instead of
+((a)((a)(a)?)?)? with disastrous performance if the maximum was of any size.
+
+
+Version 2.03 02-Feb-99
+----------------------
+
+1. Fixed typo and small mistake in man page.
+
+2. Added 4th condition (GPL supersedes if conflict) and created separate
+LICENCE file containing the conditions.
+
+3. Updated pcretest so that patterns such as /abc\/def/ work like they do in
+Perl, that is the internal \ allows the delimiter to be included in the
+pattern. Locked out the use of \ as a delimiter. If \ immediately follows
+the final delimiter, add \ to the end of the pattern (to test the error).
+
+4. Added the convenience functions for extracting substrings after a successful
+match. Updated pcretest to make it able to test these functions.
+
+
+Version 2.02 14-Jan-99
+----------------------
+
+1. Initialized the working variables associated with each extraction so that
+their saving and restoring doesn't refer to uninitialized store.
+
+2. Put dummy code into study.c in order to trick the optimizer of the IBM C
+compiler for OS/2 into generating correct code. Apparently IBM isn't going to
+fix the problem.
+
+3. Pcretest: the timing code wasn't using LOOPREPEAT for timing execution
+calls, and wasn't printing the correct value for compiling calls. Increased the
+default value of LOOPREPEAT, and the number of significant figures in the
+times.
+
+4. Changed "/bin/rm" in the Makefile to "-rm" so it works on Windows NT.
+
+5. Renamed "deftables" as "dftables" to get it down to 8 characters, to avoid
+a building problem on Windows NT with a FAT file system.
+
+
+Version 2.01 21-Oct-98
+----------------------
+
+1. Changed the API for pcre_compile() to allow for the provision of a pointer
+to character tables built by pcre_maketables() in the current locale. If NULL
+is passed, the default tables are used.
+
+
+Version 2.00 24-Sep-98
+----------------------
+
+1. Since the (>?) facility is in Perl 5.005, don't require PCRE_EXTRA to enable
+it any more.
+
+2. Allow quantification of (?>) groups, and make it work correctly.
+
+3. The first character computation wasn't working for (?>) groups.
+
+4. Correct the implementation of \Z (it is permitted to match on the \n at the
+end of the subject) and add 5.005's \z, which really does match only at the
+very end of the subject.
+
+5. Remove the \X "cut" facility; Perl doesn't have it, and (?> is neater.
+
+6. Remove the ability to specify CASELESS, MULTILINE, DOTALL, and
+DOLLAR_END_ONLY at runtime, to make it possible to implement the Perl 5.005
+localized options. All options to pcre_study() were also removed.
+
+7. Add other new features from 5.005:
+
+   $(?<=           positive lookbehind
+   $(?<!           negative lookbehind
+   (?imsx-imsx)    added the unsetting capability
+                   such a setting is global if at outer level; local otherwise
+   (?imsx-imsx:)   non-capturing groups with option setting
+   (?(cond)re|re)  conditional pattern matching
+
+   A backreference to itself in a repeated group matches the previous
+   captured string.
+
+8. General tidying up of studying (both automatic and via "study")
+consequential on the addition of new assertions.
+
+9. As in 5.005, unlimited repeated groups that could match an empty substring
+are no longer faulted at compile time. Instead, the loop is forcibly broken at
+runtime if any iteration does actually match an empty substring.
+
+10. Include the RunTest script in the distribution.
+
+11. Added tests from the Perl 5.005_02 distribution. This showed up a few
+discrepancies, some of which were old and were also with respect to 5.004. They
+have now been fixed.
+
+
+Version 1.09 28-Apr-98
+----------------------
+
+1. A negated single character class followed by a quantifier with a minimum
+value of one (e.g.  [^x]{1,6}  ) was not compiled correctly. This could lead to
+program crashes, or just wrong answers. This did not apply to negated classes
+containing more than one character, or to minima other than one.
+
+
+Version 1.08 27-Mar-98
+----------------------
+
+1. Add PCRE_UNGREEDY to invert the greediness of quantifiers.
+
+2. Add (?U) and (?X) to set PCRE_UNGREEDY and PCRE_EXTRA respectively. The
+latter must appear before anything that relies on it in the pattern.
+
+
+Version 1.07 16-Feb-98
+----------------------
+
+1. A pattern such as /((a)*)*/ was not being diagnosed as in error (unlimited
+repeat of a potentially empty string).
+
+
+Version 1.06 23-Jan-98
+----------------------
+
+1. Added Markus Oberhumer's little patches for C++.
+
+2. Literal strings longer than 255 characters were broken.
+
+
+Version 1.05 23-Dec-97
+----------------------
+
+1. Negated character classes containing more than one character were failing if
+PCRE_CASELESS was set at run time.
+
+
+Version 1.04 19-Dec-97
+----------------------
+
+1. Corrected the man page, where some "const" qualifiers had been omitted.
+
+2. Made debugging output print "{0,xxx}" instead of just "{,xxx}" to agree with
+input syntax.
+
+3. Fixed memory leak which occurred when a regex with back references was
+matched with an offsets vector that wasn't big enough. The temporary memory
+that is used in this case wasn't being freed if the match failed.
+
+4. Tidied pcretest to ensure it frees memory that it gets.
+
+5. Temporary memory was being obtained in the case where the passed offsets
+vector was exactly big enough.
+
+6. Corrected definition of offsetof() from change 5 below.
+
+7. I had screwed up change 6 below and broken the rules for the use of
+setjmp(). Now fixed.
+
+
+Version 1.03 18-Dec-97
+----------------------
+
+1. A erroneous regex with a missing opening parenthesis was correctly
+diagnosed, but PCRE attempted to access brastack[-1], which could cause crashes
+on some systems.
+
+2. Replaced offsetof(real_pcre, code) by offsetof(real_pcre, code[0]) because
+it was reported that one broken compiler failed on the former because "code" is
+also an independent variable.
+
+3. The erroneous regex a[]b caused an array overrun reference.
+
+4. A regex ending with a one-character negative class (e.g. /[^k]$/) did not
+fail on data ending with that character. (It was going on too far, and checking
+the next character, typically a binary zero.) This was specific to the
+optimized code for single-character negative classes.
+
+5. Added a contributed patch from the TIN world which does the following:
+
+  + Add an undef for memmove, in case the the system defines a macro for it.
+
+  + Add a definition of offsetof(), in case there isn't one. (I don't know
+    the reason behind this - offsetof() is part of the ANSI standard - but
+    it does no harm).
+
+  + Reduce the ifdef's in pcre.c using macro DPRINTF, thereby eliminating
+    most of the places where whitespace preceded '#'. I have given up and
+    allowed the remaining 2 cases to be at the margin.
+
+  + Rename some variables in pcre to eliminate shadowing. This seems very
+    pedantic, but does no harm, of course.
+
+6. Moved the call to setjmp() into its own function, to get rid of warnings
+from gcc -Wall, and avoided calling it at all unless PCRE_EXTRA is used.
+
+7. Constructs such as \d{8,} were compiling into the equivalent of
+\d{8}\d{0,65527} instead of \d{8}\d* which didn't make much difference to the
+outcome, but in this particular case used more store than had been allocated,
+which caused the bug to be discovered because it threw up an internal error.
+
+8. The debugging code in both pcre and pcretest for outputting the compiled
+form of a regex was going wrong in the case of back references followed by
+curly-bracketed repeats.
+
+
+Version 1.02 12-Dec-97
+----------------------
+
+1. Typos in pcre.3 and comments in the source fixed.
+
+2. Applied a contributed patch to get rid of places where it used to remove
+'const' from variables, and fixed some signed/unsigned and uninitialized
+variable warnings.
+
+3. Added the "runtest" target to Makefile.
+
+4. Set default compiler flag to -O2 rather than just -O.
+
+
+Version 1.01 19-Nov-97
+----------------------
+
+1. PCRE was failing to diagnose unlimited repeat of empty string for patterns
+like /([ab]*)*/, that is, for classes with more than one character in them.
+
+2. Likewise, it wasn't diagnosing patterns with "once-only" subpatterns, such
+as /((?>a*))*/ (a PCRE_EXTRA facility).
+
+
+Version 1.00 18-Nov-97
+----------------------
+
+1. Added compile-time macros to support systems such as SunOS4 which don't have
+memmove() or strerror() but have other things that can be used instead.
+
+2. Arranged that "make clean" removes the executables.
+
+
+Version 0.99 27-Oct-97
+----------------------
+
+1. Fixed bug in code for optimizing classes with only one character. It was
+initializing a 32-byte map regardless, which could cause it to run off the end
+of the memory it had got.
+
+2. Added, conditional on PCRE_EXTRA, the proposed (?>REGEX) construction.
+
+
+Version 0.98 22-Oct-97
+----------------------
+
+1. Fixed bug in code for handling temporary memory usage when there are more
+back references than supplied space in the ovector. This could cause segfaults.
+
+
+Version 0.97 21-Oct-97
+----------------------
+
+1. Added the \X "cut" facility, conditional on PCRE_EXTRA.
+
+2. Optimized negated single characters not to use a bit map.
+
+3. Brought error texts together as macro definitions; clarified some of them;
+fixed one that was wrong - it said "range out of order" when it meant "invalid
+escape sequence".
+
+4. Changed some char * arguments to const char *.
+
+5. Added PCRE_NOTBOL and PCRE_NOTEOL (from POSIX).
+
+6. Added the POSIX-style API wrapper in pcreposix.a and testing facilities in
+pcretest.
+
+
+Version 0.96 16-Oct-97
+----------------------
+
+1. Added a simple "pgrep" utility to the distribution.
+
+2. Fixed an incompatibility with Perl: "{" is now treated as a normal character
+unless it appears in one of the precise forms "{ddd}", "{ddd,}", or "{ddd,ddd}"
+where "ddd" means "one or more decimal digits".
+
+3. Fixed serious bug. If a pattern had a back reference, but the call to
+pcre_exec() didn't supply a large enough ovector to record the related
+identifying subpattern, the match always failed. PCRE now remembers the number
+of the largest back reference, and gets some temporary memory in which to save
+the offsets during matching if necessary, in order to ensure that
+backreferences always work.
+
+4. Increased the compatibility with Perl in a number of ways:
+
+  (a) . no longer matches \n by default; an option PCRE_DOTALL is provided
+      to request this handling. The option can be set at compile or exec time.
+
+  (b) $ matches before a terminating newline by default; an option
+      PCRE_DOLLAR_ENDONLY is provided to override this (but not in multiline
+      mode). The option can be set at compile or exec time.
+
+  (c) The handling of \ followed by a digit other than 0 is now supposed to be
+      the same as Perl's. If the decimal number it represents is less than 10
+      or there aren't that many previous left capturing parentheses, an octal
+      escape is read. Inside a character class, it's always an octal escape,
+      even if it is a single digit.
+
+  (d) An escaped but undefined alphabetic character is taken as a literal,
+      unless PCRE_EXTRA is set. Currently this just reserves the remaining
+      escapes.
+
+  (e) {0} is now permitted. (The previous item is removed from the compiled
+      pattern).
+
+5. Changed all the names of code files so that the basic parts are no longer
+than 10 characters, and abolished the teeny "globals.c" file.
+
+6. Changed the handling of character classes; they are now done with a 32-byte
+bit map always.
+
+7. Added the -d and /D options to pcretest to make it possible to look at the
+internals of compilation without having to recompile pcre.
+
+
+Version 0.95 23-Sep-97
+----------------------
+
+1. Fixed bug in pre-pass concerning escaped "normal" characters such as \x5c or
+\x20 at the start of a run of normal characters. These were being treated as
+real characters, instead of the source characters being re-checked.
+
+
+Version 0.94 18-Sep-97
+----------------------
+
+1. The functions are now thread-safe, with the caveat that the global variables
+containing pointers to malloc() and free() or alternative functions are the
+same for all threads.
+
+2. Get pcre_study() to generate a bitmap of initial characters for non-
+anchored patterns when this is possible, and use it if passed to pcre_exec().
+
+
+Version 0.93 15-Sep-97
+----------------------
+
+1. /(b)|(:+)/ was computing an incorrect first character.
+
+2. Add pcre_study() to the API and the passing of pcre_extra to pcre_exec(),
+but not actually doing anything yet.
+
+3. Treat "-" characters in classes that cannot be part of ranges as literals,
+as Perl does (e.g. [-az] or [az-]).
+
+4. Set the anchored flag if a branch starts with .* or .*? because that tests
+all possible positions.
+
+5. Split up into different modules to avoid including unneeded functions in a
+compiled binary. However, compile and exec are still in one module. The "study"
+function is split off.
+
+6. The character tables are now in a separate module whose source is generated
+by an auxiliary program - but can then be edited by hand if required. There are
+now no calls to isalnum(), isspace(), isdigit(), isxdigit(), tolower() or
+toupper() in the code.
+
+7. Turn the malloc/free funtions variables into pcre_malloc and pcre_free and
+make them global. Abolish the function for setting them, as the caller can now
+set them directly.
+
+
+Version 0.92 11-Sep-97
+----------------------
+
+1. A repeat with a fixed maximum and a minimum of 1 for an ordinary character
+(e.g. /a{1,3}/) was broken (I mis-optimized it).
+
+2. Caseless matching was not working in character classes if the characters in
+the pattern were in upper case.
+
+3. Make ranges like [W-c] work in the same way as Perl for caseless matching.
+
+4. Make PCRE_ANCHORED public and accept as a compile option.
+
+5. Add an options word to pcre_exec() and accept PCRE_ANCHORED and
+PCRE_CASELESS at run time. Add escapes \A and \I to pcretest to cause it to
+pass them.
+
+6. Give an error if bad option bits passed at compile or run time.
+
+7. Add PCRE_MULTILINE at compile and exec time, and (?m) as well. Add \M to
+pcretest to cause it to pass that flag.
+
+8. Add pcre_info(), to get the number of identifying subpatterns, the stored
+options, and the first character, if set.
+
+9. Recognize C+ or C{n,m} where n >= 1 as providing a fixed starting character.
+
+
+Version 0.91 10-Sep-97
+----------------------
+
+1. PCRE was failing to diagnose unlimited repeats of subpatterns that could
+match the empty string as in /(a*)*/. It was looping and ultimately crashing.
+
+2. PCRE was looping on encountering an indefinitely repeated back reference to
+a subpattern that had matched an empty string, e.g. /(a|)\1*/. It now does what
+Perl does - treats the match as successful.
+
+****
diff --git a/connectors/jk/native/iis/pcre/INSTALL b/connectors/jk/native/iis/pcre/INSTALL
new file mode 100644
index 0000000..0880281
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/INSTALL
@@ -0,0 +1,185 @@
+Basic Installation
+==================
+
+   These are generic installation instructions that apply to systems that
+can run the `configure' shell script - Unix systems and any that imitate
+it. They are not specific to PCRE. There are PCRE-specific instructions
+for non-Unix systems in the file NON-UNIX-USE.
+
+   The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation.  It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions.  Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, a file
+`config.cache' that saves the results of its tests to speed up
+reconfiguring, and a file `config.log' containing compiler output
+(useful mainly for debugging `configure').
+
+   If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release.  If at some point `config.cache'
+contains results you don't want to keep, you may remove or edit it.
+
+   The file `configure.in' is used to create `configure' by a program
+called `autoconf'.  You only need `configure.in' if you want to change
+it or regenerate `configure' using a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+  1. `cd' to the directory containing the package's source code and type
+     `./configure' to configure the package for your system.  If you're
+     using `csh' on an old version of System V, you might need to type
+     `sh ./configure' instead to prevent `csh' from trying to execute
+     `configure' itself.
+
+     Running `configure' takes awhile.  While running, it prints some
+     messages telling which features it is checking for.
+
+  2. Type `make' to compile the package.
+
+  3. Optionally, type `make check' to run any self-tests that come with
+     the package.
+
+  4. Type `make install' to install the programs and any data files and
+     documentation.
+
+  5. You can remove the program binaries and object files from the
+     source code directory by typing `make clean'.  To also remove the
+     files that `configure' created (so you can compile the package for
+     a different kind of computer), type `make distclean'.  There is
+     also a `make maintainer-clean' target, but that is intended mainly
+     for the package's developers.  If you use it, you may have to get
+     all sorts of other programs in order to regenerate files that came
+     with the distribution.
+
+Compilers and Options
+=====================
+
+   Some systems require unusual options for compilation or linking that
+the `configure' script does not know about.  You can give `configure'
+initial values for variables by setting them in the environment.  Using
+a Bourne-compatible shell, you can do that on the command line like
+this:
+     CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
+
+Or on systems that have the `env' program, you can do it like this:
+     env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
+
+Compiling For Multiple Architectures
+====================================
+
+   You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory.  To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'.  `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script.  `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+   If you have to use a `make' that does not supports the `VPATH'
+variable, you have to compile the package for one architecture at a time
+in the source code directory.  After you have installed the package for
+one architecture, use `make distclean' before reconfiguring for another
+architecture.
+
+Installation Names
+==================
+
+   By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc.  You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PATH'.
+
+   You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files.  If you
+give `configure' the option `--exec-prefix=PATH', the package will use
+PATH as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+   In addition, if you use an unusual directory layout you can give
+options like `--bindir=PATH' to specify different values for particular
+kinds of files.  Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+   If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+   Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System).  The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+   For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+   There may be some features `configure' can not figure out
+automatically, but needs to determine by the type of host the package
+will run on.  Usually `configure' can figure that out, but if it prints
+a message saying it can not guess the host type, give it the
+`--host=TYPE' option.  TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name with three fields:
+     CPU-COMPANY-SYSTEM
+
+See the file `config.sub' for the possible values of each field.  If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the host type.
+
+   If you are building compiler tools for cross-compiling, you can also
+use the `--target=TYPE' option to select the type of system they will
+produce code for and the `--build=TYPE' option to select the type of
+system on which you are compiling the package.
+
+Sharing Defaults
+================
+
+   If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists.  Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Operation Controls
+==================
+
+   `configure' recognizes the following options to control how it
+operates.
+
+`--cache-file=FILE'
+     Use and save the results of the tests in FILE instead of
+     `./config.cache'.  Set FILE to `/dev/null' to disable caching, for
+     debugging `configure'.
+
+`--help'
+     Print a summary of the options to `configure', and exit.
+
+`--quiet'
+`--silent'
+`-q'
+     Do not print messages saying which checks are being made.  To
+     suppress all normal output, redirect it to `/dev/null' (any error
+     messages will still be shown).
+
+`--srcdir=DIR'
+     Look for the package's source code in directory DIR.  Usually
+     `configure' can determine that directory automatically.
+
+`--version'
+     Print the version of Autoconf used to generate the `configure'
+     script, and exit.
+
+`configure' also accepts some other, not widely useful, options.
diff --git a/connectors/jk/native/iis/pcre/LICENCE b/connectors/jk/native/iis/pcre/LICENCE
new file mode 100644
index 0000000..1573583
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/LICENCE
@@ -0,0 +1,45 @@
+PCRE LICENCE
+------------
+
+PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+Release 5 of PCRE is distributed under the terms of the "BSD" licence, as
+specified below. The documentation for PCRE, supplied in the "doc"
+directory, is distributed under the same terms as the software itself.
+
+Written by: Philip Hazel <ph10@cam.ac.uk>
+
+University of Cambridge Computing Service,
+Cambridge, England. Phone: +44 1223 334714.
+
+Copyright (c) 1997-2004 University of Cambridge
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+    * Neither the name of the University of Cambridge nor the names of its
+      contributors may be used to endorse or promote products derived from
+      this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+End
diff --git a/connectors/jk/native/iis/pcre/Makefile.in b/connectors/jk/native/iis/pcre/Makefile.in
new file mode 100644
index 0000000..22eeee7
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/Makefile.in
@@ -0,0 +1,20 @@
+LTLIBRARY_NAME = libpcre.la
+LTLIBRARY_SOURCES = maketables.c get.c study.c pcre.c
+
+CLEAN_TARGETS = dftables chartables.c
+DISTCLEAN_TARGETS = pcre.h pcre-config config.h config.log config.status $(CLEAN_TARGETS)
+
+include $(top_srcdir)/build/ltlib.mk
+
+config.h:
+	touch $@
+
+$(LTLIBRARY_OBJECTS) dftables.lo: config.h
+
+dftables: dftables.lo
+	$(LINK) $(EXTRA_LDFLAGS) dftables.lo $(EXTRA_LIBS)
+
+$(srcdir)/chartables.c: dftables
+	./dftables $@
+
+pcre.lo: $(srcdir)/chartables.c
diff --git a/connectors/jk/native/iis/pcre/NEWS b/connectors/jk/native/iis/pcre/NEWS
new file mode 100644
index 0000000..e9a5cf2
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/NEWS
@@ -0,0 +1,201 @@
+News about PCRE releases
+------------------------
+
+Release 5.0 13-Sep-04
+---------------------
+
+The licence under which PCRE is released has been changed to the more
+conventional "BSD" licence.
+
+In the code, some bugs have been fixed, and there are also some major changes
+in this release (which is why I've increased the number to 5.0). Some changes
+are internal rearrangements, and some provide a number of new facilities. The
+new features are:
+
+1. There's an "automatic callout" feature that inserts callouts before every
+   item in the regex, and there's a new callout field that gives the position
+   in the pattern - useful for debugging and tracing.
+
+2. The extra_data structure can now be used to pass in a set of character
+   tables at exec time. This is useful if compiled regex are saved and re-used
+   at a later time when the tables may not be at the same address. If the
+   default internal tables are used, the pointer saved with the compiled
+   pattern is now set to NULL, which means that you don't need to do anything
+   special unless you are using custom tables.
+
+3. It is possible, with some restrictions on the content of the regex, to
+   request "partial" matching. A special return code is given if all of the
+   subject string matched part of the regex. This could be useful for testing
+   an input field as it is being typed.
+
+4. There is now some optional support for Unicode character properties, which
+   means that the patterns items such as \p{Lu} and \X can now be used. Only
+   the general category properties are supported. If PCRE is compiled with this
+   support, an additional 90K data structure is include, which increases the
+   size of the library dramatically.
+
+5. There is support for saving compiled patterns and re-using them later.
+
+6. There is support for running regular expressions that were compiled on a
+   different host with the opposite endianness.
+
+7. The pcretest program has been extended to accommodate the new features.
+
+The main internal rearrangement is that sequences of literal characters are no
+longer handled as strings. Instead, each character is handled on its own. This
+makes some UTF-8 handling easier, and makes the support of partial matching
+possible. Compiled patterns containing long literal strings will be larger as a
+result of this change; I hope that performance will not be much affected.
+
+
+Release 4.5 01-Dec-03
+---------------------
+
+Again mainly a bug-fix and tidying release, with only a couple of new features:
+
+1. It's possible now to compile PCRE so that it does not use recursive
+function calls when matching. Instead it gets memory from the heap. This slows
+things down, but may be necessary on systems with limited stacks.
+
+2. UTF-8 string checking has been tightened to reject overlong sequences and to
+check that a starting offset points to the start of a character. Failure of the
+latter returns a new error code: PCRE_ERROR_BADUTF8_OFFSET.
+
+3. PCRE can now be compiled for systems that use EBCDIC code.
+
+
+Release 4.4 21-Aug-03
+---------------------
+
+This is mainly a bug-fix and tidying release. The only new feature is that PCRE
+checks UTF-8 strings for validity by default. There is an option to suppress
+this, just in case anybody wants that teeny extra bit of performance.
+
+
+Releases 4.1 - 4.3
+------------------
+
+Sorry, I forgot about updating the NEWS file for these releases. Please take a
+look at ChangeLog.
+
+
+Release 4.0 17-Feb-03
+---------------------
+
+There have been a lot of changes for the 4.0 release, adding additional
+functionality and mending bugs. Below is a list of the highlights of the new
+functionality. For full details of these features, please consult the
+documentation. For a complete list of changes, see the ChangeLog file.
+
+1. Support for Perl's \Q...\E escapes.
+
+2. "Possessive quantifiers" ?+, *+, ++, and {,}+ which come from Sun's Java
+package. They provide some syntactic sugar for simple cases of "atomic
+grouping".
+
+3. Support for the \G assertion. It is true when the current matching position
+is at the start point of the match.
+
+4. A new feature that provides some of the functionality that Perl provides
+with (?{...}). The facility is termed a "callout". The way it is done in PCRE
+is for the caller to provide an optional function, by setting pcre_callout to
+its entry point. To get the function called, the regex must include (?C) at
+appropriate points.
+
+5. Support for recursive calls to individual subpatterns. This makes it really
+easy to get totally confused.
+
+6. Support for named subpatterns. The Python syntax (?P<name>...) is used to
+name a group.
+
+7. Several extensions to UTF-8 support; it is now fairly complete. There is an
+option for pcregrep to make it operate in UTF-8 mode.
+
+8. The single man page has been split into a number of separate man pages.
+These also give rise to individual HTML pages which are put in a separate
+directory. There is an index.html page that lists them all. Some hyperlinking
+between the pages has been installed.
+
+
+Release 3.5 15-Aug-01
+---------------------
+
+1. The configuring system has been upgraded to use later versions of autoconf
+and libtool. By default it builds both a shared and a static library if the OS
+supports it. You can use --disable-shared or --disable-static on the configure
+command if you want only one of them.
+
+2. The pcretest utility is now installed along with pcregrep because it is
+useful for users (to test regexs) and by doing this, it automatically gets
+relinked by libtool. The documentation has been turned into a man page, so
+there are now .1, .txt, and .html versions in /doc.
+
+3. Upgrades to pcregrep:
+   (i)   Added long-form option names like gnu grep.
+   (ii)  Added --help to list all options with an explanatory phrase.
+   (iii) Added -r, --recursive to recurse into sub-directories.
+   (iv)  Added -f, --file to read patterns from a file.
+
+4. Added --enable-newline-is-cr and --enable-newline-is-lf to the configure
+script, to force use of CR or LF instead of \n in the source. On non-Unix
+systems, the value can be set in config.h.
+
+5. The limit of 200 on non-capturing parentheses is a _nesting_ limit, not an
+absolute limit. Changed the text of the error message to make this clear, and
+likewise updated the man page.
+
+6. The limit of 99 on the number of capturing subpatterns has been removed.
+The new limit is 65535, which I hope will not be a "real" limit.
+
+
+Release 3.3 01-Aug-00
+---------------------
+
+There is some support for UTF-8 character strings. This is incomplete and
+experimental. The documentation describes what is and what is not implemented.
+Otherwise, this is just a bug-fixing release.
+
+
+Release 3.0 01-Feb-00
+---------------------
+
+1. A "configure" script is now used to configure PCRE for Unix systems. It
+builds a Makefile, a config.h file, and the pcre-config script.
+
+2. PCRE is built as a shared library by default.
+
+3. There is support for POSIX classes such as [:alpha:].
+
+5. There is an experimental recursion feature.
+
+----------------------------------------------------------------------------
+          IMPORTANT FOR THOSE UPGRADING FROM VERSIONS BEFORE 2.00
+
+Please note that there has been a change in the API such that a larger
+ovector is required at matching time, to provide some additional workspace.
+The new man page has details. This change was necessary in order to support
+some of the new functionality in Perl 5.005.
+
+          IMPORTANT FOR THOSE UPGRADING FROM VERSION 2.00
+
+Another (I hope this is the last!) change has been made to the API for the
+pcre_compile() function. An additional argument has been added to make it
+possible to pass over a pointer to character tables built in the current
+locale by pcre_maketables(). To use the default tables, this new arguement
+should be passed as NULL.
+
+          IMPORTANT FOR THOSE UPGRADING FROM VERSION 2.05
+
+Yet another (and again I hope this really is the last) change has been made
+to the API for the pcre_exec() function. An additional argument has been
+added to make it possible to start the match other than at the start of the
+subject string. This is important if there are lookbehinds. The new man
+page has the details, but you just want to convert existing programs, all
+you need to do is to stick in a new fifth argument to pcre_exec(), with a
+value of zero. For example, change
+
+  pcre_exec(pattern, extra, subject, length, options, ovec, ovecsize)
+to
+  pcre_exec(pattern, extra, subject, length, 0, options, ovec, ovecsize)
+
+****
diff --git a/connectors/jk/native/iis/pcre/NON-UNIX-USE b/connectors/jk/native/iis/pcre/NON-UNIX-USE
new file mode 100644
index 0000000..f6280af
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/NON-UNIX-USE
@@ -0,0 +1,244 @@
+Compiling PCRE on non-Unix systems
+----------------------------------
+
+See below for comments on Cygwin or MinGW and OpenVMS usage. I (Philip Hazel)
+have no knowledge of Windows or VMS sytems and how their libraries work. The
+items in the PCRE Makefile that relate to anything other than Unix-like systems
+have been contributed by PCRE users. There are some other comments and files in
+the Contrib directory on the ftp site that you may find useful. See
+
+  ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/Contrib
+
+If you want to compile PCRE for a non-Unix system (or perhaps, more strictly,
+for a system that does not support "configure" and "make" files), note that
+PCRE consists entirely of code written in Standard C, and so should compile
+successfully on any system that has a Standard C compiler and library.
+
+
+GENERIC INSTRUCTIONS
+
+The following are generic comments about building PCRE. The interspersed
+indented commands are suggestions from Mark Tetrode as to which commands you
+might use on a Windows system to build a static library.
+
+(1) Copy or rename the file config.in as config.h, and change the macros that
+define HAVE_STRERROR and HAVE_MEMMOVE to define them as 1 rather than 0.
+Unfortunately, because of the way Unix autoconf works, the default setting has
+to be 0. You may also want to make changes to other macros in config.h. In
+particular, if you want to force a specific value for newline, you can define
+the NEWLINE macro. The default is to use '\n', thereby using whatever value
+your compiler gives to '\n'.
+
+  rem Mark Tetrode's commands
+  copy config.in config.h
+  rem Use write, because notepad cannot handle UNIX files. Change values.
+  write config.h
+
+(2) Copy or rename the file pcre.in as pcre.h, and change the macro definitions
+for PCRE_MAJOR, PCRE_MINOR, and PCRE_DATE near its start to the values set in
+configure.in.
+
+  rem Mark Tetrode's commands
+  copy pcre.in pcre.h
+  rem Read values from configure.in
+  write configure.in
+  rem Change values
+  write pcre.h
+
+(3) Compile dftables.c as a stand-alone program, and then run it with
+the single argument "chartables.c". This generates a set of standard
+character tables and writes them to that file.
+
+  rem Mark Tetrode's commands
+  rem Compile & run
+  cl -DSUPPORT_UTF8 dftables.c
+  dftables.exe > chartables.c
+
+(4) Compile maketables.c, get.c, study.c and pcre.c and link them all
+together into an object library in whichever form your system keeps such
+libraries. This is the pcre library (chartables.c is included by means of an
+#include directive). If your system has static and shared libraries, you may
+have to do this once for each type.
+
+  rem Mark Tetrode's commands, for a static library
+  rem Compile & lib
+  cl -DSUPPORT_UTF8 -DPOSIX_MALLOC_THRESHOLD=10 /c maketables.c get.c study.c pcre.c
+  lib /OUT:pcre.lib maketables.obj get.obj study.obj pcre.obj
+
+(5) Similarly, compile pcreposix.c and link it (on its own) as the pcreposix
+library.
+
+  rem Mark Tetrode's commands, for a static library
+  rem Compile & lib
+  cl -DSUPPORT_UTF8 -DPOSIX_MALLOC_THRESHOLD=10 /c pcreposix.c
+  lib /OUT:pcreposix.lib pcreposix.obj
+
+(6) Compile the test program pcretest.c. This needs the functions in the
+pcre and pcreposix libraries when linking.
+
+  rem Mark Tetrode's commands
+  rem compile & link
+  cl pcretest.c pcre.lib pcreposix.lib
+
+(7) Run pcretest on the testinput files in the testdata directory, and check
+that the output matches the corresponding testoutput files. You must use the
+-i option when checking testinput2. Note that the supplied files are in Unix
+format, with just LF characters as line terminators. You may need to edit them
+to change this if your system uses a different convention.
+
+  rem Mark Tetrode's commands
+  rem Make a change, i.e. space, backspace, and save again - do this for all
+  rem to change UNIX to Win, \n to \n\r
+  write testoutput1
+  write testoutput2
+  write testoutput3
+  write testoutput4
+  write testoutput5
+  pcretest testdata\testinput1 testdata\myoutput1
+  windiff testdata\testoutput1 testdata\myoutput1
+  pcretest -i testdata\testinput2 testdata\myoutput2
+  windiff testdata\testoutput2 testdata\myoutput2
+  pcretest testdata\testinput3 testdata\myoutput3
+  windiff testdata\testoutput3 testdata\myoutput3
+  pcretest testdata\testinput4 testdata\myoutput4
+  windiff testdata\testoutput4 testdata\myoutput4
+  pcretest testdata\testinput5 testdata\myoutput5
+  windiff testdata\testoutput5 testdata\myoutput5
+
+
+FURTHER REMARKS
+
+If you have a system without "configure" but where you can use a Makefile, edit
+Makefile.in to create Makefile, substituting suitable values for the variables
+at the head of the file.
+
+Some help in building a Win32 DLL of PCRE in GnuWin32 environments was
+contributed by Paul Sokolovsky. These environments are Mingw32
+(http://www.xraylith.wisc.edu/~khan/software/gnu-win32/) and CygWin
+(http://sourceware.cygnus.com/cygwin/). Paul comments:
+
+  For CygWin, set CFLAGS=-mno-cygwin, and do 'make dll'. You'll get
+  pcre.dll (containing pcreposix also), libpcre.dll.a, and dynamically
+  linked pgrep and pcretest. If you have /bin/sh, run RunTest (three
+  main test go ok, locale not supported).
+
+Changes to do MinGW with autoconf 2.50 were supplied by Fred Cox
+<sailorFred@yahoo.com>, who comments as follows:
+
+  If you are using the PCRE DLL, the normal Unix style configure && make &&
+  make check && make install should just work[*]. If you want to statically
+  link against the .a file, you must define PCRE_STATIC before including
+  pcre.h, otherwise the pcre_malloc and pcre_free exported functions will be
+  declared __declspec(dllimport), with hilarious results.  See the configure.in
+  and pcretest.c for how it is done for the static test.
+
+  Also, there will only be a libpcre.la, not a libpcreposix.la, as you
+  would expect from the Unix version. The single DLL includes the pcreposix
+  interface.
+
+[*] But note that the supplied test files are in Unix format, with just LF
+characters as line terminators. You will have to edit them to change to CR LF
+terminators.
+
+A script for building PCRE using Borland's C++ compiler for use with VPASCAL
+was contributed by Alexander Tokarev. It is called makevp.bat.
+
+These are some further comments about Win32 builds from Mark Evans. They
+were contributed before Fred Cox's changes were made, so it is possible that
+they may no longer be relevant.
+
+"The documentation for Win32 builds is a bit shy.  Under MSVC6 I
+followed their instructions to the letter, but there were still
+some things missing.
+
+(1) Must #define STATIC for entire project if linking statically.
+    (I see no reason to use DLLs for code this compact.)  This of
+    course is a project setting in MSVC under Preprocessor.
+
+(2) Missing some #ifdefs relating to the function pointers
+    pcre_malloc and pcre_free.  See my solution below.  (The stubs
+    may not be mandatory but they made me feel better.)"
+
+=========================
+#ifdef _WIN32
+#include <malloc.h>
+
+void* malloc_stub(size_t N)
+{ return malloc(N); }
+void free_stub(void* p)
+{ free(p); }
+void *(*pcre_malloc)(size_t) = &malloc_stub;
+void  (*pcre_free)(void *) = &free_stub;
+
+#else
+
+void *(*pcre_malloc)(size_t) = malloc;
+void  (*pcre_free)(void *) = free;
+
+#endif
+=========================
+
+
+BUILDING PCRE ON OPENVMS
+
+Dan Mooney sent the following comments about building PCRE on OpenVMS:
+
+"It was quite easy to compile and link the library. I don't have a formal
+make file but the attached file [reproduced below] contains the OpenVMS DCL
+commands I used to build the library. I had to add #define
+POSIX_MALLOC_THRESHOLD 10 to pcre.h since it was not defined anywhere.
+
+The library was built on:
+O/S: HP OpenVMS v7.3-1
+Compiler: Compaq C v6.5-001-48BCD
+Linker: vA13-01
+
+The test results did not match 100% due to the issues you mention in your
+documentation regarding isprint(), iscntrl(), isgraph() and ispunct(). I
+modified some of the character tables temporarily and was able to get the
+results to match. Tests using the fr locale did not match since I don't have
+that locale loaded. The study size was always reported to be 3 less than the
+value in the standard test output files."
+
+=========================
+$! This DCL procedure builds PCRE on OpenVMS
+$!
+$! I followed the instructions in the non-unix-use file in the distribution.
+$!
+$ COMPILE == "CC/LIST/NOMEMBER_ALIGNMENT/PREFIX_LIBRARY_ENTRIES=ALL_ENTRIES
+$ COMPILE DFTABLES.C
+$ LINK/EXE=DFTABLES.EXE DFTABLES.OBJ
+$ RUN DFTABLES.EXE/OUTPUT=CHARTABLES.C
+$ COMPILE MAKETABLES.C
+$ COMPILE GET.C
+$ COMPILE STUDY.C
+$! I had to set POSIX_MALLOC_THRESHOLD to 10 in PCRE.H since the symbol
+$! did not seem to be defined anywhere.
+$! I edited pcre.h and added #DEFINE SUPPORT_UTF8 to enable UTF8 support.
+$ COMPILE PCRE.C
+$ LIB/CREATE PCRE MAKETABLES.OBJ, GET.OBJ, STUDY.OBJ, PCRE.OBJ
+$! I had to set POSIX_MALLOC_THRESHOLD to 10 in PCRE.H since the symbol
+$! did not seem to be defined anywhere.
+$ COMPILE PCREPOSIX.C
+$ LIB/CREATE PCREPOSIX PCREPOSIX.OBJ
+$ COMPILE PCRETEST.C
+$ LINK/EXE=PCRETEST.EXE PCRETEST.OBJ, PCRE/LIB, PCREPOSIX/LIB
+$! C programs that want access to command line arguments must be
+$! defined as a symbol
+$ PCRETEST :== "$ SYS$ROADSUSERS:[DMOONEY.REGEXP]PCRETEST.EXE"
+$! Arguments must be enclosed in quotes.
+$ PCRETEST "-C"
+$! Test results:
+$!
+$!   The test results did not match 100%. The functions isprint(), iscntrl(),
+$!   isgraph() and ispunct() on OpenVMS must not produce the same results
+$!   as the system that built the test output files provided with the
+$!   distribution.
+$!
+$!   The study size did not match and was always 3 less on OpenVMS.
+$!
+$!   Locale could not be set to fr
+$!
+=========================
+
+****
diff --git a/connectors/jk/native/iis/pcre/NWGNUmakefile b/connectors/jk/native/iis/pcre/NWGNUmakefile
new file mode 100644
index 0000000..a40897c
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/NWGNUmakefile
@@ -0,0 +1,267 @@
+#
+# NWGNUmakefile for DfTables.nlm (Apache2)
+# 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 $(APR_WORK)\build\NWGNUhead.inc
+
+PCRE		= $(AP_WORK)/srclib/pcre
+
+#
+# build this level's files
+
+FILES_prebuild_headers = \
+	$(PCRE)/config.h \
+	$(PCRE)/pcre.h \
+	$(EOLIST) 
+	
+$(PCRE)/%.h: $(subst /,\,$(PCRE))\%.hw
+	@echo Creating $(subst /,\,$@)
+	copy $< $(subst /,\,$(PCRE))\$(@F)
+
+#
+# Make sure all needed macro's are defined
+#
+
+#
+# These directories will be at the beginning of the include list, followed by
+# INCDIRS
+#
+XINCDIRS	+= \
+			$(AP_WORK)/os/netware \
+			$(APR)/include/arch/netware \
+			$(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		= dftables
+
+#
+# This is used by the link '-desc ' directive. 
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION	= Generate character tables
+
+#$(FILES_prebuild_headers)
+# This is used by the '-threadname' directive.  If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME	= dftables
+
+#
+# If this is specified, it will override VERSION value in 
+# $(APR_WORK)\build\NWGNUenvironment.inc
+#
+NLM_VERSION		= 1,0,0
+
+#
+# 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 this is specified it will be used by the link '-flags' directive
+#
+NLM_FLAGS		= PSEUDOPREEMPTION
+ 
+#
+# If this is specified it will be linked in with the XDCData option in the def 
+# file instead of the default of $(APR)/misc/netware/apr.xdc.  XDCData can 
+# be disabled by setting APACHE_UNIPROC in the environment
+#
+XDCDATA         = 
+
+#
+# Declare all target files (you must add your files here)
+#
+
+#
+# If there is an NLM target, put it here
+#
+TARGET_nlm = \
+$(OBJDIR)/dftables.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 = \
+	$(FILES_prebuild_headers) \
+	$(OBJDIR)/dftables.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 = \
+	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 = \
+	@libc.imp \
+	$(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 $(APR_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 $(APR_WORK)\build\NWGNUtail.inc
+
+# End of NWGNUmakefile for DfTables.nlm (Apache2)
diff --git a/connectors/jk/native/iis/pcre/README b/connectors/jk/native/iis/pcre/README
new file mode 100644
index 0000000..fc5397e
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/README
@@ -0,0 +1,427 @@
+README file for PCRE (Perl-compatible regular expression library)
+-----------------------------------------------------------------
+
+The latest release of PCRE is always available from
+
+  ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-xxx.tar.gz
+
+Please read the NEWS file if you are upgrading from a previous release.
+
+PCRE has its own native API, but a set of "wrapper" functions that are based on
+the POSIX API are also supplied in the library libpcreposix. Note that this
+just provides a POSIX calling interface to PCRE: the regular expressions
+themselves still follow Perl syntax and semantics. The header file
+for the POSIX-style functions is called pcreposix.h. The official POSIX name is
+regex.h, but I didn't want to risk possible problems with existing files of
+that name by distributing it that way. To use it with an existing program that
+uses the POSIX API, it will have to be renamed or pointed at by a link.
+
+If you are using the POSIX interface to PCRE and there is already a POSIX regex
+library installed on your system, you must take care when linking programs to
+ensure that they link with PCRE's libpcreposix library. Otherwise they may pick
+up the "real" POSIX functions of the same name.
+
+
+Documentation for PCRE
+----------------------
+
+If you install PCRE in the normal way, you will end up with an installed set of
+man pages whose names all start with "pcre". The one that is called "pcre"
+lists all the others. In addition to these man pages, the PCRE documentation is
+supplied in two other forms; however, as there is no standard place to install
+them, they are left in the doc directory of the unpacked source distribution.
+These forms are:
+
+  1. Files called doc/pcre.txt, doc/pcregrep.txt, and doc/pcretest.txt. The
+     first of these is a concatenation of the text forms of all the section 3
+     man pages except those that summarize individual functions. The other two
+     are the text forms of the section 1 man pages for the pcregrep and
+     pcretest commands. Text forms are provided for ease of scanning with text
+     editors or similar tools.
+
+  2. A subdirectory called doc/html contains all the documentation in HTML
+     form, hyperlinked in various ways, and rooted in a file called
+     doc/index.html.
+
+
+Contributions by users of PCRE
+------------------------------
+
+You can find contributions from PCRE users in the directory
+
+  ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/Contrib
+
+where there is also a README file giving brief descriptions of what they are.
+Several of them provide support for compiling PCRE on various flavours of
+Windows systems (I myself do not use Windows). Some are complete in themselves;
+others are pointers to URLs containing relevant files.
+
+
+Building PCRE on a Unix-like system
+-----------------------------------
+
+To build PCRE on a Unix-like system, first run the "configure" command from the
+PCRE distribution directory, with your current directory set to the directory
+where you want the files to be created. This command is a standard GNU
+"autoconf" configuration script, for which generic instructions are supplied in
+INSTALL.
+
+Most commonly, people build PCRE within its own distribution directory, and in
+this case, on many systems, just running "./configure" is sufficient, but the
+usual methods of changing standard defaults are available. For example:
+
+CFLAGS='-O2 -Wall' ./configure --prefix=/opt/local
+
+specifies that the C compiler should be run with the flags '-O2 -Wall' instead
+of the default, and that "make install" should install PCRE under /opt/local
+instead of the default /usr/local.
+
+If you want to build in a different directory, just run "configure" with that
+directory as current. For example, suppose you have unpacked the PCRE source
+into /source/pcre/pcre-xxx, but you want to build it in /build/pcre/pcre-xxx:
+
+cd /build/pcre/pcre-xxx
+/source/pcre/pcre-xxx/configure
+
+There are some optional features that can be included or omitted from the PCRE
+library. You can read more about them in the pcrebuild man page.
+
+. If you want to make use of the support for UTF-8 character strings in PCRE,
+  you must add --enable-utf8 to the "configure" command. Without it, the code
+  for handling UTF-8 is not included in the library. (Even when included, it
+  still has to be enabled by an option at run time.)
+
+. If, in addition to support for UTF-8 character strings, you want to include
+  support for the \P, \p, and \X sequences that recognize Unicode character
+  properties, you must add --enable-unicode-properties to the "configure"
+  command. This adds about 90K to the size of the library (in the form of a
+  property table); only the basic two-letter properties such as Lu are
+  supported.
+
+. You can build PCRE to recognized CR or NL as the newline character, instead
+  of whatever your compiler uses for "\n", by adding --newline-is-cr or
+  --newline-is-nl to the "configure" command, respectively. Only do this if you
+  really understand what you are doing. On traditional Unix-like systems, the
+  newline character is NL.
+
+. When called via the POSIX interface, PCRE uses malloc() to get additional
+  storage for processing capturing parentheses if there are more than 10 of
+  them. You can increase this threshold by setting, for example,
+
+  --with-posix-malloc-threshold=20
+
+  on the "configure" command.
+
+. PCRE has a counter which can be set to limit the amount of resources it uses.
+  If the limit is exceeded during a match, the match fails. The default is ten
+  million. You can change the default by setting, for example,
+
+  --with-match-limit=500000
+
+  on the "configure" command. This is just the default; individual calls to
+  pcre_exec() can supply their own value. There is discussion on the pcreapi
+  man page.
+
+. The default maximum compiled pattern size is around 64K. You can increase
+  this by adding --with-link-size=3 to the "configure" command. You can
+  increase it even more by setting --with-link-size=4, but this is unlikely
+  ever to be necessary. If you build PCRE with an increased link size, test 2
+  (and 5 if you are using UTF-8) will fail. Part of the output of these tests
+  is a representation of the compiled pattern, and this changes with the link
+  size.
+
+. You can build PCRE so that its match() function does not call itself
+  recursively. Instead, it uses blocks of data from the heap via special
+  functions pcre_stack_malloc() and pcre_stack_free() to save data that would
+  otherwise be saved on the stack. To build PCRE like this, use
+
+  --disable-stack-for-recursion
+
+  on the "configure" command. PCRE runs more slowly in this mode, but it may be
+  necessary in environments with limited stack sizes.
+
+The "configure" script builds seven files:
+
+. pcre.h is build by copying pcre.in and making substitutions
+. Makefile is built by copying Makefile.in and making substitutions.
+. config.h is built by copying config.in and making substitutions.
+. pcre-config is built by copying pcre-config.in and making substitutions.
+. libpcre.pc is data for the pkg-config command, built from libpcre.pc.in
+. libtool is a script that builds shared and/or static libraries
+. RunTest is a script for running tests
+
+Once "configure" has run, you can run "make". It builds two libraries called
+libpcre and libpcreposix, a test program called pcretest, and the pcregrep
+command. You can use "make install" to copy these, the public header files
+pcre.h and pcreposix.h, and the man pages to appropriate live directories on
+your system, in the normal way.
+
+
+Retrieving configuration information on Unix-like systems
+---------------------------------------------------------
+
+Running "make install" also installs the command pcre-config, which can be used
+to recall information about the PCRE configuration and installation. For
+example:
+
+  pcre-config --version
+
+prints the version number, and
+
+  pcre-config --libs
+
+outputs information about where the library is installed. This command can be
+included in makefiles for programs that use PCRE, saving the programmer from
+having to remember too many details.
+
+The pkg-config command is another system for saving and retrieving information
+about installed libraries. Instead of separate commands for each library, a
+single command is used. For example:
+
+  pkg-config --cflags pcre
+
+The data is held in *.pc files that are installed in a directory called
+pkgconfig.
+
+
+Shared libraries on Unix-like systems
+-------------------------------------
+
+The default distribution builds PCRE as two shared libraries and two static
+libraries, as long as the operating system supports shared libraries. Shared
+library support relies on the "libtool" script which is built as part of the
+"configure" process.
+
+The libtool script is used to compile and link both shared and static
+libraries. They are placed in a subdirectory called .libs when they are newly
+built. The programs pcretest and pcregrep are built to use these uninstalled
+libraries (by means of wrapper scripts in the case of shared libraries). When
+you use "make install" to install shared libraries, pcregrep and pcretest are
+automatically re-built to use the newly installed shared libraries before being
+installed themselves. However, the versions left in the source directory still
+use the uninstalled libraries.
+
+To build PCRE using static libraries only you must use --disable-shared when
+configuring it. For example:
+
+./configure --prefix=/usr/gnu --disable-shared
+
+Then run "make" in the usual way. Similarly, you can use --disable-static to
+build only shared libraries.
+
+
+Cross-compiling on a Unix-like system
+-------------------------------------
+
+You can specify CC and CFLAGS in the normal way to the "configure" command, in
+order to cross-compile PCRE for some other host. However, during the building
+process, the dftables.c source file is compiled *and run* on the local host, in
+order to generate the default character tables (the chartables.c file). It
+therefore needs to be compiled with the local compiler, not the cross compiler.
+You can do this by specifying CC_FOR_BUILD (and if necessary CFLAGS_FOR_BUILD)
+when calling the "configure" command. If they are not specified, they default
+to the values of CC and CFLAGS.
+
+
+Building on non-Unix systems
+----------------------------
+
+For a non-Unix system, read the comments in the file NON-UNIX-USE, though if
+the system supports the use of "configure" and "make" you may be able to build
+PCRE in the same way as for Unix systems.
+
+PCRE has been compiled on Windows systems and on Macintoshes, but I don't know
+the details because I don't use those systems. It should be straightforward to
+build PCRE on any system that has a Standard C compiler, because it uses only
+Standard C functions.
+
+
+Testing PCRE
+------------
+
+To test PCRE on a Unix system, run the RunTest script that is created by the
+configuring process. (This can also be run by "make runtest", "make check", or
+"make test".) For other systems, see the instructions in NON-UNIX-USE.
+
+The script runs the pcretest test program (which is documented in its own man
+page) on each of the testinput files (in the testdata directory) in turn,
+and compares the output with the contents of the corresponding testoutput file.
+A file called testtry is used to hold the main output from pcretest
+(testsavedregex is also used as a working file). To run pcretest on just one of
+the test files, give its number as an argument to RunTest, for example:
+
+  RunTest 2
+
+The first file can also be fed directly into the perltest script to check that
+Perl gives the same results. The only difference you should see is in the first
+few lines, where the Perl version is given instead of the PCRE version.
+
+The second set of tests check pcre_fullinfo(), pcre_info(), pcre_study(),
+pcre_copy_substring(), pcre_get_substring(), pcre_get_substring_list(), error
+detection, and run-time flags that are specific to PCRE, as well as the POSIX
+wrapper API. It also uses the debugging flag to check some of the internals of
+pcre_compile().
+
+If you build PCRE with a locale setting that is not the standard C locale, the
+character tables may be different (see next paragraph). In some cases, this may
+cause failures in the second set of tests. For example, in a locale where the
+isprint() function yields TRUE for characters in the range 128-255, the use of
+[:isascii:] inside a character class defines a different set of characters, and
+this shows up in this test as a difference in the compiled code, which is being
+listed for checking. Where the comparison test output contains [\x00-\x7f] the
+test will contain [\x00-\xff], and similarly in some other cases. This is not a
+bug in PCRE.
+
+The third set of tests checks pcre_maketables(), the facility for building a
+set of character tables for a specific locale and using them instead of the
+default tables. The tests make use of the "fr_FR" (French) locale. Before
+running the test, the script checks for the presence of this locale by running
+the "locale" command. If that command fails, or if it doesn't include "fr_FR"
+in the list of available locales, the third test cannot be run, and a comment
+is output to say why. If running this test produces instances of the error
+
+  ** Failed to set locale "fr_FR"
+
+in the comparison output, it means that locale is not available on your system,
+despite being listed by "locale". This does not mean that PCRE is broken.
+
+The fourth test checks the UTF-8 support. It is not run automatically unless
+PCRE is built with UTF-8 support. To do this you must set --enable-utf8 when
+running "configure". This file can be also fed directly to the perltest script,
+provided you are running Perl 5.8 or higher. (For Perl 5.6, a small patch,
+commented in the script, can be be used.)
+
+The fifth test checks error handling with UTF-8 encoding, and internal UTF-8
+features of PCRE that are not relevant to Perl.
+
+The sixth and final test checks the support for Unicode character properties.
+It it not run automatically unless PCRE is built with Unicode property support.
+To to this you must set --enable-unicode-properties when running "configure".
+
+
+Character tables
+----------------
+
+PCRE uses four tables for manipulating and identifying characters whose values
+are less than 256. The final argument of the pcre_compile() function is a
+pointer to a block of memory containing the concatenated tables. A call to
+pcre_maketables() can be used to generate a set of tables in the current
+locale. If the final argument for pcre_compile() is passed as NULL, a set of
+default tables that is built into the binary is used.
+
+The source file called chartables.c contains the default set of tables. This is
+not supplied in the distribution, but is built by the program dftables
+(compiled from dftables.c), which uses the ANSI C character handling functions
+such as isalnum(), isalpha(), isupper(), islower(), etc. to build the table
+sources. This means that the default C locale which is set for your system will
+control the contents of these default tables. You can change the default tables
+by editing chartables.c and then re-building PCRE. If you do this, you should
+probably also edit Makefile to ensure that the file doesn't ever get
+re-generated.
+
+The first two 256-byte tables provide lower casing and case flipping functions,
+respectively. The next table consists of three 32-byte bit maps which identify
+digits, "word" characters, and white space, respectively. These are used when
+building 32-byte bit maps that represent character classes.
+
+The final 256-byte table has bits indicating various character types, as
+follows:
+
+    1   white space character
+    2   letter
+    4   decimal digit
+    8   hexadecimal digit
+   16   alphanumeric or '_'
+  128   regular expression metacharacter or binary zero
+
+You should not alter the set of characters that contain the 128 bit, as that
+will cause PCRE to malfunction.
+
+
+Manifest
+--------
+
+The distribution should contain the following files:
+
+(A) The actual source files of the PCRE library functions and their
+    headers:
+
+  dftables.c            auxiliary program for building chartables.c
+
+  get.c                 )
+  maketables.c          )
+  study.c               ) source of the functions
+  pcre.c                )   in the library
+  pcreposix.c           )
+  printint.c            )
+
+  ucp.c                 )
+  ucp.h                 ) source for the code that is used for
+  ucpinternal.h         )   Unicode property handling
+  ucptable.c            )
+  ucptypetable.c        )
+
+  pcre.in               "source" for the header for the external API; pcre.h
+                          is built from this by "configure"
+  pcreposix.h           header for the external POSIX wrapper API
+  internal.h            header for internal use
+  config.in             template for config.h, which is built by configure
+
+(B) Auxiliary files:
+
+  AUTHORS               information about the author of PCRE
+  ChangeLog             log of changes to the code
+  INSTALL               generic installation instructions
+  LICENCE               conditions for the use of PCRE
+  COPYING               the same, using GNU's standard name
+  Makefile.in           template for Unix Makefile, which is built by configure
+  NEWS                  important changes in this release
+  NON-UNIX-USE          notes on building PCRE on non-Unix systems
+  README                this file
+  RunTest.in            template for a Unix shell script for running tests
+  config.guess          ) files used by libtool,
+  config.sub            )   used only when building a shared library
+  configure             a configuring shell script (built by autoconf)
+  configure.in          the autoconf input used to build configure
+  doc/Tech.Notes        notes on the encoding
+  doc/*.3               man page sources for the PCRE functions
+  doc/*.1               man page sources for pcregrep and pcretest
+  doc/html/*            HTML documentation
+  doc/pcre.txt          plain text version of the man pages
+  doc/pcretest.txt      plain text documentation of test program
+  doc/perltest.txt      plain text documentation of Perl test program
+  install-sh            a shell script for installing files
+  libpcre.pc.in         "source" for libpcre.pc for pkg-config
+  ltmain.sh             file used to build a libtool script
+  mkinstalldirs         script for making install directories
+  pcretest.c            comprehensive test program
+  pcredemo.c            simple demonstration of coding calls to PCRE
+  perltest              Perl test program
+  pcregrep.c            source of a grep utility that uses PCRE
+  pcre-config.in        source of script which retains PCRE information
+  testdata/testinput1   test data, compatible with Perl
+  testdata/testinput2   test data for error messages and non-Perl things
+  testdata/testinput3   test data for locale-specific tests
+  testdata/testinput4   test data for UTF-8 tests compatible with Perl
+  testdata/testinput5   test data for other UTF-8 tests
+  testdata/testinput6   test data for Unicode property support tests
+  testdata/testoutput1  test results corresponding to testinput1
+  testdata/testoutput2  test results corresponding to testinput2
+  testdata/testoutput3  test results corresponding to testinput3
+  testdata/testoutput4  test results corresponding to testinput4
+  testdata/testoutput5  test results corresponding to testinput5
+  testdata/testoutput6  test results corresponding to testinput6
+
+(C) Auxiliary files for Win32 DLL
+
+  dll.mk
+  libpcre.def
+  libpcreposix.def
+  pcre.def
+
+(D) Auxiliary file for VPASCAL
+
+  makevp.bat
+
+Philip Hazel <ph10@cam.ac.uk>
+September 2004
diff --git a/connectors/jk/native/iis/pcre/RunTest.in b/connectors/jk/native/iis/pcre/RunTest.in
new file mode 100755
index 0000000..5e945e1
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/RunTest.in
@@ -0,0 +1,192 @@
+#! /bin/sh
+
+# This file is generated by configure from RunTest.in. Make any changes
+# to that file.
+
+# Run PCRE tests
+
+cf=diff
+testdata=@top_srcdir@/testdata
+
+# Select which tests to run; if no selection, run all
+
+do1=no
+do2=no
+do3=no
+do4=no
+do5=no
+do6=no
+
+while [ $# -gt 0 ] ; do
+  case $1 in
+    1) do1=yes;;
+    2) do2=yes;;
+    3) do3=yes;;
+    4) do4=yes;;
+    5) do5=yes;; 
+    6) do6=yes;; 
+    *) echo "Unknown test number $1"; exit 1;;
+  esac
+  shift
+done
+
+if [ "@LINK_SIZE@" != "" -a "@LINK_SIZE@" != "-DLINK_SIZE=2" ] ; then
+  if [ $do2 = yes ] ; then
+    echo "Can't run test 2 with an internal link size other than 2"
+    exit 1  
+  fi   
+  if [ $do5 = yes ] ; then
+    echo "Can't run test 5 with an internal link size other than 2"
+    exit 1  
+  fi   
+  if [ $do6 = yes ] ; then
+    echo "Can't run test 6 with an internal link size other than 2"
+    exit 1  
+  fi   
+fi
+
+if [ "@UTF8@" = "" ] ; then
+  if [ $do4 = yes ] ; then
+    echo "Can't run test 4 because UTF-8 support is not configured"
+    exit 1
+  fi   
+  if [ $do5 = yes ] ; then
+    echo "Can't run test 5 because UTF-8 support is not configured"
+    exit 1
+  fi   
+  if [ $do6 = yes ] ; then
+    echo "Can't run test 6 because UTF-8 support is not configured"
+    exit 1
+  fi   
+fi    
+
+if [ "@UCP@" = "" ] ; then
+  if [ $do6 = yes ] ; then
+    echo "Can't run test 6 because Unicode property support is not configured"
+    exit 1
+  fi
+fi      
+
+if [ $do1 = no -a $do2 = no -a $do3 = no -a $do4 = no -a \
+     $do5 = no -a $do6 = no ] ; then
+  do1=yes
+  do2=yes 
+  do3=yes
+  if [ "@UTF8@" != "" ] ; then do4=yes; fi
+  if [ "@UTF8@" != "" ] ; then do5=yes; fi
+  if [ "@UTF8@" != "" -a "@UCP@" != "" ] ; then do6=yes; fi
+fi
+
+# Show which release
+
+./pcretest /dev/null
+
+# Primary test, Perl-compatible
+
+if [ $do1 = yes ] ; then
+  echo "Test 1: main functionality (Perl compatible)"
+  ./pcretest $testdata/testinput1 testtry
+  if [ $? = 0 ] ; then
+    $cf testtry $testdata/testoutput1
+    if [ $? != 0 ] ; then exit 1; fi
+    echo " " 
+  else exit 1
+  fi
+fi
+
+# PCRE tests that are not Perl-compatible - API & error tests, mostly
+
+if [ $do2 = yes ] ; then
+  if [ "@LINK_SIZE@" = "" -o "@LINK_SIZE@" = "-DLINK_SIZE=2" ] ; then   
+    echo "Test 2: API and error handling (not Perl compatible)"
+    ./pcretest -i $testdata/testinput2 testtry
+    if [ $? = 0 ] ; then
+      $cf testtry $testdata/testoutput2
+      if [ $? != 0 ] ; then exit 1; fi
+    else exit 1
+    fi
+  else
+    echo Test 2 skipped for link size other than 2 \(@LINK_SIZE@\)    
+  fi   
+fi
+
+if [ $do1 = yes -a $do2 = yes ] ; then
+  echo " " 
+  echo "The two main tests ran OK"
+  echo " " 
+fi
+
+# Locale-specific tests, provided the "fr_FR" locale is available
+
+if [ $do3 = yes ] ; then
+  locale -a | grep '^fr_FR$' >/dev/null
+  if [ $? -eq 0 ] ; then
+    echo "Test 3: locale-specific features (using 'fr_FR' locale)"
+    ./pcretest $testdata/testinput3 testtry
+    if [ $? = 0 ] ; then
+      $cf testtry $testdata/testoutput3
+      if [ $? != 0 ] ; then 
+        echo " "
+        echo "Locale test did not run entirely successfully."
+        echo "This usually means that there is a problem with the locale"
+        echo "settings rather than a bug in PCRE."    
+      else
+      echo "Locale test ran OK" 
+      fi 
+      echo " " 
+    else exit 1
+    fi
+  else
+    echo "Cannot test locale-specific features - 'fr_FR' locale not found,"
+    echo "or the \"locale\" command is not available to check for it."
+    echo " " 
+  fi
+fi
+
+# Additional tests for UTF8 support
+
+if [ $do4 = yes ] ; then
+  echo "Test 4: UTF-8 support (Perl compatible)"
+  ./pcretest $testdata/testinput4 testtry 
+  if [ $? = 0 ] ; then
+    $cf testtry $testdata/testoutput4
+    if [ $? != 0 ] ; then exit 1; fi
+  else exit 1
+  fi
+  echo "UTF8 test ran OK"
+  echo " "
+fi
+
+if [ $do5 = yes ] ; then
+  if [ "@LINK_SIZE@" = "" -o "@LINK_SIZE@" = "-DLINK_SIZE=2" ] ; then   
+    echo "Test 5: API and internals for UTF-8 support (not Perl compatible)"
+    ./pcretest $testdata/testinput5 testtry 
+    if [ $? = 0 ] ; then
+      $cf testtry $testdata/testoutput5
+      if [ $? != 0 ] ; then exit 1; fi
+    else exit 1
+    fi
+    echo "UTF8 internals test ran OK"
+    echo " "
+  else
+    echo Test 5 skipped for link size other than 2 \(@LINK_SIZE@\)    
+  fi   
+fi
+
+if [ $do6 = yes ] ; then
+  if [ "@LINK_SIZE@" = "" -o "@LINK_SIZE@" = "-DLINK_SIZE=2" ] ; then   
+    echo "Test 6: Unicode property support"
+    ./pcretest $testdata/testinput6 testtry 
+    if [ $? = 0 ] ; then
+      $cf testtry $testdata/testoutput6
+      if [ $? != 0 ] ; then exit 1; fi
+    else exit 1
+    fi
+    echo "Unicode properties test ran OK"
+    echo " "
+  else   
+    echo Test 6 skipped for link size other than 2 \(@LINK_SIZE@\)    
+  fi   
+fi
+
+# End
diff --git a/connectors/jk/native/iis/pcre/config.hw b/connectors/jk/native/iis/pcre/config.hw
new file mode 100644
index 0000000..5427366
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/config.hw
@@ -0,0 +1,107 @@
+
+/* On Unix systems config.in is converted by configure into config.h. PCRE is
+written in Standard C, but there are a few non-standard things it can cope
+with, allowing it to run on SunOS4 and other "close to standard" systems.
+
+On a non-Unix system you should just copy this file into config.h, and set up
+the macros the way you need them. You should normally change the definitions of
+HAVE_STRERROR and HAVE_MEMMOVE to 1. Unfortunately, because of the way autoconf
+works, these cannot be made the defaults. If your system has bcopy() and not
+memmove(), change the definition of HAVE_BCOPY instead of HAVE_MEMMOVE. If your
+system has neither bcopy() nor memmove(), leave them both as 0; an emulation
+function will be used. */
+
+/* If you are compiling for a system that uses EBCDIC instead of ASCII
+character codes, define this macro as 1. On systems that can use "configure",
+this can be done via --enable-ebcdic. */
+
+#ifndef EBCDIC
+#define EBCDIC 0
+#endif
+
+/* If you are compiling for a system that needs some magic to be inserted
+before the definition of an exported function, define this macro to contain the
+relevant magic. It apears at the start of every exported function. */
+
+#define EXPORT
+
+/* Define to empty if the "const" keyword does not work. */
+
+#undef const
+
+/* Define to "unsigned" if <stddef.h> doesn't define size_t. */
+
+#undef size_t
+
+/* The following two definitions are mainly for the benefit of SunOS4, which
+doesn't have the strerror() or memmove() functions that should be present in
+all Standard C libraries. The macros HAVE_STRERROR and HAVE_MEMMOVE should
+normally be defined with the value 1 for other systems, but unfortunately we
+can't make this the default because "configure" files generated by autoconf
+will only change 0 to 1; they won't change 1 to 0 if the functions are not
+found. */
+
+#define HAVE_STRERROR 1
+#define HAVE_MEMMOVE  1
+
+/* There are some non-Unix systems that don't even have bcopy(). If this macro
+is false, an emulation is used. If HAVE_MEMMOVE is set to 1, the value of
+HAVE_BCOPY is not relevant. */
+
+#define HAVE_BCOPY 0
+
+/* The value of NEWLINE determines the newline character. The default is to
+leave it up to the compiler, but some sites want to force a particular value.
+On Unix systems, "configure" can be used to override this default. */
+
+#ifndef NEWLINE
+#define NEWLINE '\n'
+#endif
+
+/* The value of LINK_SIZE determines the number of bytes used to store
+links as offsets within the compiled regex. The default is 2, which allows for
+compiled patterns up to 64K long. This covers the vast majority of cases.
+However, PCRE can also be compiled to use 3 or 4 bytes instead. This allows for
+longer patterns in extreme cases. On Unix systems, "configure" can be used to
+override this default. */
+
+#ifndef LINK_SIZE
+#define LINK_SIZE   2
+#endif
+
+/* The value of MATCH_LIMIT determines the default number of times the match()
+function can be called during a single execution of pcre_exec(). (There is a
+runtime method of setting a different limit.) The limit exists in order to
+catch runaway regular expressions that take for ever to determine that they do
+not match. The default is set very large so that it does not accidentally catch
+legitimate cases. On Unix systems, "configure" can be used to override this
+default default. */
+
+#ifndef MATCH_LIMIT
+#define MATCH_LIMIT 10000000
+#endif
+
+/* When calling PCRE via the POSIX interface, additional working storage is
+required for holding the pointers to capturing substrings because PCRE requires
+three integers per substring, whereas the POSIX interface provides only two. If
+the number of expected substrings is small, the wrapper function uses space on
+the stack, because this is faster than using malloc() for each call. The
+threshold above which the stack is no longer use is defined by POSIX_MALLOC_
+THRESHOLD. On Unix systems, "configure" can be used to override this default.
+*/
+
+#ifndef POSIX_MALLOC_THRESHOLD
+#define POSIX_MALLOC_THRESHOLD 10
+#endif
+
+/* PCRE uses recursive function calls to handle backtracking while matching.
+This can sometimes be a problem on systems that have stacks of limited size.
+Define NO_RECURSE to get a version that doesn't use recursion in the match()
+function; instead it creates its own stack by steam using pcre_recurse_malloc
+to get memory. For more detail, see comments and other stuff just above the
+match() function. On Unix systems, "configure" can be used to set this in the
+Makefile (use --disable-stack-for-recursion). */
+
+/* #define NO_RECURSE */
+
+/* End */
diff --git a/connectors/jk/native/iis/pcre/config.in b/connectors/jk/native/iis/pcre/config.in
new file mode 100644
index 0000000..fc17ddd
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/config.in
@@ -0,0 +1,107 @@
+
+/* On Unix systems config.in is converted by configure into config.h. PCRE is
+written in Standard C, but there are a few non-standard things it can cope
+with, allowing it to run on SunOS4 and other "close to standard" systems.
+
+On a non-Unix system you should just copy this file into config.h, and set up
+the macros the way you need them. You should normally change the definitions of
+HAVE_STRERROR and HAVE_MEMMOVE to 1. Unfortunately, because of the way autoconf
+works, these cannot be made the defaults. If your system has bcopy() and not
+memmove(), change the definition of HAVE_BCOPY instead of HAVE_MEMMOVE. If your
+system has neither bcopy() nor memmove(), leave them both as 0; an emulation
+function will be used. */
+
+/* If you are compiling for a system that uses EBCDIC instead of ASCII
+character codes, define this macro as 1. On systems that can use "configure",
+this can be done via --enable-ebcdic. */
+
+#ifndef EBCDIC
+#define EBCDIC 0
+#endif
+
+/* If you are compiling for a system that needs some magic to be inserted
+before the definition of an exported function, define this macro to contain the
+relevant magic. It apears at the start of every exported function. */
+
+#define EXPORT
+
+/* Define to empty if the "const" keyword does not work. */
+
+#undef const
+
+/* Define to "unsigned" if <stddef.h> doesn't define size_t. */
+
+#undef size_t
+
+/* The following two definitions are mainly for the benefit of SunOS4, which
+doesn't have the strerror() or memmove() functions that should be present in
+all Standard C libraries. The macros HAVE_STRERROR and HAVE_MEMMOVE should
+normally be defined with the value 1 for other systems, but unfortunately we
+can't make this the default because "configure" files generated by autoconf
+will only change 0 to 1; they won't change 1 to 0 if the functions are not
+found. */
+
+#define HAVE_STRERROR 0
+#define HAVE_MEMMOVE  0
+
+/* There are some non-Unix systems that don't even have bcopy(). If this macro
+is false, an emulation is used. If HAVE_MEMMOVE is set to 1, the value of
+HAVE_BCOPY is not relevant. */
+
+#define HAVE_BCOPY    0
+
+/* The value of NEWLINE determines the newline character. The default is to
+leave it up to the compiler, but some sites want to force a particular value.
+On Unix systems, "configure" can be used to override this default. */
+
+#ifndef NEWLINE
+#define NEWLINE '\n'
+#endif
+
+/* The value of LINK_SIZE determines the number of bytes used to store
+links as offsets within the compiled regex. The default is 2, which allows for
+compiled patterns up to 64K long. This covers the vast majority of cases.
+However, PCRE can also be compiled to use 3 or 4 bytes instead. This allows for
+longer patterns in extreme cases. On Unix systems, "configure" can be used to
+override this default. */
+
+#ifndef LINK_SIZE
+#define LINK_SIZE   2
+#endif
+
+/* The value of MATCH_LIMIT determines the default number of times the match()
+function can be called during a single execution of pcre_exec(). (There is a
+runtime method of setting a different limit.) The limit exists in order to
+catch runaway regular expressions that take for ever to determine that they do
+not match. The default is set very large so that it does not accidentally catch
+legitimate cases. On Unix systems, "configure" can be used to override this
+default default. */
+
+#ifndef MATCH_LIMIT
+#define MATCH_LIMIT 10000000
+#endif
+
+/* When calling PCRE via the POSIX interface, additional working storage is
+required for holding the pointers to capturing substrings because PCRE requires
+three integers per substring, whereas the POSIX interface provides only two. If
+the number of expected substrings is small, the wrapper function uses space on
+the stack, because this is faster than using malloc() for each call. The
+threshold above which the stack is no longer use is defined by POSIX_MALLOC_
+THRESHOLD. On Unix systems, "configure" can be used to override this default.
+*/
+
+#ifndef POSIX_MALLOC_THRESHOLD
+#define POSIX_MALLOC_THRESHOLD 10
+#endif
+
+/* PCRE uses recursive function calls to handle backtracking while matching.
+This can sometimes be a problem on systems that have stacks of limited size.
+Define NO_RECURSE to get a version that doesn't use recursion in the match()
+function; instead it creates its own stack by steam using pcre_recurse_malloc
+to get memory. For more detail, see comments and other stuff just above the
+match() function. On Unix systems, "configure" can be used to set this in the
+Makefile (use --disable-stack-for-recursion). */
+
+/* #define NO_RECURSE */
+
+/* End */
diff --git a/connectors/jk/native/iis/pcre/configure.in b/connectors/jk/native/iis/pcre/configure.in
new file mode 100644
index 0000000..4f8d031
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/configure.in
@@ -0,0 +1,185 @@
+dnl Process this file with autoconf to produce a configure script.
+
+dnl This is required at the start; the name is the name of a file
+dnl it should be seeing, to verify it is in the same directory.
+
+AC_INIT(dftables.c)
+
+dnl A safety precaution
+
+AC_PREREQ(2.57)
+
+dnl Arrange to build config.h from config.in. Note that pcre.h is
+dnl built differently, as it is just a "substitution" file.
+dnl Manual says this macro should come right after AC_INIT.
+AC_CONFIG_HEADER(config.h:config.in)
+
+dnl Provide the current PCRE version information. Do not use numbers
+dnl with leading zeros for the minor version, as they end up in a C
+dnl macro, and may be treated as octal constants. Stick to single
+dnl digits for minor numbers less than 10. There are unlikely to be
+dnl that many releases anyway.
+
+PCRE_MAJOR=5
+PCRE_MINOR=0
+PCRE_DATE=13-Sep-2004
+PCRE_VERSION=${PCRE_MAJOR}.${PCRE_MINOR}
+
+dnl Default values for miscellaneous macros
+
+POSIX_MALLOC_THRESHOLD=-DPOSIX_MALLOC_THRESHOLD=10
+
+dnl Provide versioning information for libtool shared libraries that
+dnl are built by default on Unix systems.
+
+PCRE_LIB_VERSION=0:1:0
+PCRE_POSIXLIB_VERSION=0:0:0
+
+dnl Checks for programs.
+
+AC_PROG_CC
+
+dnl Checks for header files.
+
+AC_HEADER_STDC
+AC_CHECK_HEADERS(limits.h)
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+
+AC_C_CONST
+AC_TYPE_SIZE_T
+
+dnl Checks for library functions.
+
+AC_CHECK_FUNCS(bcopy memmove strerror)
+
+dnl Handle --enable-shared-libraries
+
+LIBTOOL=./libtool
+LIBSUFFIX=la
+AC_ARG_ENABLE(shared,
+[  --disable-shared        build PCRE as a static library],
+if test "$enableval" = "no"; then
+  LIBTOOL=
+  LIBSUFFIX=a
+fi
+)
+
+dnl Handle --enable-utf8
+
+AC_ARG_ENABLE(utf8,
+[  --enable-utf8           enable UTF8 support],
+if test "$enableval" = "yes"; then
+  UTF8=-DSUPPORT_UTF8
+fi
+)
+
+dnl Handle --enable-unicode-properties
+
+AC_ARG_ENABLE(unicode-properties,
+[  --enable-unicode-properties  enable Unicode properties support],
+if test "$enableval" = "yes"; then
+  UCP=-DSUPPORT_UCP
+fi
+)
+
+dnl Handle --enable-newline-is-cr
+
+AC_ARG_ENABLE(newline-is-cr,
+[  --enable-newline-is-cr  use CR as the newline character],
+if test "$enableval" = "yes"; then
+  NEWLINE=-DNEWLINE=13
+fi
+)
+
+dnl Handle --enable-newline-is-lf
+
+AC_ARG_ENABLE(newline-is-lf,
+[  --enable-newline-is-lf  use LF as the newline character],
+if test "$enableval" = "yes"; then
+  NEWLINE=-DNEWLINE=10
+fi
+)
+
+dnl Handle --enable-ebcdic
+
+AC_ARG_ENABLE(ebcdic,
+[  --enable-ebcdic         assume EBCDIC coding rather than ASCII],
+if test "$enableval" == "yes"; then
+  EBCDIC=-DEBCDIC=1
+fi
+)
+
+dnl Handle --disable-stack-for-recursion
+
+AC_ARG_ENABLE(stack-for-recursion,
+[  --disable-stack-for-recursion  disable use of stack recursion when matching],
+if test "$enableval" = "no"; then
+  NO_RECURSE=-DNO_RECURSE
+fi
+)
+
+dnl There doesn't seem to be a straightforward way of having parameters
+dnl that set values, other than fudging the --with thing. So that's what
+dnl I've done.
+
+dnl Handle --with-posix-malloc-threshold=n
+
+AC_ARG_WITH(posix-malloc-threshold,
+[  --with-posix-malloc-threshold=5  threshold for POSIX malloc usage],
+  POSIX_MALLOC_THRESHOLD=-DPOSIX_MALLOC_THRESHOLD=$withval
+)
+
+dnl Handle --with-link-size=n
+
+AC_ARG_WITH(link-size,
+[  --with-link-size=2    internal link size (2, 3, or 4 allowed)],
+  LINK_SIZE=-DLINK_SIZE=$withval
+)
+
+dnl Handle --with-match_limit=n
+
+AC_ARG_WITH(match-limit,
+[  --with-match-limit=10000000      default limit on internal looping)],
+  MATCH_LIMIT=-DMATCH_LIMIT=$withval
+)
+
+dnl Unicode character property support implies UTF-8 support
+
+if test "$UCP" != "" ; then
+  UTF8=-DSUPPORT_UTF8
+fi
+
+dnl "Export" these variables
+
+AC_SUBST(BUILD_EXEEXT)
+AC_SUBST(BUILD_OBJEXT)
+AC_SUBST(CC_FOR_BUILD)
+AC_SUBST(CFLAGS_FOR_BUILD)
+AC_SUBST(EBCDIC)
+AC_SUBST(HAVE_MEMMOVE)
+AC_SUBST(HAVE_STRERROR)
+AC_SUBST(LIBTOOL)
+AC_SUBST(LIBSUFFIX)
+AC_SUBST(LINK_SIZE)
+AC_SUBST(MATCH_LIMIT)
+AC_SUBST(NEWLINE)
+AC_SUBST(NO_RECURSE)
+AC_SUBST(PCRE_MAJOR)
+AC_SUBST(PCRE_MINOR)
+AC_SUBST(PCRE_DATE)
+AC_SUBST(PCRE_VERSION)
+AC_SUBST(PCRE_LIB_VERSION)
+AC_SUBST(PCRE_POSIXLIB_VERSION)
+AC_SUBST(POSIX_MALLOC_THRESHOLD)
+AC_SUBST(UCP)
+AC_SUBST(UTF8)
+
+
+if test "x$enable_shared" = "xno" ; then
+    AC_DEFINE([PCRE_STATIC],[1],[to link statically])
+fi
+
+dnl This must be last; it determines what files are written as well as config.h
+AC_OUTPUT(Makefile pcre.h:pcre.in pcre-config,[chmod a+x pcre-config])
+
diff --git a/connectors/jk/native/iis/pcre/dftables.c b/connectors/jk/native/iis/pcre/dftables.c
new file mode 100644
index 0000000..8458c60
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/dftables.c
@@ -0,0 +1,173 @@
+/*************************************************
+*      Perl-Compatible Regular Expressions       *
+*************************************************/
+
+/*
+PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+Written by: Philip Hazel <ph10@cam.ac.uk>
+
+           Copyright (c) 1997-2004 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+    * Neither the name of the University of Cambridge nor the names of its
+      contributors may be used to endorse or promote products derived from
+      this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+
+/* This is a support program to generate the file chartables.c, containing
+character tables of various kinds. They are built according to the default C
+locale and used as the default tables by PCRE. Now that pcre_maketables is
+a function visible to the outside world, we make use of its code from here in
+order to be consistent. */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "internal.h"
+
+#define DFTABLES          /* maketables.c notices this */
+#include "maketables.c"
+
+
+int main(int argc, char **argv)
+{
+int i;
+FILE *f;
+const unsigned char *tables = pcre_maketables();
+
+if (argc != 2)
+  {
+  fprintf(stderr, "dftables: one filename argument is required\n");
+  return 1;
+  }
+
+f = fopen(argv[1], "w");
+if (f == NULL)
+  {
+  fprintf(stderr, "dftables: failed to open %s for writing\n", argv[1]);
+  return 1;
+  }
+
+/* There are two fprintf() calls here, because gcc in pedantic mode complains
+about the very long string otherwise. */
+
+fprintf(f,
+  "/*************************************************\n"
+  "*      Perl-Compatible Regular Expressions       *\n"
+  "*************************************************/\n\n"
+  "/* This file is automatically written by the dftables auxiliary \n"
+  "program. If you edit it by hand, you might like to edit the Makefile to \n"
+  "prevent its ever being regenerated.\n\n");
+fprintf(f,
+  "This file is #included in the compilation of pcre.c to build the default\n"
+  "character tables which are used when no tables are passed to the compile\n"
+  "function. */\n\n"
+  "static unsigned char pcre_default_tables[] = {\n\n"
+  "/* This table is a lower casing table. */\n\n");
+
+fprintf(f, "  ");
+for (i = 0; i < 256; i++)
+  {
+  if ((i & 7) == 0 && i != 0) fprintf(f, "\n  ");
+  fprintf(f, "%3d", *tables++);
+  if (i != 255) fprintf(f, ",");
+  }
+fprintf(f, ",\n\n");
+
+fprintf(f, "/* This table is a case flipping table. */\n\n");
+
+fprintf(f, "  ");
+for (i = 0; i < 256; i++)
+  {
+  if ((i & 7) == 0 && i != 0) fprintf(f, "\n  ");
+  fprintf(f, "%3d", *tables++);
+  if (i != 255) fprintf(f, ",");
+  }
+fprintf(f, ",\n\n");
+
+fprintf(f,
+  "/* This table contains bit maps for various character classes.\n"
+  "Each map is 32 bytes long and the bits run from the least\n"
+  "significant end of each byte. The classes that have their own\n"
+  "maps are: space, xdigit, digit, upper, lower, word, graph\n"
+  "print, punct, and cntrl. Other classes are built from combinations. */\n\n");
+
+fprintf(f, "  ");
+for (i = 0; i < cbit_length; i++)
+  {
+  if ((i & 7) == 0 && i != 0)
+    {
+    if ((i & 31) == 0) fprintf(f, "\n");
+    fprintf(f, "\n  ");
+    }
+  fprintf(f, "0x%02x", *tables++);
+  if (i != cbit_length - 1) fprintf(f, ",");
+  }
+fprintf(f, ",\n\n");
+
+fprintf(f,
+  "/* This table identifies various classes of character by individual bits:\n"
+  "  0x%02x   white space character\n"
+  "  0x%02x   letter\n"
+  "  0x%02x   decimal digit\n"
+  "  0x%02x   hexadecimal digit\n"
+  "  0x%02x   alphanumeric or '_'\n"
+  "  0x%02x   regular expression metacharacter or binary zero\n*/\n\n",
+  ctype_space, ctype_letter, ctype_digit, ctype_xdigit, ctype_word,
+  ctype_meta);
+
+fprintf(f, "  ");
+for (i = 0; i < 256; i++)
+  {
+  if ((i & 7) == 0 && i != 0)
+    {
+    fprintf(f, " /* ");
+    if (isprint(i-8)) fprintf(f, " %c -", i-8);
+      else fprintf(f, "%3d-", i-8);
+    if (isprint(i-1)) fprintf(f, " %c ", i-1);
+      else fprintf(f, "%3d", i-1);
+    fprintf(f, " */\n  ");
+    }
+  fprintf(f, "0x%02x", *tables++);
+  if (i != 255) fprintf(f, ",");
+  }
+
+fprintf(f, "};/* ");
+if (isprint(i-8)) fprintf(f, " %c -", i-8);
+  else fprintf(f, "%3d-", i-8);
+if (isprint(i-1)) fprintf(f, " %c ", i-1);
+  else fprintf(f, "%3d", i-1);
+fprintf(f, " */\n\n/* End of chartables.c */\n");
+
+fclose(f);
+return 0;
+}
+
+/* End of dftables.c */
diff --git a/connectors/jk/native/iis/pcre/dftables.dsp b/connectors/jk/native/iis/pcre/dftables.dsp
new file mode 100644
index 0000000..4a67c15
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/dftables.dsp
@@ -0,0 +1,165 @@
+# Microsoft Developer Studio Project File - Name="dftables" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=dftables - 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 "dftables.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 "dftables.mak" CFG="dftables - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "dftables - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "dftables - 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)" == "dftables - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ""
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ""
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "_WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Fd"Release\dftables" /FD /c
+# ADD CPP /nologo /MD /W3 /wd4996 /O2 /D "_WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Fd"Release\dftables" /FD /c
+# 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 /nologo /subsystem:console /pdb:"Release\dftables.pdb"
+# SUBTRACT BASE LINK32 /pdb:none
+# ADD LINK32 kernel32.lib /nologo /subsystem:console /pdb:"Release\dftables.pdb" /opt:ref
+# SUBTRACT LINK32 /pdb:none
+
+!ELSEIF  "$(CFG)" == "dftables - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ""
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ""
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "_WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Fd"Debug\dftables" /FD /c
+# ADD CPP /nologo /MDd /W3 /wd4996 /EHsc /Zi /Od /D "_WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Fd"Debug\dftables" /FD /c
+# 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 /nologo /subsystem:console /incremental:no /pdb:"Debug\dftables.pdb" /debug /pdbtype:sept
+# SUBTRACT BASE LINK32 /pdb:none
+# ADD LINK32 kernel32.lib /nologo /subsystem:console /incremental:no /pdb:"Debug\dftables.pdb" /debug
+# SUBTRACT LINK32 /pdb:none
+
+!ENDIF 
+
+# Begin Target
+
+# Name "dftables - Win32 Release"
+# Name "dftables - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\dftables.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hw"
+# Begin Source File
+
+SOURCE=.\config.hw
+
+!IF  "$(CFG)" == "dftables - Win32 Release"
+
+# Begin Custom Build - Creating pcre config.h from config.hw 
+InputPath=.\config.hw
+
+".\config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	type .\config.hw > .\config.h
+	
+# End Custom Build
+
+!ELSEIF  "$(CFG)" == "dftables - Win32 Debug"
+
+# Begin Custom Build - Creating pcre config.h from config.hw 
+InputPath=.\config.hw
+
+".\config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	type .\config.hw > .\config.h
+	
+# End Custom Build
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\internal.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\maketables.c
+# PROP Exclude_From_Build 1
+# End Source File
+# Begin Source File
+
+SOURCE=.\pcre.hw
+
+!IF  "$(CFG)" == "dftables - Win32 Release"
+
+# Begin Custom Build - Creating pcre.h from pcre.hw 
+InputPath=.\pcre.hw
+
+".\pcre.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	type .\pcre.hw > .\pcre.h
+	
+# End Custom Build
+
+!ELSEIF  "$(CFG)" == "dftables - Win32 Debug"
+
+# Begin Custom Build - Creating pcre.h from pcre.hw 
+InputPath=.\pcre.hw
+
+".\pcre.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	type .\pcre.hw > .\pcre.h
+	
+# End Custom Build
+
+!ENDIF 
+
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/connectors/jk/native/iis/pcre/dftables.x86 b/connectors/jk/native/iis/pcre/dftables.x86
new file mode 100644
index 0000000..00df5a0
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/dftables.x86
@@ -0,0 +1,94 @@
+CPP=cl.exe
+RSC=rc.exe
+OUTDIR=.
+INTDIR=.\Release_x86
+# Begin Custom Macros
+OutDir=.
+# End Custom Macros
+
+ALL : "$(OUTDIR)\dftables.exe"
+
+
+CLEAN :
+	-@erase "$(INTDIR)\dftables.idb"
+	-@erase "$(INTDIR)\dftables.obj"
+	-@erase "$(OUTDIR)\dftables.exe"
+
+"$(INTDIR)" :
+    if not exist "$(INTDIR)/$(NULL)" mkdir "$(INTDIR)"
+
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\dftables.bsc" 
+BSC32_SBRS= \
+	
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib /nologo /subsystem:console /incremental:no /pdb:"$(OUTDIR)\Release\dftables.pdb" /machine:I386 /out:"$(OUTDIR)\dftables.exe" /opt:ref 
+LINK32_OBJS= \
+	"$(INTDIR)\dftables.obj"
+
+"$(OUTDIR)\dftables.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+CPP_PROJ=/nologo /MD /W3 /O2 /D "_WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\dftables" /FD /wd4996 /c 
+
+.c{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cpp{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cxx{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.c{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cpp{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cxx{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+
+!IF "$(CFG)" == "dftables_x86"
+SOURCE=.\dftables.c
+
+"$(INTDIR)\dftables.obj" : $(SOURCE) "$(INTDIR)" ".\config.h" ".\pcre.h"
+
+
+SOURCE=.\config.hw
+InputPath=.\config.hw
+
+".\config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	<<tempfile.bat 
+	@echo off 
+	type .\config.hw > .\config.h
+<< 
+	
+SOURCE=.\maketables.c
+SOURCE=.\pcre.hw
+InputPath=.\pcre.hw
+
+".\pcre.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	<<tempfile.bat 
+	@echo off 
+	type .\pcre.hw > .\pcre.h
+<< 
+	
+
+!ENDIF 
+
diff --git a/connectors/jk/native/iis/pcre/dll.mk b/connectors/jk/native/iis/pcre/dll.mk
new file mode 100644
index 0000000..d8b728e
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/dll.mk
@@ -0,0 +1,60 @@
+# dll.mk - auxilary Makefile to easy build dll's for mingw32 target
+# ver. 0.6 of 1999-03-25
+#
+# Homepage of this makefile - http://www.is.lg.ua/~paul/devel/
+# Homepage of original mingw32 project -
+#		      http://www.fu.is.saga-u.ac.jp/~colin/gcc.html
+#
+# How to use:
+# This makefile can:
+# 1. Create automatical .def file from list of objects
+# 2. Create .dll from objects and .def file, either automatical, or your
+#    hand-written (maybe) file, which must have same basename as dll
+# WARNING! There MUST be object, which name match dll's name. Make sux.
+# 3. Create import library from .def (as for .dll, only its name required,
+#    not dll itself)
+#    By convention implibs for dll have .dll.a suffix, e.g. libstuff.dll.a
+#    Why not just libstuff.a? 'Cos that's name for static lib, ok?
+# Process divided into 3 phases because:
+# 1. Pre-existent .def possible
+# 2. Generating implib is enough time-consuming
+#
+# Variables:
+#   DLL_LDLIBS  - libs for linking dll
+#   DLL_LDFLAGS - flags for linking dll
+#
+# By using $(DLL_SUFFIX) instead of 'dll', e.g. stuff.$(DLL_SUFFIX)
+# you may help porting makefiles to other platforms
+#
+# Put this file in your make's include path (e.g. main include dir, for
+# more information see include section in make doc). Put in the beginning
+# of your own Makefile line "include dll.mk". Specify dependences, e.g.:
+#
+# Do all stuff in one step
+# libstuff.dll.a: $(OBJECTS) stuff.def
+# stuff.def: $(OBJECTS)
+#
+# Steps separated, pre-provided .def, link with user32
+#
+# DLL_LDLIBS=-luser32
+# stuff.dll: $(OBJECTS)
+# libstuff.dll.a: $(OBJECTS)
+
+
+DLLWRAP=dllwrap
+DLLTOOL=dlltool
+
+DLL_SUFFIX=dll
+
+.SUFFIXES: .o .$(DLL_SUFFIX)
+
+_%.def: %.o
+      $(DLLTOOL) --export-all --output-def $@ $^
+
+%.$(DLL_SUFFIX): %.o
+      $(DLLWRAP) --dllname $(notdir $@) --driver-name $(CC) --def $*.def -o $@ $(filter %.o,$^) $(DLL_LDFLAGS) $(DLL_LDLIBS)
+
+lib%.$(DLL_SUFFIX).a:%.def
+      $(DLLTOOL) --dllname $(notdir $*.dll) --def $< --output-lib $@
+
+# End
diff --git a/connectors/jk/native/iis/pcre/doc/README_httpd b/connectors/jk/native/iis/pcre/doc/README_httpd
new file mode 100644
index 0000000..322c39c
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/doc/README_httpd
@@ -0,0 +1,6 @@
+The documentation directory has been omitted from this copy of PCRE
+inside the httpd codebase because it's huge--over a megabyte of PCRE docs.
+
+The PCRE documentation directory is available in unmodified form in the
+vendor branch.  You can access it via web browser or Subversion checkout at
+http://svn.apache.org/repos/asf/httpd/httpd/vendor/pcre/current/doc/
diff --git a/connectors/jk/native/iis/pcre/get.c b/connectors/jk/native/iis/pcre/get.c
new file mode 100644
index 0000000..225843e
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/get.c
@@ -0,0 +1,357 @@
+/*************************************************
+*      Perl-Compatible Regular Expressions       *
+*************************************************/
+
+/*
+This is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language. See
+the file Tech.Notes for some information on the internals.
+
+Written by: Philip Hazel <ph10@cam.ac.uk>
+
+           Copyright (c) 1997-2003 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+    * Neither the name of the University of Cambridge nor the names of its
+      contributors may be used to endorse or promote products derived from
+      this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+/* This module contains some convenience functions for extracting substrings
+from the subject string after a regex match has succeeded. The original idea
+for these functions came from Scott Wimer. */
+
+
+/* Include the internals header, which itself includes Standard C headers plus
+the external pcre header. */
+
+#include "internal.h"
+
+
+/*************************************************
+*           Find number for named string         *
+*************************************************/
+
+/* This function is used by the two extraction functions below, as well
+as being generally available.
+
+Arguments:
+  code        the compiled regex
+  stringname  the name whose number is required
+
+Returns:      the number of the named parentheses, or a negative number
+                (PCRE_ERROR_NOSUBSTRING) if not found
+*/
+
+int
+pcre_get_stringnumber(const pcre *code, const char *stringname)
+{
+int rc;
+int entrysize;
+int top, bot;
+uschar *nametable;
+
+if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMECOUNT, &top)) != 0)
+  return rc;
+if (top <= 0) return PCRE_ERROR_NOSUBSTRING;
+
+if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMEENTRYSIZE, &entrysize)) != 0)
+  return rc;
+if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMETABLE, &nametable)) != 0)
+  return rc;
+
+bot = 0;
+while (top > bot)
+  {
+  int mid = (top + bot) / 2;
+  uschar *entry = nametable + entrysize*mid;
+  int c = strcmp(stringname, (char *)(entry + 2));
+  if (c == 0) return (entry[0] << 8) + entry[1];
+  if (c > 0) bot = mid + 1; else top = mid;
+  }
+
+return PCRE_ERROR_NOSUBSTRING;
+}
+
+
+
+/*************************************************
+*      Copy captured string to given buffer      *
+*************************************************/
+
+/* This function copies a single captured substring into a given buffer.
+Note that we use memcpy() rather than strncpy() in case there are binary zeros
+in the string.
+
+Arguments:
+  subject        the subject string that was matched
+  ovector        pointer to the offsets table
+  stringcount    the number of substrings that were captured
+                   (i.e. the yield of the pcre_exec call, unless
+                   that was zero, in which case it should be 1/3
+                   of the offset table size)
+  stringnumber   the number of the required substring
+  buffer         where to put the substring
+  size           the size of the buffer
+
+Returns:         if successful:
+                   the length of the copied string, not including the zero
+                   that is put on the end; can be zero
+                 if not successful:
+                   PCRE_ERROR_NOMEMORY (-6) buffer too small
+                   PCRE_ERROR_NOSUBSTRING (-7) no such captured substring
+*/
+
+int
+pcre_copy_substring(const char *subject, int *ovector, int stringcount,
+  int stringnumber, char *buffer, int size)
+{
+int yield;
+if (stringnumber < 0 || stringnumber >= stringcount)
+  return PCRE_ERROR_NOSUBSTRING;
+stringnumber *= 2;
+yield = ovector[stringnumber+1] - ovector[stringnumber];
+if (size < yield + 1) return PCRE_ERROR_NOMEMORY;
+memcpy(buffer, subject + ovector[stringnumber], yield);
+buffer[yield] = 0;
+return yield;
+}
+
+
+
+/*************************************************
+*   Copy named captured string to given buffer   *
+*************************************************/
+
+/* This function copies a single captured substring into a given buffer,
+identifying it by name.
+
+Arguments:
+  code           the compiled regex
+  subject        the subject string that was matched
+  ovector        pointer to the offsets table
+  stringcount    the number of substrings that were captured
+                   (i.e. the yield of the pcre_exec call, unless
+                   that was zero, in which case it should be 1/3
+                   of the offset table size)
+  stringname     the name of the required substring
+  buffer         where to put the substring
+  size           the size of the buffer
+
+Returns:         if successful:
+                   the length of the copied string, not including the zero
+                   that is put on the end; can be zero
+                 if not successful:
+                   PCRE_ERROR_NOMEMORY (-6) buffer too small
+                   PCRE_ERROR_NOSUBSTRING (-7) no such captured substring
+*/
+
+int
+pcre_copy_named_substring(const pcre *code, const char *subject, int *ovector,
+  int stringcount, const char *stringname, char *buffer, int size)
+{
+int n = pcre_get_stringnumber(code, stringname);
+if (n <= 0) return n;
+return pcre_copy_substring(subject, ovector, stringcount, n, buffer, size);
+}
+
+
+
+/*************************************************
+*      Copy all captured strings to new store    *
+*************************************************/
+
+/* This function gets one chunk of store and builds a list of pointers and all
+of the captured substrings in it. A NULL pointer is put on the end of the list.
+
+Arguments:
+  subject        the subject string that was matched
+  ovector        pointer to the offsets table
+  stringcount    the number of substrings that were captured
+                   (i.e. the yield of the pcre_exec call, unless
+                   that was zero, in which case it should be 1/3
+                   of the offset table size)
+  listptr        set to point to the list of pointers
+
+Returns:         if successful: 0
+                 if not successful:
+                   PCRE_ERROR_NOMEMORY (-6) failed to get store
+*/
+
+int
+pcre_get_substring_list(const char *subject, int *ovector, int stringcount,
+  const char ***listptr)
+{
+int i;
+int size = sizeof(char *);
+int double_count = stringcount * 2;
+char **stringlist;
+char *p;
+
+for (i = 0; i < double_count; i += 2)
+  size += sizeof(char *) + ovector[i+1] - ovector[i] + 1;
+
+stringlist = (char **)(pcre_malloc)(size);
+if (stringlist == NULL) return PCRE_ERROR_NOMEMORY;
+
+*listptr = (const char **)stringlist;
+p = (char *)(stringlist + stringcount + 1);
+
+for (i = 0; i < double_count; i += 2)
+  {
+  int len = ovector[i+1] - ovector[i];
+  memcpy(p, subject + ovector[i], len);
+  *stringlist++ = p;
+  p += len;
+  *p++ = 0;
+  }
+
+*stringlist = NULL;
+return 0;
+}
+
+
+
+/*************************************************
+*   Free store obtained by get_substring_list    *
+*************************************************/
+
+/* This function exists for the benefit of people calling PCRE from non-C
+programs that can call its functions, but not free() or (pcre_free)() directly.
+
+Argument:   the result of a previous pcre_get_substring_list()
+Returns:    nothing
+*/
+
+void
+pcre_free_substring_list(const char **pointer)
+{
+(pcre_free)((void *)pointer);
+}
+
+
+
+/*************************************************
+*      Copy captured string to new store         *
+*************************************************/
+
+/* This function copies a single captured substring into a piece of new
+store
+
+Arguments:
+  subject        the subject string that was matched
+  ovector        pointer to the offsets table
+  stringcount    the number of substrings that were captured
+                   (i.e. the yield of the pcre_exec call, unless
+                   that was zero, in which case it should be 1/3
+                   of the offset table size)
+  stringnumber   the number of the required substring
+  stringptr      where to put a pointer to the substring
+
+Returns:         if successful:
+                   the length of the string, not including the zero that
+                   is put on the end; can be zero
+                 if not successful:
+                   PCRE_ERROR_NOMEMORY (-6) failed to get store
+                   PCRE_ERROR_NOSUBSTRING (-7) substring not present
+*/
+
+int
+pcre_get_substring(const char *subject, int *ovector, int stringcount,
+  int stringnumber, const char **stringptr)
+{
+int yield;
+char *substring;
+if (stringnumber < 0 || stringnumber >= stringcount)
+  return PCRE_ERROR_NOSUBSTRING;
+stringnumber *= 2;
+yield = ovector[stringnumber+1] - ovector[stringnumber];
+substring = (char *)(pcre_malloc)(yield + 1);
+if (substring == NULL) return PCRE_ERROR_NOMEMORY;
+memcpy(substring, subject + ovector[stringnumber], yield);
+substring[yield] = 0;
+*stringptr = substring;
+return yield;
+}
+
+
+
+/*************************************************
+*   Copy named captured string to new store      *
+*************************************************/
+
+/* This function copies a single captured substring, identified by name, into
+new store.
+
+Arguments:
+  code           the compiled regex
+  subject        the subject string that was matched
+  ovector        pointer to the offsets table
+  stringcount    the number of substrings that were captured
+                   (i.e. the yield of the pcre_exec call, unless
+                   that was zero, in which case it should be 1/3
+                   of the offset table size)
+  stringname     the name of the required substring
+  stringptr      where to put the pointer
+
+Returns:         if successful:
+                   the length of the copied string, not including the zero
+                   that is put on the end; can be zero
+                 if not successful:
+                   PCRE_ERROR_NOMEMORY (-6) couldn't get memory
+                   PCRE_ERROR_NOSUBSTRING (-7) no such captured substring
+*/
+
+int
+pcre_get_named_substring(const pcre *code, const char *subject, int *ovector,
+  int stringcount, const char *stringname, const char **stringptr)
+{
+int n = pcre_get_stringnumber(code, stringname);
+if (n <= 0) return n;
+return pcre_get_substring(subject, ovector, stringcount, n, stringptr);
+}
+
+
+
+
+/*************************************************
+*       Free store obtained by get_substring     *
+*************************************************/
+
+/* This function exists for the benefit of people calling PCRE from non-C
+programs that can call its functions, but not free() or (pcre_free)() directly.
+
+Argument:   the result of a previous pcre_get_substring()
+Returns:    nothing
+*/
+
+void
+pcre_free_substring(const char *pointer)
+{
+(pcre_free)((void *)pointer);
+}
+
+/* End of get.c */
diff --git a/connectors/jk/native/iis/pcre/install-sh b/connectors/jk/native/iis/pcre/install-sh
new file mode 100755
index 0000000..e9de238
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/install-sh
@@ -0,0 +1,251 @@
+#!/bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission.  M.I.T. makes no representations about the
+# suitability of this software for any purpose.  It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.  It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# 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}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+    case $1 in
+	-c) instcmd="$cpprog"
+	    shift
+	    continue;;
+
+	-d) dir_arg=true
+	    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;;
+
+	-t=*) transformarg=`echo $1 | sed 's/-t=//'`
+	    shift
+	    continue;;
+
+	-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+	    shift
+	    continue;;
+
+	*)  if [ x"$src" = x ]
+	    then
+		src=$1
+	    else
+		# this colon is to work around a 386BSD /bin/sh bug
+		:
+		dst=$1
+	    fi
+	    shift
+	    continue;;
+    esac
+done
+
+if [ x"$src" = x ]
+then
+	echo "install:	no input file specified"
+	exit 1
+else
+	true
+fi
+
+if [ x"$dir_arg" != x ]; then
+	dst=$src
+	src=""
+	
+	if [ -d $dst ]; then
+		instcmd=:
+		chmodcmd=""
+	else
+		instcmd=mkdir
+	fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad 
+# if $src (and thus $dsttmp) contains '*'.
+
+	if [ -f $src -o -d $src ]
+	then
+		true
+	else
+		echo "install:  $src does not exist"
+		exit 1
+	fi
+	
+	if [ x"$dst" = x ]
+	then
+		echo "install:	no destination specified"
+		exit 1
+	else
+		true
+	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`
+	else
+		true
+	fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+#  this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='	
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+	pathcomp="${pathcomp}${1}"
+	shift
+
+	if [ ! -d "${pathcomp}" ] ;
+        then
+		$mkdirprog "${pathcomp}"
+	else
+		true
+	fi
+
+	pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+	$doit $instcmd $dst &&
+
+	if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+	if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+	if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+	if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+	if [ x"$transformarg" = x ] 
+	then
+		dstfile=`basename $dst`
+	else
+		dstfile=`basename $dst $transformbasename | 
+			sed $transformarg`$transformbasename
+	fi
+
+# don't allow the sed command to completely eliminate the filename
+
+	if [ x"$dstfile" = x ] 
+	then
+		dstfile=`basename $dst`
+	else
+		true
+	fi
+
+# Make a temp file name in the proper directory.
+
+	dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+	$doit $instcmd $src $dsttmp &&
+
+	trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing.  If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+	if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+	if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+	if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+	if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+	$doit $rmcmd -f $dstdir/$dstfile &&
+	$doit $mvcmd $dsttmp $dstdir/$dstfile 
+
+fi &&
+
+
+exit 0
diff --git a/connectors/jk/native/iis/pcre/internal.h b/connectors/jk/native/iis/pcre/internal.h
new file mode 100644
index 0000000..5d14331
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/internal.h
@@ -0,0 +1,752 @@
+/*************************************************
+*      Perl-Compatible Regular Expressions       *
+*************************************************/
+
+
+/* This is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language. See
+the file doc/Tech.Notes for some information on the internals.
+
+Written by: Philip Hazel <ph10@cam.ac.uk>
+
+           Copyright (c) 1997-2004 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+    * Neither the name of the University of Cambridge nor the names of its
+      contributors may be used to endorse or promote products derived from
+      this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+/* This header contains definitions that are shared between the different
+modules, but which are not relevant to the outside. */
+
+/* Get the definitions provided by running "configure" */
+
+#include "config.h"
+
+/* Standard C headers plus the external interface definition. The only time
+setjmp and stdarg are used is when NO_RECURSE is set. */
+
+#include <ctype.h>
+#include <limits.h>
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef PCRE_SPY
+#define PCRE_DEFINITION       /* Win32 __declspec(export) trigger for .dll */
+#endif
+
+/* We need to have types that specify unsigned 16-bit and 32-bit integers. We
+cannot determine these outside the compilation (e.g. by running a program as
+part of "configure") because PCRE is often cross-compiled for use on other
+systems. Instead we make use of the maximum sizes that are available at
+preprocessor time in standard C environments. */
+
+#if USHRT_MAX == 65535
+  typedef unsigned short pcre_uint16;
+#elif UINT_MAX == 65535
+  typedef unsigned int pcre_uint16;
+#else
+  #error Cannot determine a type for 16-bit unsigned integers
+#endif
+
+#if UINT_MAX == 4294967295
+  typedef unsigned int pcre_uint32;
+#elif ULONG_MAX == 4294967295
+  typedef unsigned long int pcre_uint32;
+#else
+  #error Cannot determine a type for 32-bit unsigned integers
+#endif
+
+/* All character handling must be done as unsigned characters. Otherwise there
+are problems with top-bit-set characters and functions such as isspace().
+However, we leave the interface to the outside world as char *, because that
+should make things easier for callers. We define a short type for unsigned char
+to save lots of typing. I tried "uchar", but it causes problems on Digital
+Unix, where it is defined in sys/types, so use "uschar" instead. */
+
+typedef unsigned char uschar;
+
+/* Include the public PCRE header */
+
+#include "pcre.h"
+
+/* When compiling for use with the Virtual Pascal compiler, these functions
+need to have their names changed. PCRE must be compiled with the -DVPCOMPAT
+option on the command line. */
+
+#ifdef VPCOMPAT
+#define strncmp(s1,s2,m) _strncmp(s1,s2,m)
+#define memcpy(d,s,n)    _memcpy(d,s,n)
+#define memmove(d,s,n)   _memmove(d,s,n)
+#define memset(s,c,n)    _memset(s,c,n)
+#else  /* VPCOMPAT */
+
+/* To cope with SunOS4 and other systems that lack memmove() but have bcopy(),
+define a macro for memmove() if HAVE_MEMMOVE is false, provided that HAVE_BCOPY
+is set. Otherwise, include an emulating function for those systems that have
+neither (there some non-Unix environments where this is the case). This assumes
+that all calls to memmove are moving strings upwards in store, which is the
+case in PCRE. */
+
+#if ! HAVE_MEMMOVE
+#undef  memmove        /* some systems may have a macro */
+#if HAVE_BCOPY
+#define memmove(a, b, c) bcopy(b, a, c)
+#else  /* HAVE_BCOPY */
+void *
+pcre_memmove(unsigned char *dest, const unsigned char *src, size_t n)
+{
+int i;
+dest += n;
+src += n;
+for (i = 0; i < n; ++i) *(--dest) =  *(--src);
+}
+#define memmove(a, b, c) pcre_memmove(a, b, c)
+#endif   /* not HAVE_BCOPY */
+#endif   /* not HAVE_MEMMOVE */
+#endif   /* not VPCOMPAT */
+
+
+/* PCRE keeps offsets in its compiled code as 2-byte quantities (always stored
+in big-endian order) by default. These are used, for example, to link from the
+start of a subpattern to its alternatives and its end. The use of 2 bytes per
+offset limits the size of the compiled regex to around 64K, which is big enough
+for almost everybody. However, I received a request for an even bigger limit.
+For this reason, and also to make the code easier to maintain, the storing and
+loading of offsets from the byte string is now handled by the macros that are
+defined here.
+
+The macros are controlled by the value of LINK_SIZE. This defaults to 2 in
+the config.h file, but can be overridden by using -D on the command line. This
+is automated on Unix systems via the "configure" command. */
+
+#if LINK_SIZE == 2
+
+#define PUT(a,n,d)   \
+  (a[n] = (d) >> 8), \
+  (a[(n)+1] = (d) & 255)
+
+#define GET(a,n) \
+  (((a)[n] << 8) | (a)[(n)+1])
+
+#define MAX_PATTERN_SIZE (1 << 16)
+
+
+#elif LINK_SIZE == 3
+
+#define PUT(a,n,d)       \
+  (a[n] = (d) >> 16),    \
+  (a[(n)+1] = (d) >> 8), \
+  (a[(n)+2] = (d) & 255)
+
+#define GET(a,n) \
+  (((a)[n] << 16) | ((a)[(n)+1] << 8) | (a)[(n)+2])
+
+#define MAX_PATTERN_SIZE (1 << 24)
+
+
+#elif LINK_SIZE == 4
+
+#define PUT(a,n,d)        \
+  (a[n] = (d) >> 24),     \
+  (a[(n)+1] = (d) >> 16), \
+  (a[(n)+2] = (d) >> 8),  \
+  (a[(n)+3] = (d) & 255)
+
+#define GET(a,n) \
+  (((a)[n] << 24) | ((a)[(n)+1] << 16) | ((a)[(n)+2] << 8) | (a)[(n)+3])
+
+#define MAX_PATTERN_SIZE (1 << 30)   /* Keep it positive */
+
+
+#else
+#error LINK_SIZE must be either 2, 3, or 4
+#endif
+
+
+/* Convenience macro defined in terms of the others */
+
+#define PUTINC(a,n,d)   PUT(a,n,d), a += LINK_SIZE
+
+
+/* PCRE uses some other 2-byte quantities that do not change when the size of
+offsets changes. There are used for repeat counts and for other things such as
+capturing parenthesis numbers in back references. */
+
+#define PUT2(a,n,d)   \
+  a[n] = (d) >> 8; \
+  a[(n)+1] = (d) & 255
+
+#define GET2(a,n) \
+  (((a)[n] << 8) | (a)[(n)+1])
+
+#define PUT2INC(a,n,d)  PUT2(a,n,d), a += 2
+
+
+/* In case there is no definition of offsetof() provided - though any proper
+Standard C system should have one. */
+
+#ifndef offsetof
+#define offsetof(p_type,field) ((size_t)&(((p_type *)0)->field))
+#endif
+
+
+/* These are the public options that can change during matching. */
+
+#define PCRE_IMS (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL)
+
+/* Private options flags start at the most significant end of the four bytes,
+but skip the top bit so we can use ints for convenience without getting tangled
+with negative values. The public options defined in pcre.h start at the least
+significant end. Make sure they don't overlap, though now that we have expanded
+to four bytes, there is plenty of space. */
+
+#define PCRE_FIRSTSET      0x40000000  /* first_byte is set */
+#define PCRE_REQCHSET      0x20000000  /* req_byte is set */
+#define PCRE_STARTLINE     0x10000000  /* start after \n for multiline */
+#define PCRE_ICHANGED      0x08000000  /* i option changes within regex */
+#define PCRE_NOPARTIAL     0x04000000  /* can't use partial with this regex */
+
+/* Options for the "extra" block produced by pcre_study(). */
+
+#define PCRE_STUDY_MAPPED   0x01     /* a map of starting chars exists */
+
+/* Masks for identifying the public options which are permitted at compile
+time, run time or study time, respectively. */
+
+#define PUBLIC_OPTIONS \
+  (PCRE_CASELESS|PCRE_EXTENDED|PCRE_ANCHORED|PCRE_MULTILINE| \
+   PCRE_DOTALL|PCRE_DOLLAR_ENDONLY|PCRE_EXTRA|PCRE_UNGREEDY|PCRE_UTF8| \
+   PCRE_NO_AUTO_CAPTURE|PCRE_NO_UTF8_CHECK|PCRE_AUTO_CALLOUT)
+
+#define PUBLIC_EXEC_OPTIONS \
+  (PCRE_ANCHORED|PCRE_NOTBOL|PCRE_NOTEOL|PCRE_NOTEMPTY|PCRE_NO_UTF8_CHECK| \
+   PCRE_PARTIAL)
+
+#define PUBLIC_STUDY_OPTIONS 0   /* None defined */
+
+/* Magic number to provide a small check against being handed junk. */
+
+#define MAGIC_NUMBER  0x50435245UL   /* 'PCRE' */
+
+/* Negative values for the firstchar and reqchar variables */
+
+#define REQ_UNSET (-2)
+#define REQ_NONE  (-1)
+
+/* Flags added to firstbyte or reqbyte; a "non-literal" item is either a
+variable-length repeat, or a anything other than literal characters. */
+
+#define REQ_CASELESS 0x0100    /* indicates caselessness */
+#define REQ_VARY     0x0200    /* reqbyte followed non-literal item */
+
+/* Miscellaneous definitions */
+
+typedef int BOOL;
+
+#define FALSE   0
+#define TRUE    1
+
+/* Escape items that are just an encoding of a particular data value. Note that
+ESC_n is defined as yet another macro, which is set in config.h to either \n
+(the default) or \r (which some people want). */
+
+#ifndef ESC_e
+#define ESC_e 27
+#endif
+
+#ifndef ESC_f
+#define ESC_f '\f'
+#endif
+
+#ifndef ESC_n
+#define ESC_n NEWLINE
+#endif
+
+#ifndef ESC_r
+#define ESC_r '\r'
+#endif
+
+/* We can't officially use ESC_t because it is a POSIX reserved identifier
+(presumably because of all the others like size_t). */
+
+#ifndef ESC_tee
+#define ESC_tee '\t'
+#endif
+
+/* These are escaped items that aren't just an encoding of a particular data
+value such as \n. They must have non-zero values, as check_escape() returns
+their negation. Also, they must appear in the same order as in the opcode
+definitions below, up to ESC_z. There's a dummy for OP_ANY because it
+corresponds to "." rather than an escape sequence. The final one must be
+ESC_REF as subsequent values are used for \1, \2, \3, etc. There is are two
+tests in the code for an escape greater than ESC_b and less than ESC_Z to
+detect the types that may be repeated. These are the types that consume
+characters. If any new escapes are put in between that don't consume a
+character, that code will have to change. */
+
+enum { ESC_A = 1, ESC_G, ESC_B, ESC_b, ESC_D, ESC_d, ESC_S, ESC_s, ESC_W,
+       ESC_w, ESC_dum1, ESC_C, ESC_P, ESC_p, ESC_X, ESC_Z, ESC_z, ESC_E,
+       ESC_Q, ESC_REF };
+
+/* Flag bits and data types for the extended class (OP_XCLASS) for classes that
+contain UTF-8 characters with values greater than 255. */
+
+#define XCL_NOT    0x01    /* Flag: this is a negative class */
+#define XCL_MAP    0x02    /* Flag: a 32-byte map is present */
+
+#define XCL_END       0    /* Marks end of individual items */
+#define XCL_SINGLE    1    /* Single item (one multibyte char) follows */
+#define XCL_RANGE     2    /* A range (two multibyte chars) follows */
+#define XCL_PROP      3    /* Unicode property (one property code) follows */
+#define XCL_NOTPROP   4    /* Unicode inverted property (ditto) */
+
+
+/* Opcode table: OP_BRA must be last, as all values >= it are used for brackets
+that extract substrings. Starting from 1 (i.e. after OP_END), the values up to
+OP_EOD must correspond in order to the list of escapes immediately above.
+Note that whenever this list is updated, the two macro definitions that follow
+must also be updated to match. */
+
+enum {
+  OP_END,            /* 0 End of pattern */
+
+  /* Values corresponding to backslashed metacharacters */
+
+  OP_SOD,            /* 1 Start of data: \A */
+  OP_SOM,            /* 2 Start of match (subject + offset): \G */
+  OP_NOT_WORD_BOUNDARY,  /*  3 \B */
+  OP_WORD_BOUNDARY,      /*  4 \b */
+  OP_NOT_DIGIT,          /*  5 \D */
+  OP_DIGIT,              /*  6 \d */
+  OP_NOT_WHITESPACE,     /*  7 \S */
+  OP_WHITESPACE,         /*  8 \s */
+  OP_NOT_WORDCHAR,       /*  9 \W */
+  OP_WORDCHAR,           /* 10 \w */
+  OP_ANY,            /* 11 Match any character */
+  OP_ANYBYTE,        /* 12 Match any byte (\C); different to OP_ANY for UTF-8 */
+  OP_NOTPROP,        /* 13 \P (not Unicode property) */
+  OP_PROP,           /* 14 \p (Unicode property) */
+  OP_EXTUNI,         /* 15 \X (extended Unicode sequence */
+  OP_EODN,           /* 16 End of data or \n at end of data: \Z. */
+  OP_EOD,            /* 17 End of data: \z */
+
+  OP_OPT,            /* 18 Set runtime options */
+  OP_CIRC,           /* 19 Start of line - varies with multiline switch */
+  OP_DOLL,           /* 20 End of line - varies with multiline switch */
+  OP_CHAR,           /* 21 Match one character, casefully */
+  OP_CHARNC,         /* 22 Match one character, caselessly */
+  OP_NOT,            /* 23 Match anything but the following char */
+
+  OP_STAR,           /* 24 The maximizing and minimizing versions of */
+  OP_MINSTAR,        /* 25 all these opcodes must come in pairs, with */
+  OP_PLUS,           /* 26 the minimizing one second. */
+  OP_MINPLUS,        /* 27 This first set applies to single characters */
+  OP_QUERY,          /* 28 */
+  OP_MINQUERY,       /* 29 */
+  OP_UPTO,           /* 30 From 0 to n matches */
+  OP_MINUPTO,        /* 31 */
+  OP_EXACT,          /* 32 Exactly n matches */
+
+  OP_NOTSTAR,        /* 33 The maximizing and minimizing versions of */
+  OP_NOTMINSTAR,     /* 34 all these opcodes must come in pairs, with */
+  OP_NOTPLUS,        /* 35 the minimizing one second. */
+  OP_NOTMINPLUS,     /* 36 This set applies to "not" single characters */
+  OP_NOTQUERY,       /* 37 */
+  OP_NOTMINQUERY,    /* 38 */
+  OP_NOTUPTO,        /* 39 From 0 to n matches */
+  OP_NOTMINUPTO,     /* 40 */
+  OP_NOTEXACT,       /* 41 Exactly n matches */
+
+  OP_TYPESTAR,       /* 42 The maximizing and minimizing versions of */
+  OP_TYPEMINSTAR,    /* 43 all these opcodes must come in pairs, with */
+  OP_TYPEPLUS,       /* 44 the minimizing one second. These codes must */
+  OP_TYPEMINPLUS,    /* 45 be in exactly the same order as those above. */
+  OP_TYPEQUERY,      /* 46 This set applies to character types such as \d */
+  OP_TYPEMINQUERY,   /* 47 */
+  OP_TYPEUPTO,       /* 48 From 0 to n matches */
+  OP_TYPEMINUPTO,    /* 49 */
+  OP_TYPEEXACT,      /* 50 Exactly n matches */
+
+  OP_CRSTAR,         /* 51 The maximizing and minimizing versions of */
+  OP_CRMINSTAR,      /* 52 all these opcodes must come in pairs, with */
+  OP_CRPLUS,         /* 53 the minimizing one second. These codes must */
+  OP_CRMINPLUS,      /* 54 be in exactly the same order as those above. */
+  OP_CRQUERY,        /* 55 These are for character classes and back refs */
+  OP_CRMINQUERY,     /* 56 */
+  OP_CRRANGE,        /* 57 These are different to the three sets above. */
+  OP_CRMINRANGE,     /* 58 */
+
+  OP_CLASS,          /* 59 Match a character class, chars < 256 only */
+  OP_NCLASS,         /* 60 Same, but the bitmap was created from a negative
+                           class - the difference is relevant only when a UTF-8
+                           character > 255 is encountered. */
+
+  OP_XCLASS,         /* 61 Extended class for handling UTF-8 chars within the
+                           class. This does both positive and negative. */
+
+  OP_REF,            /* 62 Match a back reference */
+  OP_RECURSE,        /* 63 Match a numbered subpattern (possibly recursive) */
+  OP_CALLOUT,        /* 64 Call out to external function if provided */
+
+  OP_ALT,            /* 65 Start of alternation */
+  OP_KET,            /* 66 End of group that doesn't have an unbounded repeat */
+  OP_KETRMAX,        /* 67 These two must remain together and in this */
+  OP_KETRMIN,        /* 68 order. They are for groups the repeat for ever. */
+
+  /* The assertions must come before ONCE and COND */
+
+  OP_ASSERT,         /* 69 Positive lookahead */
+  OP_ASSERT_NOT,     /* 70 Negative lookahead */
+  OP_ASSERTBACK,     /* 71 Positive lookbehind */
+  OP_ASSERTBACK_NOT, /* 72 Negative lookbehind */
+  OP_REVERSE,        /* 73 Move pointer back - used in lookbehind assertions */
+
+  /* ONCE and COND must come after the assertions, with ONCE first, as there's
+  a test for >= ONCE for a subpattern that isn't an assertion. */
+
+  OP_ONCE,           /* 74 Once matched, don't back up into the subpattern */
+  OP_COND,           /* 75 Conditional group */
+  OP_CREF,           /* 76 Used to hold an extraction string number (cond ref) */
+
+  OP_BRAZERO,        /* 77 These two must remain together and in this */
+  OP_BRAMINZERO,     /* 78 order. */
+
+  OP_BRANUMBER,      /* 79 Used for extracting brackets whose number is greater
+                           than can fit into an opcode. */
+
+  OP_BRA             /* 80 This and greater values are used for brackets that
+                           extract substrings up to EXTRACT_BASIC_MAX. After
+                           that, use is made of OP_BRANUMBER. */
+};
+
+/* WARNING WARNING WARNING: There is an implicit assumption in pcre.c and
+study.c that all opcodes are less than 128 in value. This makes handling UTF-8
+character sequences easier. */
+
+/* The highest extraction number before we have to start using additional
+bytes. (Originally PCRE didn't have support for extraction counts highter than
+this number.) The value is limited by the number of opcodes left after OP_BRA,
+i.e. 255 - OP_BRA. We actually set it a bit lower to leave room for additional
+opcodes. */
+
+#define EXTRACT_BASIC_MAX  100
+
+
+/* This macro defines textual names for all the opcodes. There are used only
+for debugging, in pcre.c when DEBUG is defined, and also in pcretest.c. The
+macro is referenced only in printint.c. */
+
+#define OP_NAME_LIST \
+  "End", "\\A", "\\G", "\\B", "\\b", "\\D", "\\d",                \
+  "\\S", "\\s", "\\W", "\\w", "Any", "Anybyte",                   \
+  "notprop", "prop", "extuni",                                    \
+  "\\Z", "\\z",                                                   \
+  "Opt", "^", "$", "char", "charnc", "not",                       \
+  "*", "*?", "+", "+?", "?", "??", "{", "{", "{",                 \
+  "*", "*?", "+", "+?", "?", "??", "{", "{", "{",                 \
+  "*", "*?", "+", "+?", "?", "??", "{", "{", "{",                 \
+  "*", "*?", "+", "+?", "?", "??", "{", "{",                      \
+  "class", "nclass", "xclass", "Ref", "Recurse", "Callout",       \
+  "Alt", "Ket", "KetRmax", "KetRmin", "Assert", "Assert not",     \
+  "AssertB", "AssertB not", "Reverse", "Once", "Cond", "Cond ref",\
+  "Brazero", "Braminzero", "Branumber", "Bra"
+
+
+/* This macro defines the length of fixed length operations in the compiled
+regex. The lengths are used when searching for specific things, and also in the
+debugging printing of a compiled regex. We use a macro so that it can be
+incorporated both into pcre.c and pcretest.c without being publicly exposed.
+
+As things have been extended, some of these are no longer fixed lenths, but are
+minima instead. For example, the length of a single-character repeat may vary
+in UTF-8 mode. The code that uses this table must know about such things. */
+
+#define OP_LENGTHS \
+  1,                             /* End                                    */ \
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* \A, \G, \B, \B, \D, \d, \S, \s, \W, \w */ \
+  1, 1,                          /* Any, Anybyte                           */ \
+  2, 2, 1,                       /* NOTPROP, PROP, EXTUNI                  */ \
+  1, 1, 2, 1, 1,                 /* \Z, \z, Opt, ^, $                      */ \
+  2,                             /* Char  - the minimum length             */ \
+  2,                             /* Charnc  - the minimum length           */ \
+  2,                             /* not                                    */ \
+  /* Positive single-char repeats                            ** These are  */ \
+  2, 2, 2, 2, 2, 2,              /* *, *?, +, +?, ?, ??      ** minima in  */ \
+  4, 4, 4,                       /* upto, minupto, exact     ** UTF-8 mode */ \
+  /* Negative single-char repeats - only for chars < 256                   */ \
+  2, 2, 2, 2, 2, 2,              /* NOT *, *?, +, +?, ?, ??                */ \
+  4, 4, 4,                       /* NOT upto, minupto, exact               */ \
+  /* Positive type repeats                                                 */ \
+  2, 2, 2, 2, 2, 2,              /* Type *, *?, +, +?, ?, ??               */ \
+  4, 4, 4,                       /* Type upto, minupto, exact              */ \
+  /* Character class & ref repeats                                         */ \
+  1, 1, 1, 1, 1, 1,              /* *, *?, +, +?, ?, ??                    */ \
+  5, 5,                          /* CRRANGE, CRMINRANGE                    */ \
+ 33,                             /* CLASS                                  */ \
+ 33,                             /* NCLASS                                 */ \
+  0,                             /* XCLASS - variable length               */ \
+  3,                             /* REF                                    */ \
+  1+LINK_SIZE,                   /* RECURSE                                */ \
+  2+2*LINK_SIZE,                 /* CALLOUT                                */ \
+  1+LINK_SIZE,                   /* Alt                                    */ \
+  1+LINK_SIZE,                   /* Ket                                    */ \
+  1+LINK_SIZE,                   /* KetRmax                                */ \
+  1+LINK_SIZE,                   /* KetRmin                                */ \
+  1+LINK_SIZE,                   /* Assert                                 */ \
+  1+LINK_SIZE,                   /* Assert not                             */ \
+  1+LINK_SIZE,                   /* Assert behind                          */ \
+  1+LINK_SIZE,                   /* Assert behind not                      */ \
+  1+LINK_SIZE,                   /* Reverse                                */ \
+  1+LINK_SIZE,                   /* Once                                   */ \
+  1+LINK_SIZE,                   /* COND                                   */ \
+  3,                             /* CREF                                   */ \
+  1, 1,                          /* BRAZERO, BRAMINZERO                    */ \
+  3,                             /* BRANUMBER                              */ \
+  1+LINK_SIZE                    /* BRA                                    */ \
+
+
+/* A magic value for OP_CREF to indicate the "in recursion" condition. */
+
+#define CREF_RECURSE  0xffff
+
+/* The texts of compile-time error messages are defined as macros here so that
+they can be accessed by the POSIX wrapper and converted into error codes.  Yes,
+I could have used error codes in the first place, but didn't feel like changing
+just to accommodate the POSIX wrapper. */
+
+#define ERR1  "\\ at end of pattern"
+#define ERR2  "\\c at end of pattern"
+#define ERR3  "unrecognized character follows \\"
+#define ERR4  "numbers out of order in {} quantifier"
+#define ERR5  "number too big in {} quantifier"
+#define ERR6  "missing terminating ] for character class"
+#define ERR7  "invalid escape sequence in character class"
+#define ERR8  "range out of order in character class"
+#define ERR9  "nothing to repeat"
+#define ERR10 "operand of unlimited repeat could match the empty string"
+#define ERR11 "internal error: unexpected repeat"
+#define ERR12 "unrecognized character after (?"
+#define ERR13 "POSIX named classes are supported only within a class"
+#define ERR14 "missing )"
+#define ERR15 "reference to non-existent subpattern"
+#define ERR16 "erroffset passed as NULL"
+#define ERR17 "unknown option bit(s) set"
+#define ERR18 "missing ) after comment"
+#define ERR19 "parentheses nested too deeply"
+#define ERR20 "regular expression too large"
+#define ERR21 "failed to get memory"
+#define ERR22 "unmatched parentheses"
+#define ERR23 "internal error: code overflow"
+#define ERR24 "unrecognized character after (?<"
+#define ERR25 "lookbehind assertion is not fixed length"
+#define ERR26 "malformed number after (?("
+#define ERR27 "conditional group contains more than two branches"
+#define ERR28 "assertion expected after (?("
+#define ERR29 "(?R or (?digits must be followed by )"
+#define ERR30 "unknown POSIX class name"
+#define ERR31 "POSIX collating elements are not supported"
+#define ERR32 "this version of PCRE is not compiled with PCRE_UTF8 support"
+#define ERR33 "spare error"
+#define ERR34 "character value in \\x{...} sequence is too large"
+#define ERR35 "invalid condition (?(0)"
+#define ERR36 "\\C not allowed in lookbehind assertion"
+#define ERR37 "PCRE does not support \\L, \\l, \\N, \\U, or \\u"
+#define ERR38 "number after (?C is > 255"
+#define ERR39 "closing ) for (?C expected"
+#define ERR40 "recursive call could loop indefinitely"
+#define ERR41 "unrecognized character after (?P"
+#define ERR42 "syntax error after (?P"
+#define ERR43 "two named groups have the same name"
+#define ERR44 "invalid UTF-8 string"
+#define ERR45 "support for \\P, \\p, and \\X has not been compiled"
+#define ERR46 "malformed \\P or \\p sequence"
+#define ERR47 "unknown property name after \\P or \\p"
+
+/* The real format of the start of the pcre block; the index of names and the
+code vector run on as long as necessary after the end. We store an explicit
+offset to the name table so that if a regex is compiled on one host, saved, and
+then run on another where the size of pointers is different, all might still
+be well. For the case of compiled-on-4 and run-on-8, we include an extra
+pointer that is always NULL. For future-proofing, we also include a few dummy
+fields - even though you can never get this planning right!
+
+NOTE NOTE NOTE:
+Because people can now save and re-use compiled patterns, any additions to this
+structure should be made at the end, and something earlier (e.g. a new
+flag in the options or one of the dummy fields) should indicate that the new
+fields are present. Currently PCRE always sets the dummy fields to zero.
+NOTE NOTE NOTE:
+*/
+
+typedef struct real_pcre {
+  pcre_uint32 magic_number;
+  pcre_uint32 size;               /* Total that was malloced */
+  pcre_uint32 options;
+  pcre_uint32 dummy1;             /* For future use, maybe */
+
+  pcre_uint16 top_bracket;
+  pcre_uint16 top_backref;
+  pcre_uint16 first_byte;
+  pcre_uint16 req_byte;
+  pcre_uint16 name_table_offset;  /* Offset to name table that follows */
+  pcre_uint16 name_entry_size;    /* Size of any name items */
+  pcre_uint16 name_count;         /* Number of name items */
+  pcre_uint16 dummy2;             /* For future use, maybe */
+
+  const unsigned char *tables;    /* Pointer to tables or NULL for std */
+  const unsigned char *nullpad;   /* NULL padding */
+} real_pcre;
+
+/* The format of the block used to store data from pcre_study(). The same
+remark (see NOTE above) about extending this structure applies. */
+
+typedef struct pcre_study_data {
+  pcre_uint32 size;               /* Total that was malloced */
+  pcre_uint32 options;
+  uschar start_bits[32];
+} pcre_study_data;
+
+/* Structure for passing "static" information around between the functions
+doing the compiling, so that they are thread-safe. */
+
+typedef struct compile_data {
+  const uschar *lcc;            /* Points to lower casing table */
+  const uschar *fcc;            /* Points to case-flipping table */
+  const uschar *cbits;          /* Points to character type table */
+  const uschar *ctypes;         /* Points to table of type maps */
+  const uschar *start_code;     /* The start of the compiled code */
+  const uschar *start_pattern;  /* The start of the pattern */
+  uschar *name_table;           /* The name/number table */
+  int  names_found;             /* Number of entries so far */
+  int  name_entry_size;         /* Size of each entry */
+  int  top_backref;             /* Maximum back reference */
+  unsigned int backref_map;     /* Bitmap of low back refs */
+  int  req_varyopt;             /* "After variable item" flag for reqbyte */
+  BOOL nopartial;               /* Set TRUE if partial won't work */
+} compile_data;
+
+/* Structure for maintaining a chain of pointers to the currently incomplete
+branches, for testing for left recursion. */
+
+typedef struct branch_chain {
+  struct branch_chain *outer;
+  uschar *current;
+} branch_chain;
+
+/* Structure for items in a linked list that represents an explicit recursive
+call within the pattern. */
+
+typedef struct recursion_info {
+  struct recursion_info *prevrec; /* Previous recursion record (or NULL) */
+  int group_num;                /* Number of group that was called */
+  const uschar *after_call;     /* "Return value": points after the call in the expr */
+  const uschar *save_start;     /* Old value of md->start_match */
+  int *offset_save;             /* Pointer to start of saved offsets */
+  int saved_max;                /* Number of saved offsets */
+} recursion_info;
+
+/* When compiling in a mode that doesn't use recursive calls to match(),
+a structure is used to remember local variables on the heap. It is defined in
+pcre.c, close to the match() function, so that it is easy to keep it in step
+with any changes of local variable. However, the pointer to the current frame
+must be saved in some "static" place over a longjmp(). We declare the
+structure here so that we can put a pointer in the match_data structure.
+NOTE: This isn't used for a "normal" compilation of pcre. */
+
+struct heapframe;
+
+/* Structure for passing "static" information around between the functions
+doing the matching, so that they are thread-safe. */
+
+typedef struct match_data {
+  unsigned long int match_call_count; /* As it says */
+  unsigned long int match_limit;/* As it says */
+  int   *offset_vector;         /* Offset vector */
+  int    offset_end;            /* One past the end */
+  int    offset_max;            /* The maximum usable for return data */
+  const uschar *lcc;            /* Points to lower casing table */
+  const uschar *ctypes;         /* Points to table of type maps */
+  BOOL   offset_overflow;       /* Set if too many extractions */
+  BOOL   notbol;                /* NOTBOL flag */
+  BOOL   noteol;                /* NOTEOL flag */
+  BOOL   utf8;                  /* UTF8 flag */
+  BOOL   endonly;               /* Dollar not before final \n */
+  BOOL   notempty;              /* Empty string match not wanted */
+  BOOL   partial;               /* PARTIAL flag */
+  BOOL   hitend;                /* Hit the end of the subject at some point */
+  const uschar *start_code;     /* For use when recursing */
+  const uschar *start_subject;  /* Start of the subject string */
+  const uschar *end_subject;    /* End of the subject string */
+  const uschar *start_match;    /* Start of this match attempt */
+  const uschar *end_match_ptr;  /* Subject position at end match */
+  int    end_offset_top;        /* Highwater mark at end of match */
+  int    capture_last;          /* Most recent capture number */
+  int    start_offset;          /* The start offset value */
+  recursion_info *recursive;    /* Linked list of recursion data */
+  void  *callout_data;          /* To pass back to callouts */
+  struct heapframe *thisframe;  /* Used only when compiling for no recursion */
+} match_data;
+
+/* Bit definitions for entries in the pcre_ctypes table. */
+
+#define ctype_space   0x01
+#define ctype_letter  0x02
+#define ctype_digit   0x04
+#define ctype_xdigit  0x08
+#define ctype_word    0x10   /* alphameric or '_' */
+#define ctype_meta    0x80   /* regexp meta char or zero (end pattern) */
+
+/* Offsets for the bitmap tables in pcre_cbits. Each table contains a set
+of bits for a class map. Some classes are built by combining these tables. */
+
+#define cbit_space     0      /* [:space:] or \s */
+#define cbit_xdigit   32      /* [:xdigit:] */
+#define cbit_digit    64      /* [:digit:] or \d */
+#define cbit_upper    96      /* [:upper:] */
+#define cbit_lower   128      /* [:lower:] */
+#define cbit_word    160      /* [:word:] or \w */
+#define cbit_graph   192      /* [:graph:] */
+#define cbit_print   224      /* [:print:] */
+#define cbit_punct   256      /* [:punct:] */
+#define cbit_cntrl   288      /* [:cntrl:] */
+#define cbit_length  320      /* Length of the cbits table */
+
+/* Offsets of the various tables from the base tables pointer, and
+total length. */
+
+#define lcc_offset      0
+#define fcc_offset    256
+#define cbits_offset  512
+#define ctypes_offset (cbits_offset + cbit_length)
+#define tables_length (ctypes_offset + 256)
+
+/* End of internal.h */
diff --git a/connectors/jk/native/iis/pcre/libpcre.def b/connectors/jk/native/iis/pcre/libpcre.def
new file mode 100644
index 0000000..2b35d10
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/libpcre.def
@@ -0,0 +1,19 @@
+LIBRARY libpcre
+EXPORTS
+pcre_malloc
+pcre_free
+pcre_config
+pcre_callout
+pcre_compile
+pcre_copy_substring
+pcre_exec
+pcre_get_substring
+pcre_get_stringnumber
+pcre_get_substring_list
+pcre_free_substring
+pcre_free_substring_list
+pcre_info
+pcre_fullinfo
+pcre_maketables
+pcre_study
+pcre_version
diff --git a/connectors/jk/native/iis/pcre/libpcre.pc.in b/connectors/jk/native/iis/pcre/libpcre.pc.in
new file mode 100644
index 0000000..4784401
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/libpcre.pc.in
@@ -0,0 +1,12 @@
+# Package Information for pkg-config
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libpcre
+Description: PCRE - Perl compatible regular expressions C library
+Version: @PCRE_VERSION@
+Libs: -L${libdir} -lpcre
+Cflags: -I${includedir}
diff --git a/connectors/jk/native/iis/pcre/libpcreposix.def b/connectors/jk/native/iis/pcre/libpcreposix.def
new file mode 100644
index 0000000..5723440
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/libpcreposix.def
@@ -0,0 +1,24 @@
+LIBRARY libpcreposix
+EXPORTS
+pcre_malloc
+pcre_free
+pcre_config
+pcre_callout
+pcre_compile
+pcre_copy_substring
+pcre_exec
+pcre_get_substring
+pcre_get_stringnumber
+pcre_get_substring_list
+pcre_free_substring
+pcre_free_substring_list
+pcre_info
+pcre_fullinfo
+pcre_maketables
+pcre_study
+pcre_version
+
+regcomp
+regexec
+regerror
+regfree
diff --git a/connectors/jk/native/iis/pcre/maketables.c b/connectors/jk/native/iis/pcre/maketables.c
new file mode 100644
index 0000000..f1c7b9a
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/maketables.c
@@ -0,0 +1,146 @@
+/*************************************************
+*      Perl-Compatible Regular Expressions       *
+*************************************************/
+
+/*
+PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+Written by: Philip Hazel <ph10@cam.ac.uk>
+
+           Copyright (c) 1997-2003 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+    * Neither the name of the University of Cambridge nor the names of its
+      contributors may be used to endorse or promote products derived from
+      this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+
+/* This file is compiled on its own as part of the PCRE library. However,
+it is also included in the compilation of dftables.c, in which case the macro
+DFTABLES is defined. */
+
+#ifndef DFTABLES
+#include "internal.h"
+#endif
+
+
+
+/*************************************************
+*           Create PCRE character tables         *
+*************************************************/
+
+/* This function builds a set of character tables for use by PCRE and returns
+a pointer to them. They are build using the ctype functions, and consequently
+their contents will depend upon the current locale setting. When compiled as
+part of the library, the store is obtained via pcre_malloc(), but when compiled
+inside dftables, use malloc().
+
+Arguments:   none
+Returns:     pointer to the contiguous block of data
+*/
+
+const unsigned char *
+pcre_maketables(void)
+{
+unsigned char *yield, *p;
+int i;
+
+#ifndef DFTABLES
+yield = (unsigned char*)(pcre_malloc)(tables_length);
+#else
+yield = (unsigned char*)malloc(tables_length);
+#endif
+
+if (yield == NULL) return NULL;
+p = yield;
+
+/* First comes the lower casing table */
+
+for (i = 0; i < 256; i++) *p++ = tolower(i);
+
+/* Next the case-flipping table */
+
+for (i = 0; i < 256; i++) *p++ = islower(i)? toupper(i) : tolower(i);
+
+/* Then the character class tables. Don't try to be clever and save effort
+on exclusive ones - in some locales things may be different. Note that the
+table for "space" includes everything "isspace" gives, including VT in the
+default locale. This makes it work for the POSIX class [:space:]. */
+
+memset(p, 0, cbit_length);
+for (i = 0; i < 256; i++)
+  {
+  if (isdigit(i))
+    {
+    p[cbit_digit  + i/8] |= 1 << (i&7);
+    p[cbit_word   + i/8] |= 1 << (i&7);
+    }
+  if (isupper(i))
+    {
+    p[cbit_upper  + i/8] |= 1 << (i&7);
+    p[cbit_word   + i/8] |= 1 << (i&7);
+    }
+  if (islower(i))
+    {
+    p[cbit_lower  + i/8] |= 1 << (i&7);
+    p[cbit_word   + i/8] |= 1 << (i&7);
+    }
+  if (i == '_')   p[cbit_word   + i/8] |= 1 << (i&7);
+  if (isspace(i)) p[cbit_space  + i/8] |= 1 << (i&7);
+  if (isxdigit(i))p[cbit_xdigit + i/8] |= 1 << (i&7);
+  if (isgraph(i)) p[cbit_graph  + i/8] |= 1 << (i&7);
+  if (isprint(i)) p[cbit_print  + i/8] |= 1 << (i&7);
+  if (ispunct(i)) p[cbit_punct  + i/8] |= 1 << (i&7);
+  if (iscntrl(i)) p[cbit_cntrl  + i/8] |= 1 << (i&7);
+  }
+p += cbit_length;
+
+/* Finally, the character type table. In this, we exclude VT from the white
+space chars, because Perl doesn't recognize it as such for \s and for comments
+within regexes. */
+
+for (i = 0; i < 256; i++)
+  {
+  int x = 0;
+  if (i != 0x0b && isspace(i)) x += ctype_space;
+  if (isalpha(i)) x += ctype_letter;
+  if (isdigit(i)) x += ctype_digit;
+  if (isxdigit(i)) x += ctype_xdigit;
+  if (isalnum(i) || i == '_') x += ctype_word;
+
+  /* Note: strchr includes the terminating zero in the characters it considers.
+  In this instance, that is ok because we want binary zero to be flagged as a
+  meta-character, which in this sense is any character that terminates a run
+  of data characters. */
+
+  if (strchr("*+?{^.$|()[", i) != 0) x += ctype_meta; *p++ = x; }
+
+return yield;
+}
+
+/* End of maketables.c */
diff --git a/connectors/jk/native/iis/pcre/makevp.bat b/connectors/jk/native/iis/pcre/makevp.bat
new file mode 100644
index 0000000..10bd248
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/makevp.bat
@@ -0,0 +1,25 @@
+@echo off
+
+REM This file was contributed by Alexander Tokarev for building PCRE for use
+REM with Virtual Pascal. It has not been tested with the latest PCRE release.
+
+REM CHANGE THIS FOR YOUR BORLAND C++ COMPILER PATH
+
+SET BORLAND=c:\usr\apps\bcc55
+
+sh configure
+
+bcc32 -DDFTABLES -DSTATIC -DVPCOMPAT -I%BORLAND%\include -L%BORLAND%\lib dftables.c
+
+dftables > chartables.c
+
+bcc32 -c -RT- -y- -v- -u- -P- -O2 -5 -DSTATIC -DVPCOMPAT -UDFTABLES -I%BORLAND%\include get.c maketables.c pcre.c study.c
+
+tlib %BORLAND%\lib\cw32.lib *calloc *del *strncmp *memcpy *memmove *memset
+tlib pcre.lib +get.obj +maketables.obj +pcre.obj +study.obj +calloc.obj +del.obj +strncmp.obj +memcpy.obj +memmove.obj +memset.obj
+
+del *.obj *.exe *.tds *.bak >nul 2>nul
+
+echo ---
+echo Now the library should be complete. Please check all messages above.
+echo Don't care for warnings, it's OK.
diff --git a/connectors/jk/native/iis/pcre/mkinstalldirs b/connectors/jk/native/iis/pcre/mkinstalldirs
new file mode 100755
index 0000000..6b3b5fc
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/mkinstalldirs
@@ -0,0 +1,40 @@
+#! /bin/sh
+# mkinstalldirs --- make directory hierarchy
+# Author: Noah Friedman <friedman@prep.ai.mit.edu>
+# Created: 1993-05-16
+# Public domain
+
+# $Id$
+
+errstatus=0
+
+for file
+do
+   set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
+   shift
+
+   pathcomp=
+   for d
+   do
+     pathcomp="$pathcomp$d"
+     case "$pathcomp" in
+       -* ) pathcomp=./$pathcomp ;;
+     esac
+
+     if test ! -d "$pathcomp"; then
+        echo "mkdir $pathcomp"
+
+        mkdir "$pathcomp" || lasterr=$?
+
+        if test ! -d "$pathcomp"; then
+  	  errstatus=$lasterr
+        fi
+     fi
+
+     pathcomp="$pathcomp/"
+   done
+done
+
+exit $errstatus
+
+# mkinstalldirs ends here
diff --git a/connectors/jk/native/iis/pcre/pcre-config.in b/connectors/jk/native/iis/pcre/pcre-config.in
new file mode 100644
index 0000000..30d66ce
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/pcre-config.in
@@ -0,0 +1,66 @@
+#!/bin/sh
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+exec_prefix_set=no
+
+usage="\
+Usage: pcre-config [--prefix] [--exec-prefix] [--version] [--libs] [--libs-posix] [--cflags] [--cflags-posix]"
+
+if test $# -eq 0; then
+      echo "${usage}" 1>&2
+      exit 1
+fi
+
+libR=
+case `uname -s` in
+  *SunOS*)
+  libR=" -R@libdir@"
+  ;;
+esac
+
+while test $# -gt 0; do
+  case "$1" in
+  -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+  *) optarg= ;;
+  esac
+
+  case $1 in
+    --prefix=*)
+      prefix=$optarg
+      if test $exec_prefix_set = no ; then
+        exec_prefix=$optarg
+      fi
+      ;;
+    --prefix)
+      echo $prefix
+      ;;
+    --exec-prefix=*)
+      exec_prefix=$optarg
+      exec_prefix_set=yes
+      ;;
+    --exec-prefix)
+      echo $exec_prefix
+      ;;
+    --version)
+      echo @PCRE_VERSION@
+      ;;
+    --cflags | --cflags-posix)
+      if test @includedir@ != /usr/include ; then
+        includes=-I@includedir@
+      fi
+      echo $includes
+      ;;
+    --libs-posix)
+      echo -L@libdir@$libR -lpcreposix -lpcre
+      ;;
+    --libs)
+      echo -L@libdir@$libR -lpcre
+      ;;
+    *)
+      echo "${usage}" 1>&2
+      exit 1
+      ;;
+  esac
+  shift
+done
diff --git a/connectors/jk/native/iis/pcre/pcre.amd64 b/connectors/jk/native/iis/pcre/pcre.amd64
new file mode 100644
index 0000000..b8a1b82
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/pcre.amd64
@@ -0,0 +1,131 @@
+# Microsoft Developer Studio Generated NMAKE File, Based on pcre.dsp
+# Use Platform SDK:
+# SetEnv.cmd /X64 /RETAIL
+# nmake -f pcre.amd64
+#
+
+CPP=cl.exe
+RSC=rc.exe
+OUTDIR=.\Release_amd64
+INTDIR=.\Release_amd64
+# Begin Custom Macros
+OutDir=.\Release_amd64
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0" 
+
+ALL : "$(OUTDIR)\pcre.lib"
+
+!ELSE 
+
+ALL : "$(OUTDIR)\pcre.lib"
+
+!ENDIF 
+
+CLEAN :
+	-@erase "$(INTDIR)\get.obj"
+	-@erase "$(INTDIR)\maketables.obj"
+	-@erase "$(INTDIR)\pcre.obj"
+	-@erase "$(INTDIR)\pcre_src.idb"
+	-@erase "$(INTDIR)\pcre_src.pdb"
+	-@erase "$(INTDIR)\study.obj"
+	-@erase "$(OUTDIR)\pcre.lib"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\pcre.bsc" 
+BSC32_SBRS= \
+	
+LIB32=link.exe -lib
+LIB32_FLAGS=/nologo /out:"$(OUTDIR)\pcre.lib" 
+LIB32_OBJS= \
+	"$(INTDIR)\get.obj" \
+	"$(INTDIR)\maketables.obj" \
+	"$(INTDIR)\pcre.obj" \
+	"$(INTDIR)\study.obj"
+
+"$(OUTDIR)\pcre.lib" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS)
+    $(LIB32) @<<
+  $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS)
+<<
+
+CPP_PROJ=/nologo /MD /W3 /Zi /O2 /D "_WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_AMD64_=1" -DWIN64 /D "_WIN64" /Wp64 /FIPRE64PRA.H /D "PCRE_STATIC" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\pcre_src" /FD /c 
+
+.c{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cpp{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cxx{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.c{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cpp{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cxx{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+SOURCE=.\dftables.exe
+InputPath=.\dftables.exe
+
+".\chartables.c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	<<tempfile.bat 
+	@echo off 
+	.\dftables.exe chartables.c 
+<< 
+	
+SOURCE=.\get.c
+
+"$(INTDIR)\get.obj" : $(SOURCE) "$(INTDIR)" ".\config.h" ".\pcre.h"
+
+
+SOURCE=.\maketables.c
+
+"$(INTDIR)\maketables.obj" : $(SOURCE) "$(INTDIR)" ".\config.h" ".\pcre.h"
+
+
+SOURCE=.\pcre.c
+
+"$(INTDIR)\pcre.obj" : $(SOURCE) "$(INTDIR)" ".\chartables.c" ".\config.h" ".\pcre.h"
+
+
+SOURCE=.\study.c
+
+"$(INTDIR)\study.obj" : $(SOURCE) "$(INTDIR)" ".\config.h" ".\pcre.h"
+
+
+SOURCE=.\config.hw
+InputPath=.\config.hw
+
+".\config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	<<tempfile.bat 
+	@echo off 
+	type .\config.hw > .\config.h
+<< 
+	
+SOURCE=.\pcre.hw
+InputPath=.\pcre.hw
+
+".\pcre.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	<<tempfile.bat 
+	@echo off 
+	type .\pcre.hw > .\pcre.h
+<< 
diff --git a/connectors/jk/native/iis/pcre/pcre.c b/connectors/jk/native/iis/pcre/pcre.c
new file mode 100644
index 0000000..4936323
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/pcre.c
@@ -0,0 +1,9207 @@
+/*************************************************
+*      Perl-Compatible Regular Expressions       *
+*************************************************/
+
+/*
+This is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language. See
+the file Tech.Notes for some information on the internals.
+
+Written by: Philip Hazel <ph10@cam.ac.uk>
+
+           Copyright (c) 1997-2004 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+    * Neither the name of the University of Cambridge nor the names of its
+      contributors may be used to endorse or promote products derived from
+      this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+
+/* Define DEBUG to get debugging output on stdout. */
+/* #define DEBUG */
+
+/* Use a macro for debugging printing, 'cause that eliminates the use of #ifdef
+inline, and there are *still* stupid compilers about that don't like indented
+pre-processor statements. I suppose it's only been 10 years... */
+
+#ifdef DEBUG
+#define DPRINTF(p) printf p
+#else
+#define DPRINTF(p) /*nothing*/
+#endif
+
+/* Include the internals header, which itself includes "config.h", the Standard
+C headers, and the external pcre header. */
+
+#include "internal.h"
+
+/* If Unicode Property support is wanted, include a private copy of the
+function that does it, and the table that translates names to numbers. */
+
+#ifdef SUPPORT_UCP
+#include "ucp.c"
+#include "ucptypetable.c"
+#endif
+
+/* Maximum number of items on the nested bracket stacks at compile time. This
+applies to the nesting of all kinds of parentheses. It does not limit
+un-nested, non-capturing parentheses. This number can be made bigger if
+necessary - it is used to dimension one int and one unsigned char vector at
+compile time. */
+
+#define BRASTACK_SIZE 200
+
+
+/* Maximum number of ints of offset to save on the stack for recursive calls.
+If the offset vector is bigger, malloc is used. This should be a multiple of 3,
+because the offset vector is always a multiple of 3 long. */
+
+#define REC_STACK_SAVE_MAX 30
+
+
+/* The maximum remaining length of subject we are prepared to search for a
+req_byte match. */
+
+#define REQ_BYTE_MAX 1000
+
+
+/* Table of sizes for the fixed-length opcodes. It's defined in a macro so that
+the definition is next to the definition of the opcodes in internal.h. */
+
+static const uschar OP_lengths[] = { OP_LENGTHS };
+
+/* Min and max values for the common repeats; for the maxima, 0 => infinity */
+
+static const char rep_min[] = { 0, 0, 1, 1, 0, 0 };
+static const char rep_max[] = { 0, 0, 0, 0, 1, 1 };
+
+/* Table for handling escaped characters in the range '0'-'z'. Positive returns
+are simple data values; negative values are for special things like \d and so
+on. Zero means further processing is needed (for things like \x), or the escape
+is invalid. */
+
+#if !EBCDIC   /* This is the "normal" table for ASCII systems */
+static const short int escapes[] = {
+     0,      0,      0,      0,      0,      0,      0,      0,   /* 0 - 7 */
+     0,      0,    ':',    ';',    '<',    '=',    '>',    '?',   /* 8 - ? */
+   '@', -ESC_A, -ESC_B, -ESC_C, -ESC_D, -ESC_E,      0, -ESC_G,   /* @ - G */
+     0,      0,      0,      0,      0,      0,      0,      0,   /* H - O */
+-ESC_P, -ESC_Q,      0, -ESC_S,      0,      0,      0, -ESC_W,   /* P - W */
+-ESC_X,      0, -ESC_Z,    '[',   '\\',    ']',    '^',    '_',   /* X - _ */
+   '`',      7, -ESC_b,      0, -ESC_d,  ESC_e,  ESC_f,      0,   /* ` - g */
+     0,      0,      0,      0,      0,      0,  ESC_n,      0,   /* h - o */
+-ESC_p,      0,  ESC_r, -ESC_s,  ESC_tee,    0,      0, -ESC_w,   /* p - w */
+     0,      0, -ESC_z                                            /* x - z */
+};
+
+#else         /* This is the "abnormal" table for EBCDIC systems */
+static const short int escapes[] = {
+/*  48 */     0,     0,      0,     '.',    '<',   '(',    '+',    '|',
+/*  50 */   '&',     0,      0,       0,      0,     0,      0,      0,
+/*  58 */     0,     0,    '!',     '$',    '*',   ')',    ';',    '~',
+/*  60 */   '-',   '/',      0,       0,      0,     0,      0,      0,
+/*  68 */     0,     0,    '|',     ',',    '%',   '_',    '>',    '?',
+/*  70 */     0,     0,      0,       0,      0,     0,      0,      0,
+/*  78 */     0,   '`',    ':',     '#',    '@',  '\'',    '=',    '"',
+/*  80 */     0,     7, -ESC_b,       0, -ESC_d, ESC_e,  ESC_f,      0,
+/*  88 */     0,     0,      0,     '{',      0,     0,      0,      0,
+/*  90 */     0,     0,      0,     'l',      0, ESC_n,      0, -ESC_p,
+/*  98 */     0, ESC_r,      0,     '}',      0,     0,      0,      0,
+/*  A0 */     0,   '~', -ESC_s, ESC_tee,      0,     0, -ESC_w,      0,
+/*  A8 */     0,-ESC_z,      0,       0,      0,   '[',      0,      0,
+/*  B0 */     0,     0,      0,       0,      0,     0,      0,      0,
+/*  B8 */     0,     0,      0,       0,      0,   ']',    '=',    '-',
+/*  C0 */   '{',-ESC_A, -ESC_B,  -ESC_C, -ESC_D,-ESC_E,      0, -ESC_G,
+/*  C8 */     0,     0,      0,       0,      0,     0,      0,      0,
+/*  D0 */   '}',     0,      0,       0,      0,     0,      0, -ESC_P,
+/*  D8 */-ESC_Q,     0,      0,       0,      0,     0,      0,      0,
+/*  E0 */  '\\',     0, -ESC_S,       0,      0,     0, -ESC_W, -ESC_X,
+/*  E8 */     0,-ESC_Z,      0,       0,      0,     0,      0,      0,
+/*  F0 */     0,     0,      0,       0,      0,     0,      0,      0,
+/*  F8 */     0,     0,      0,       0,      0,     0,      0,      0
+};
+#endif
+
+
+/* Tables of names of POSIX character classes and their lengths. The list is
+terminated by a zero length entry. The first three must be alpha, upper, lower,
+as this is assumed for handling case independence. */
+
+static const char *const posix_names[] = {
+  "alpha", "lower", "upper",
+  "alnum", "ascii", "blank", "cntrl", "digit", "graph",
+  "print", "punct", "space", "word",  "xdigit" };
+
+static const uschar posix_name_lengths[] = {
+  5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 6, 0 };
+
+/* Table of class bit maps for each POSIX class; up to three may be combined
+to form the class. The table for [:blank:] is dynamically modified to remove
+the vertical space characters. */
+
+static const int posix_class_maps[] = {
+  cbit_lower, cbit_upper, -1,             /* alpha */
+  cbit_lower, -1,         -1,             /* lower */
+  cbit_upper, -1,         -1,             /* upper */
+  cbit_digit, cbit_lower, cbit_upper,     /* alnum */
+  cbit_print, cbit_cntrl, -1,             /* ascii */
+  cbit_space, -1,         -1,             /* blank - a GNU extension */
+  cbit_cntrl, -1,         -1,             /* cntrl */
+  cbit_digit, -1,         -1,             /* digit */
+  cbit_graph, -1,         -1,             /* graph */
+  cbit_print, -1,         -1,             /* print */
+  cbit_punct, -1,         -1,             /* punct */
+  cbit_space, -1,         -1,             /* space */
+  cbit_word,  -1,         -1,             /* word - a Perl extension */
+  cbit_xdigit,-1,         -1              /* xdigit */
+};
+
+/* Table to identify digits and hex digits. This is used when compiling
+patterns. Note that the tables in chartables are dependent on the locale, and
+may mark arbitrary characters as digits - but the PCRE compiling code expects
+to handle only 0-9, a-z, and A-Z as digits when compiling. That is why we have
+a private table here. It costs 256 bytes, but it is a lot faster than doing
+character value tests (at least in some simple cases I timed), and in some
+applications one wants PCRE to compile efficiently as well as match
+efficiently.
+
+For convenience, we use the same bit definitions as in chartables:
+
+  0x04   decimal digit
+  0x08   hexadecimal digit
+
+Then we can use ctype_digit and ctype_xdigit in the code. */
+
+#if !EBCDIC    /* This is the "normal" case, for ASCII systems */
+static const unsigned char digitab[] =
+  {
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*   0-  7 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*   8- 15 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  16- 23 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  24- 31 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*    - '  */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  ( - /  */
+  0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, /*  0 - 7  */
+  0x0c,0x0c,0x00,0x00,0x00,0x00,0x00,0x00, /*  8 - ?  */
+  0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x00, /*  @ - G  */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  H - O  */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  P - W  */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  X - _  */
+  0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x00, /*  ` - g  */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  h - o  */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  p - w  */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  x -127 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 128-135 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 136-143 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 144-151 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 152-159 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 160-167 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 168-175 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 176-183 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 184-191 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 192-199 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 200-207 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 208-215 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 216-223 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 224-231 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 232-239 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 240-247 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};/* 248-255 */
+
+#else          /* This is the "abnormal" case, for EBCDIC systems */
+static const unsigned char digitab[] =
+  {
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*   0-  7  0 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*   8- 15    */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  16- 23 10 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  24- 31    */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  32- 39 20 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  40- 47    */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  48- 55 30 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  56- 63    */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*    - 71 40 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  72- |     */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  & - 87 50 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  88- ¬     */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  - -103 60 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 104- ?     */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 112-119 70 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 120- "     */
+  0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x00, /* 128- g  80 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  h -143    */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 144- p  90 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  q -159    */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 160- x  A0 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  y -175    */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  ^ -183 B0 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 184-191    */
+  0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x00, /*  { - G  C0 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  H -207    */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  } - P  D0 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  Q -223    */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  \ - X  E0 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  Y -239    */
+  0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, /*  0 - 7  F0 */
+  0x0c,0x0c,0x00,0x00,0x00,0x00,0x00,0x00};/*  8 -255    */
+
+static const unsigned char ebcdic_chartab[] = { /* chartable partial dup */
+  0x80,0x00,0x00,0x00,0x00,0x01,0x00,0x00, /*   0-  7 */
+  0x00,0x00,0x00,0x00,0x01,0x01,0x00,0x00, /*   8- 15 */
+  0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00, /*  16- 23 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  24- 31 */
+  0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00, /*  32- 39 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  40- 47 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  48- 55 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  56- 63 */
+  0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*    - 71 */
+  0x00,0x00,0x00,0x80,0x00,0x80,0x80,0x80, /*  72- |  */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  & - 87 */
+  0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00, /*  88- ¬  */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  - -103 */
+  0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x80, /* 104- ?  */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 112-119 */
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 120- "  */
+  0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* 128- g  */
+  0x12,0x12,0x00,0x00,0x00,0x00,0x00,0x00, /*  h -143 */
+  0x00,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* 144- p  */
+  0x12,0x12,0x00,0x00,0x00,0x00,0x00,0x00, /*  q -159 */
+  0x00,0x00,0x12,0x12,0x12,0x12,0x12,0x12, /* 160- x  */
+  0x12,0x12,0x00,0x00,0x00,0x00,0x00,0x00, /*  y -175 */
+  0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /*  ^ -183 */
+  0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00, /* 184-191 */
+  0x80,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /*  { - G  */
+  0x12,0x12,0x00,0x00,0x00,0x00,0x00,0x00, /*  H -207 */
+  0x00,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /*  } - P  */
+  0x12,0x12,0x00,0x00,0x00,0x00,0x00,0x00, /*  Q -223 */
+  0x00,0x00,0x12,0x12,0x12,0x12,0x12,0x12, /*  \ - X  */
+  0x12,0x12,0x00,0x00,0x00,0x00,0x00,0x00, /*  Y -239 */
+  0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, /*  0 - 7  */
+  0x1c,0x1c,0x00,0x00,0x00,0x00,0x00,0x00};/*  8 -255 */
+#endif
+
+
+/* Definition to allow mutual recursion */
+
+static BOOL
+  compile_regex(int, int, int *, uschar **, const uschar **, const char **,
+    BOOL, int, int *, int *, branch_chain *, compile_data *);
+
+/* Structure for building a chain of data that actually lives on the
+stack, for holding the values of the subject pointer at the start of each
+subpattern, so as to detect when an empty string has been matched by a
+subpattern - to break infinite loops. When NO_RECURSE is set, these blocks
+are on the heap, not on the stack. */
+
+typedef struct eptrblock {
+  struct eptrblock *epb_prev;
+  const uschar *epb_saved_eptr;
+} eptrblock;
+
+/* Flag bits for the match() function */
+
+#define match_condassert   0x01    /* Called to check a condition assertion */
+#define match_isgroup      0x02    /* Set if start of bracketed group */
+
+/* Non-error returns from the match() function. Error returns are externally
+defined PCRE_ERROR_xxx codes, which are all negative. */
+
+#define MATCH_MATCH        1
+#define MATCH_NOMATCH      0
+
+
+
+/*************************************************
+*               Global variables                 *
+*************************************************/
+
+/* PCRE is thread-clean and doesn't use any global variables in the normal
+sense. However, it calls memory allocation and free functions via the four
+indirections below, and it can optionally do callouts. These values can be
+changed by the caller, but are shared between all threads. However, when
+compiling for Virtual Pascal, things are done differently (see pcre.in). */
+
+#ifndef VPCOMPAT
+#ifdef __cplusplus
+extern "C" void *(*pcre_malloc)(size_t) = malloc;
+extern "C" void  (*pcre_free)(void *) = free;
+extern "C" void *(*pcre_stack_malloc)(size_t) = malloc;
+extern "C" void  (*pcre_stack_free)(void *) = free;
+extern "C" int   (*pcre_callout)(pcre_callout_block *) = NULL;
+#else
+void *(*pcre_malloc)(size_t) = malloc;
+void  (*pcre_free)(void *) = free;
+void *(*pcre_stack_malloc)(size_t) = malloc;
+void  (*pcre_stack_free)(void *) = free;
+int   (*pcre_callout)(pcre_callout_block *) = NULL;
+#endif
+#endif
+
+
+/*************************************************
+*    Macros and tables for character handling    *
+*************************************************/
+
+/* When UTF-8 encoding is being used, a character is no longer just a single
+byte. The macros for character handling generate simple sequences when used in
+byte-mode, and more complicated ones for UTF-8 characters. */
+
+#ifndef SUPPORT_UTF8
+#define GETCHAR(c, eptr) c = *eptr;
+#define GETCHARINC(c, eptr) c = *eptr++;
+#define GETCHARINCTEST(c, eptr) c = *eptr++;
+#define GETCHARLEN(c, eptr, len) c = *eptr;
+#define BACKCHAR(eptr)
+
+#else   /* SUPPORT_UTF8 */
+
+/* Get the next UTF-8 character, not advancing the pointer. This is called when
+we know we are in UTF-8 mode. */
+
+#define GETCHAR(c, eptr) \
+  c = *eptr; \
+  if ((c & 0xc0) == 0xc0) \
+    { \
+    int gcii; \
+    int gcaa = utf8_table4[c & 0x3f];  /* Number of additional bytes */ \
+    int gcss = 6*gcaa; \
+    c = (c & utf8_table3[gcaa]) << gcss; \
+    for (gcii = 1; gcii <= gcaa; gcii++) \
+      { \
+      gcss -= 6; \
+      c |= (eptr[gcii] & 0x3f) << gcss; \
+      } \
+    }
+
+/* Get the next UTF-8 character, advancing the pointer. This is called when we
+know we are in UTF-8 mode. */
+
+#define GETCHARINC(c, eptr) \
+  c = *eptr++; \
+  if ((c & 0xc0) == 0xc0) \
+    { \
+    int gcaa = utf8_table4[c & 0x3f];  /* Number of additional bytes */ \
+    int gcss = 6*gcaa; \
+    c = (c & utf8_table3[gcaa]) << gcss; \
+    while (gcaa-- > 0) \
+      { \
+      gcss -= 6; \
+      c |= (*eptr++ & 0x3f) << gcss; \
+      } \
+    }
+
+/* Get the next character, testing for UTF-8 mode, and advancing the pointer */
+
+#define GETCHARINCTEST(c, eptr) \
+  c = *eptr++; \
+  if (md->utf8 && (c & 0xc0) == 0xc0) \
+    { \
+    int gcaa = utf8_table4[c & 0x3f];  /* Number of additional bytes */ \
+    int gcss = 6*gcaa; \
+    c = (c & utf8_table3[gcaa]) << gcss; \
+    while (gcaa-- > 0) \
+      { \
+      gcss -= 6; \
+      c |= (*eptr++ & 0x3f) << gcss; \
+      } \
+    }
+
+/* Get the next UTF-8 character, not advancing the pointer, incrementing length
+if there are extra bytes. This is called when we know we are in UTF-8 mode. */
+
+#define GETCHARLEN(c, eptr, len) \
+  c = *eptr; \
+  if ((c & 0xc0) == 0xc0) \
+    { \
+    int gcii; \
+    int gcaa = utf8_table4[c & 0x3f];  /* Number of additional bytes */ \
+    int gcss = 6*gcaa; \
+    c = (c & utf8_table3[gcaa]) << gcss; \
+    for (gcii = 1; gcii <= gcaa; gcii++) \
+      { \
+      gcss -= 6; \
+      c |= (eptr[gcii] & 0x3f) << gcss; \
+      } \
+    len += gcaa; \
+    }
+
+/* If the pointer is not at the start of a character, move it back until
+it is. Called only in UTF-8 mode. */
+
+#define BACKCHAR(eptr) while((*eptr & 0xc0) == 0x80) eptr--;
+
+#endif
+
+
+
+/*************************************************
+*             Default character tables           *
+*************************************************/
+
+/* A default set of character tables is included in the PCRE binary. Its source
+is built by the maketables auxiliary program, which uses the default C ctypes
+functions, and put in the file chartables.c. These tables are used by PCRE
+whenever the caller of pcre_compile() does not provide an alternate set of
+tables. */
+
+#include "chartables.c"
+
+
+
+#ifdef SUPPORT_UTF8
+/*************************************************
+*           Tables for UTF-8 support             *
+*************************************************/
+
+/* These are the breakpoints for different numbers of bytes in a UTF-8
+character. */
+
+static const int utf8_table1[] =
+  { 0x7f, 0x7ff, 0xffff, 0x1fffff, 0x3ffffff, 0x7fffffff};
+
+/* These are the indicator bits and the mask for the data bits to set in the
+first byte of a character, indexed by the number of additional bytes. */
+
+static const int utf8_table2[] = { 0,    0xc0, 0xe0, 0xf0, 0xf8, 0xfc};
+static const int utf8_table3[] = { 0xff, 0x1f, 0x0f, 0x07, 0x03, 0x01};
+
+/* Table of the number of extra characters, indexed by the first character
+masked with 0x3f. The highest number for a valid UTF-8 character is in fact
+0x3d. */
+
+static const uschar utf8_table4[] = {
+  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,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 };
+
+
+/*************************************************
+*       Convert character value to UTF-8         *
+*************************************************/
+
+/* This function takes an integer value in the range 0 - 0x7fffffff
+and encodes it as a UTF-8 character in 0 to 6 bytes.
+
+Arguments:
+  cvalue     the character value
+  buffer     pointer to buffer for result - at least 6 bytes long
+
+Returns:     number of characters placed in the buffer
+*/
+
+static int
+ord2utf8(int cvalue, uschar *buffer)
+{
+register int i, j;
+for (i = 0; i < sizeof(utf8_table1)/sizeof(int); i++)
+  if (cvalue <= utf8_table1[i]) break;
+buffer += i;
+for (j = i; j > 0; j--)
+ {
+ *buffer-- = 0x80 | (cvalue & 0x3f);
+ cvalue >>= 6;
+ }
+*buffer = utf8_table2[i] | cvalue;
+return i + 1;
+}
+#endif
+
+
+
+/*************************************************
+*         Print compiled regex                   *
+*************************************************/
+
+/* The code for doing this is held in a separate file that is also included in
+pcretest.c. It defines a function called print_internals(). */
+
+#ifdef DEBUG
+#include "printint.c"
+#endif
+
+
+
+/*************************************************
+*          Return version string                 *
+*************************************************/
+
+#define STRING(a)  # a
+#define XSTRING(s) STRING(s)
+
+EXPORT const char *
+pcre_version(void)
+{
+return XSTRING(PCRE_MAJOR) "." XSTRING(PCRE_MINOR) " " XSTRING(PCRE_DATE);
+}
+
+
+
+
+/*************************************************
+*         Flip bytes in an integer               *
+*************************************************/
+
+/* This function is called when the magic number in a regex doesn't match in
+order to flip its bytes to see if we are dealing with a pattern that was
+compiled on a host of different endianness. If so, this function is used to
+flip other byte values.
+
+Arguments:
+  value        the number to flip
+  n            the number of bytes to flip (assumed to be 2 or 4)
+
+Returns:       the flipped value
+*/
+
+static pcre_uint16
+byteflip2(pcre_uint16 value)
+{
+return ((value & 0x00ff) << 8) |
+       ((value & 0xff00) >> 8);
+}
+
+static pcre_uint32
+byteflip4(pcre_uint32 value)
+{
+return ((value & 0x000000ff) << 24) |
+       ((value & 0x0000ff00) <<  8) |
+       ((value & 0x00ff0000) >>  8) |
+       ((value & 0xff000000) >> 24);
+}
+
+/*************************************************
+*       Test for a byte-flipped compiled regex   *
+*************************************************/
+
+/* This function is called from pce_exec() and also from pcre_fullinfo(). Its
+job is to test whether the regex is byte-flipped - that is, it was compiled on
+a system of opposite endianness. The function is called only when the native
+MAGIC_NUMBER test fails. If the regex is indeed flipped, we flip all the
+relevant values into a different data block, and return it.
+
+Arguments:
+  re               points to the regex
+  study            points to study data, or NULL
+  internal_re      points to a new regex block
+  internal_study   points to a new study block
+
+Returns:           the new block if is is indeed a byte-flipped regex
+                   NULL if it is not
+*/
+
+static real_pcre *
+try_flipped(const real_pcre *re, real_pcre *internal_re,
+  const pcre_study_data *study, pcre_study_data *internal_study)
+{
+if (byteflip4(re->magic_number) != MAGIC_NUMBER)
+  return NULL;
+
+*internal_re = *re;           /* To copy other fields */
+internal_re->size = byteflip4(re->size);
+internal_re->options = byteflip4(re->options);
+internal_re->top_bracket = byteflip2(re->top_bracket);
+internal_re->top_backref = byteflip2(re->top_backref);
+internal_re->first_byte = byteflip2(re->first_byte);
+internal_re->req_byte = byteflip2(re->req_byte);
+internal_re->name_table_offset = byteflip2(re->name_table_offset);
+internal_re->name_entry_size = byteflip2(re->name_entry_size);
+internal_re->name_count = byteflip2(re->name_count);
+
+if (study != NULL)
+  {
+  *internal_study = *study;   /* To copy other fields */
+  internal_study->size = byteflip4(study->size);
+  internal_study->options = byteflip4(study->options);
+  }
+
+return internal_re;
+}
+
+
+
+/*************************************************
+* (Obsolete) Return info about compiled pattern  *
+*************************************************/
+
+/* This is the original "info" function. It picks potentially useful data out
+of the private structure, but its interface was too rigid. It remains for
+backwards compatibility. The public options are passed back in an int - though
+the re->options field has been expanded to a long int, all the public options
+at the low end of it, and so even on 16-bit systems this will still be OK.
+Therefore, I haven't changed the API for pcre_info().
+
+Arguments:
+  argument_re   points to compiled code
+  optptr        where to pass back the options
+  first_byte    where to pass back the first character,
+                or -1 if multiline and all branches start ^,
+                or -2 otherwise
+
+Returns:        number of capturing subpatterns
+                or negative values on error
+*/
+
+EXPORT int
+pcre_info(const pcre *argument_re, int *optptr, int *first_byte)
+{
+real_pcre internal_re;
+const real_pcre *re = (const real_pcre *)argument_re;
+if (re == NULL) return PCRE_ERROR_NULL;
+if (re->magic_number != MAGIC_NUMBER)
+  {
+  re = try_flipped(re, &internal_re, NULL, NULL);
+  if (re == NULL) return PCRE_ERROR_BADMAGIC;
+  }
+if (optptr != NULL) *optptr = (int)(re->options & PUBLIC_OPTIONS);
+if (first_byte != NULL)
+  *first_byte = ((re->options & PCRE_FIRSTSET) != 0)? re->first_byte :
+     ((re->options & PCRE_STARTLINE) != 0)? -1 : -2;
+return re->top_bracket;
+}
+
+
+
+/*************************************************
+*        Return info about compiled pattern      *
+*************************************************/
+
+/* This is a newer "info" function which has an extensible interface so
+that additional items can be added compatibly.
+
+Arguments:
+  argument_re      points to compiled code
+  extra_data       points extra data, or NULL
+  what             what information is required
+  where            where to put the information
+
+Returns:           0 if data returned, negative on error
+*/
+
+EXPORT int
+pcre_fullinfo(const pcre *argument_re, const pcre_extra *extra_data, int what,
+  void *where)
+{
+real_pcre internal_re;
+pcre_study_data internal_study;
+const real_pcre *re = (const real_pcre *)argument_re;
+const pcre_study_data *study = NULL;
+
+if (re == NULL || where == NULL) return PCRE_ERROR_NULL;
+
+if (extra_data != NULL && (extra_data->flags & PCRE_EXTRA_STUDY_DATA) != 0)
+  study = (const pcre_study_data *)extra_data->study_data;
+
+if (re->magic_number != MAGIC_NUMBER)
+  {
+  re = try_flipped(re, &internal_re, study, &internal_study);
+  if (re == NULL) return PCRE_ERROR_BADMAGIC;
+  if (study != NULL) study = &internal_study;
+  }
+
+switch (what)
+  {
+  case PCRE_INFO_OPTIONS:
+  *((unsigned long int *)where) = re->options & PUBLIC_OPTIONS;
+  break;
+
+  case PCRE_INFO_SIZE:
+  *((size_t *)where) = re->size;
+  break;
+
+  case PCRE_INFO_STUDYSIZE:
+  *((size_t *)where) = (study == NULL)? 0 : study->size;
+  break;
+
+  case PCRE_INFO_CAPTURECOUNT:
+  *((int *)where) = re->top_bracket;
+  break;
+
+  case PCRE_INFO_BACKREFMAX:
+  *((int *)where) = re->top_backref;
+  break;
+
+  case PCRE_INFO_FIRSTBYTE:
+  *((int *)where) =
+    ((re->options & PCRE_FIRSTSET) != 0)? re->first_byte :
+    ((re->options & PCRE_STARTLINE) != 0)? -1 : -2;
+  break;
+
+  /* Make sure we pass back the pointer to the bit vector in the external
+  block, not the internal copy (with flipped integer fields). */
+
+  case PCRE_INFO_FIRSTTABLE:
+  *((const uschar **)where) =
+    (study != NULL && (study->options & PCRE_STUDY_MAPPED) != 0)?
+      ((const pcre_study_data *)extra_data->study_data)->start_bits : NULL;
+  break;
+
+  case PCRE_INFO_LASTLITERAL:
+  *((int *)where) =
+    ((re->options & PCRE_REQCHSET) != 0)? re->req_byte : -1;
+  break;
+
+  case PCRE_INFO_NAMEENTRYSIZE:
+  *((int *)where) = re->name_entry_size;
+  break;
+
+  case PCRE_INFO_NAMECOUNT:
+  *((int *)where) = re->name_count;
+  break;
+
+  case PCRE_INFO_NAMETABLE:
+  *((const uschar **)where) = (const uschar *)re + re->name_table_offset;
+  break;
+
+  case PCRE_INFO_DEFAULT_TABLES:
+  *((const uschar **)where) = (const uschar *)pcre_default_tables;
+  break;
+
+  default: return PCRE_ERROR_BADOPTION;
+  }
+
+return 0;
+}
+
+
+
+/*************************************************
+* Return info about what features are configured *
+*************************************************/
+
+/* This is function which has an extensible interface so that additional items
+can be added compatibly.
+
+Arguments:
+  what             what information is required
+  where            where to put the information
+
+Returns:           0 if data returned, negative on error
+*/
+
+EXPORT int
+pcre_config(int what, void *where)
+{
+switch (what)
+  {
+  case PCRE_CONFIG_UTF8:
+#ifdef SUPPORT_UTF8
+  *((int *)where) = 1;
+#else
+  *((int *)where) = 0;
+#endif
+  break;
+
+  case PCRE_CONFIG_UNICODE_PROPERTIES:
+#ifdef SUPPORT_UCP
+  *((int *)where) = 1;
+#else
+  *((int *)where) = 0;
+#endif
+  break;
+
+  case PCRE_CONFIG_NEWLINE:
+  *((int *)where) = NEWLINE;
+  break;
+
+  case PCRE_CONFIG_LINK_SIZE:
+  *((int *)where) = LINK_SIZE;
+  break;
+
+  case PCRE_CONFIG_POSIX_MALLOC_THRESHOLD:
+  *((int *)where) = POSIX_MALLOC_THRESHOLD;
+  break;
+
+  case PCRE_CONFIG_MATCH_LIMIT:
+  *((unsigned int *)where) = MATCH_LIMIT;
+  break;
+
+  case PCRE_CONFIG_STACKRECURSE:
+#ifdef NO_RECURSE
+  *((int *)where) = 0;
+#else
+  *((int *)where) = 1;
+#endif
+  break;
+
+  default: return PCRE_ERROR_BADOPTION;
+  }
+
+return 0;
+}
+
+
+
+#ifdef DEBUG
+/*************************************************
+*        Debugging function to print chars       *
+*************************************************/
+
+/* Print a sequence of chars in printable format, stopping at the end of the
+subject if the requested.
+
+Arguments:
+  p           points to characters
+  length      number to print
+  is_subject  TRUE if printing from within md->start_subject
+  md          pointer to matching data block, if is_subject is TRUE
+
+Returns:     nothing
+*/
+
+static void
+pchars(const uschar *p, int length, BOOL is_subject, match_data *md)
+{
+int c;
+if (is_subject && length > md->end_subject - p) length = md->end_subject - p;
+while (length-- > 0)
+  if (isprint(c = *(p++))) printf("%c", c); else printf("\\x%02x", c);
+}
+#endif
+
+
+
+
+/*************************************************
+*            Handle escapes                      *
+*************************************************/
+
+/* This function is called when a \ has been encountered. It either returns a
+positive value for a simple escape such as \n, or a negative value which
+encodes one of the more complicated things such as \d. When UTF-8 is enabled,
+a positive value greater than 255 may be returned. On entry, ptr is pointing at
+the \. On exit, it is on the final character of the escape sequence.
+
+Arguments:
+  ptrptr     points to the pattern position pointer
+  errorptr   points to the pointer to the error message
+  bracount   number of previous extracting brackets
+  options    the options bits
+  isclass    TRUE if inside a character class
+
+Returns:     zero or positive => a data character
+             negative => a special escape sequence
+             on error, errorptr is set
+*/
+
+static int
+check_escape(const uschar **ptrptr, const char **errorptr, int bracount,
+  int options, BOOL isclass)
+{
+const uschar *ptr = *ptrptr;
+int c, i;
+
+/* If backslash is at the end of the pattern, it's an error. */
+
+c = *(++ptr);
+if (c == 0) *errorptr = ERR1;
+
+/* Non-alphamerics are literals. For digits or letters, do an initial lookup in
+a table. A non-zero result is something that can be returned immediately.
+Otherwise further processing may be required. */
+
+#if !EBCDIC    /* ASCII coding */
+else if (c < '0' || c > 'z') {}                           /* Not alphameric */
+else if ((i = escapes[c - '0']) != 0) c = i;
+
+#else          /* EBCDIC coding */
+else if (c < 'a' || (ebcdic_chartab[c] & 0x0E) == 0) {}   /* Not alphameric */
+else if ((i = escapes[c - 0x48]) != 0)  c = i;
+#endif
+
+/* Escapes that need further processing, or are illegal. */
+
+else
+  {
+  const uschar *oldptr;
+  switch (c)
+    {
+    /* A number of Perl escapes are not handled by PCRE. We give an explicit
+    error. */
+
+    case 'l':
+    case 'L':
+    case 'N':
+    case 'u':
+    case 'U':
+    *errorptr = ERR37;
+    break;
+
+    /* The handling of escape sequences consisting of a string of digits
+    starting with one that is not zero is not straightforward. By experiment,
+    the way Perl works seems to be as follows:
+
+    Outside a character class, the digits are read as a decimal number. If the
+    number is less than 10, or if there are that many previous extracting
+    left brackets, then it is a back reference. Otherwise, up to three octal
+    digits are read to form an escaped byte. Thus \123 is likely to be octal
+    123 (cf \0123, which is octal 012 followed by the literal 3). If the octal
+    value is greater than 377, the least significant 8 bits are taken. Inside a
+    character class, \ followed by a digit is always an octal number. */
+
+    case '1': case '2': case '3': case '4': case '5':
+    case '6': case '7': case '8': case '9':
+
+    if (!isclass)
+      {
+      oldptr = ptr;
+      c -= '0';
+      while ((digitab[ptr[1]] & ctype_digit) != 0)
+        c = c * 10 + *(++ptr) - '0';
+      if (c < 10 || c <= bracount)
+        {
+        c = -(ESC_REF + c);
+        break;
+        }
+      ptr = oldptr;      /* Put the pointer back and fall through */
+      }
+
+    /* Handle an octal number following \. If the first digit is 8 or 9, Perl
+    generates a binary zero byte and treats the digit as a following literal.
+    Thus we have to pull back the pointer by one. */
+
+    if ((c = *ptr) >= '8')
+      {
+      ptr--;
+      c = 0;
+      break;
+      }
+
+    /* \0 always starts an octal number, but we may drop through to here with a
+    larger first octal digit. */
+
+    case '0':
+    c -= '0';
+    while(i++ < 2 && ptr[1] >= '0' && ptr[1] <= '7')
+        c = c * 8 + *(++ptr) - '0';
+    c &= 255;     /* Take least significant 8 bits */
+    break;
+
+    /* \x is complicated when UTF-8 is enabled. \x{ddd} is a character number
+    which can be greater than 0xff, but only if the ddd are hex digits. */
+
+    case 'x':
+#ifdef SUPPORT_UTF8
+    if (ptr[1] == '{' && (options & PCRE_UTF8) != 0)
+      {
+      const uschar *pt = ptr + 2;
+      register int count = 0;
+      c = 0;
+      while ((digitab[*pt] & ctype_xdigit) != 0)
+        {
+        int cc = *pt++;
+        count++;
+#if !EBCDIC    /* ASCII coding */
+        if (cc >= 'a') cc -= 32;               /* Convert to upper case */
+        c = c * 16 + cc - ((cc < 'A')? '0' : ('A' - 10));
+#else          /* EBCDIC coding */
+        if (cc >= 'a' && cc <= 'z') cc += 64;  /* Convert to upper case */
+        c = c * 16 + cc - ((cc >= '0')? '0' : ('A' - 10));
+#endif
+        }
+      if (*pt == '}')
+        {
+        if (c < 0 || count > 8) *errorptr = ERR34;
+        ptr = pt;
+        break;
+        }
+      /* If the sequence of hex digits does not end with '}', then we don't
+      recognize this construct; fall through to the normal \x handling. */
+      }
+#endif
+
+    /* Read just a single hex char */
+
+    c = 0;
+    while (i++ < 2 && (digitab[ptr[1]] & ctype_xdigit) != 0)
+      {
+      int cc;                               /* Some compilers don't like ++ */
+      cc = *(++ptr);                        /* in initializers */
+#if !EBCDIC    /* ASCII coding */
+      if (cc >= 'a') cc -= 32;              /* Convert to upper case */
+      c = c * 16 + cc - ((cc < 'A')? '0' : ('A' - 10));
+#else          /* EBCDIC coding */
+      if (cc <= 'z') cc += 64;              /* Convert to upper case */
+      c = c * 16 + cc - ((cc >= '0')? '0' : ('A' - 10));
+#endif
+      }
+    break;
+
+    /* Other special escapes not starting with a digit are straightforward */
+
+    case 'c':
+    c = *(++ptr);
+    if (c == 0)
+      {
+      *errorptr = ERR2;
+      return 0;
+      }
+
+    /* A letter is upper-cased; then the 0x40 bit is flipped. This coding
+    is ASCII-specific, but then the whole concept of \cx is ASCII-specific.
+    (However, an EBCDIC equivalent has now been added.) */
+
+#if !EBCDIC    /* ASCII coding */
+    if (c >= 'a' && c <= 'z') c -= 32;
+    c ^= 0x40;
+#else          /* EBCDIC coding */
+    if (c >= 'a' && c <= 'z') c += 64;
+    c ^= 0xC0;
+#endif
+    break;
+
+    /* PCRE_EXTRA enables extensions to Perl in the matter of escapes. Any
+    other alphameric following \ is an error if PCRE_EXTRA was set; otherwise,
+    for Perl compatibility, it is a literal. This code looks a bit odd, but
+    there used to be some cases other than the default, and there may be again
+    in future, so I haven't "optimized" it. */
+
+    default:
+    if ((options & PCRE_EXTRA) != 0) switch(c)
+      {
+      default:
+      *errorptr = ERR3;
+      break;
+      }
+    break;
+    }
+  }
+
+*ptrptr = ptr;
+return c;
+}
+
+
+
+#ifdef SUPPORT_UCP
+/*************************************************
+*               Handle \P and \p                 *
+*************************************************/
+
+/* This function is called after \P or \p has been encountered, provided that
+PCRE is compiled with support for Unicode properties. On entry, ptrptr is
+pointing at the P or p. On exit, it is pointing at the final character of the
+escape sequence.
+
+Argument:
+  ptrptr     points to the pattern position pointer
+  negptr     points to a boolean that is set TRUE for negation else FALSE
+  errorptr   points to the pointer to the error message
+
+Returns:     value from ucp_type_table, or -1 for an invalid type
+*/
+
+static int
+get_ucp(const uschar **ptrptr, BOOL *negptr, const char **errorptr)
+{
+int c, i, bot, top;
+const uschar *ptr = *ptrptr;
+char name[4];
+
+c = *(++ptr);
+if (c == 0) goto ERROR_RETURN;
+
+*negptr = FALSE;
+
+/* \P or \p can be followed by a one- or two-character name in {}, optionally
+preceded by ^ for negation. */
+
+if (c == '{')
+  {
+  if (ptr[1] == '^')
+    {
+    *negptr = TRUE;
+    ptr++;
+    }
+  for (i = 0; i <= 2; i++)
+    {
+    c = *(++ptr);
+    if (c == 0) goto ERROR_RETURN;
+    if (c == '}') break;
+    name[i] = c;
+    }
+  if (c !='}')   /* Try to distinguish error cases */
+    {
+    while (*(++ptr) != 0 && *ptr != '}');
+    if (*ptr == '}') goto UNKNOWN_RETURN; else goto ERROR_RETURN;
+    }
+  name[i] = 0;
+  }
+
+/* Otherwise there is just one following character */
+
+else
+  {
+  name[0] = c;
+  name[1] = 0;
+  }
+
+*ptrptr = ptr;
+
+/* Search for a recognized property name using binary chop */
+
+bot = 0;
+top = sizeof(utt)/sizeof(ucp_type_table);
+
+while (bot < top)
+  {
+  i = (bot + top)/2;
+  c = strcmp(name, utt[i].name);
+  if (c == 0) return utt[i].value;
+  if (c > 0) bot = i + 1; else top = i;
+  }
+
+UNKNOWN_RETURN:
+*errorptr = ERR47;
+*ptrptr = ptr;
+return -1;
+
+ERROR_RETURN:
+*errorptr = ERR46;
+*ptrptr = ptr;
+return -1;
+}
+#endif
+
+
+
+
+/*************************************************
+*            Check for counted repeat            *
+*************************************************/
+
+/* This function is called when a '{' is encountered in a place where it might
+start a quantifier. It looks ahead to see if it really is a quantifier or not.
+It is only a quantifier if it is one of the forms {ddd} {ddd,} or {ddd,ddd}
+where the ddds are digits.
+
+Arguments:
+  p         pointer to the first char after '{'
+
+Returns:    TRUE or FALSE
+*/
+
+static BOOL
+is_counted_repeat(const uschar *p)
+{
+if ((digitab[*p++] & ctype_digit) == 0) return FALSE;
+while ((digitab[*p] & ctype_digit) != 0) p++;
+if (*p == '}') return TRUE;
+
+if (*p++ != ',') return FALSE;
+if (*p == '}') return TRUE;
+
+if ((digitab[*p++] & ctype_digit) == 0) return FALSE;
+while ((digitab[*p] & ctype_digit) != 0) p++;
+
+return (*p == '}');
+}
+
+
+
+/*************************************************
+*         Read repeat counts                     *
+*************************************************/
+
+/* Read an item of the form {n,m} and return the values. This is called only
+after is_counted_repeat() has confirmed that a repeat-count quantifier exists,
+so the syntax is guaranteed to be correct, but we need to check the values.
+
+Arguments:
+  p          pointer to first char after '{'
+  minp       pointer to int for min
+  maxp       pointer to int for max
+             returned as -1 if no max
+  errorptr   points to pointer to error message
+
+Returns:     pointer to '}' on success;
+             current ptr on error, with errorptr set
+*/
+
+static const uschar *
+read_repeat_counts(const uschar *p, int *minp, int *maxp, const char **errorptr)
+{
+int min = 0;
+int max = -1;
+
+/* Read the minimum value and do a paranoid check: a negative value indicates
+an integer overflow. */
+
+while ((digitab[*p] & ctype_digit) != 0) min = min * 10 + *p++ - '0';
+if (min < 0 || min > 65535)
+  {
+  *errorptr = ERR5;
+  return p;
+  }
+
+/* Read the maximum value if there is one, and again do a paranoid on its size.
+Also, max must not be less than min. */
+
+if (*p == '}') max = min; else
+  {
+  if (*(++p) != '}')
+    {
+    max = 0;
+    while((digitab[*p] & ctype_digit) != 0) max = max * 10 + *p++ - '0';
+    if (max < 0 || max > 65535)
+      {
+      *errorptr = ERR5;
+      return p;
+      }
+    if (max < min)
+      {
+      *errorptr = ERR4;
+      return p;
+      }
+    }
+  }
+
+/* Fill in the required variables, and pass back the pointer to the terminating
+'}'. */
+
+*minp = min;
+*maxp = max;
+return p;
+}
+
+
+
+/*************************************************
+*      Find first significant op code            *
+*************************************************/
+
+/* This is called by several functions that scan a compiled expression looking
+for a fixed first character, or an anchoring op code etc. It skips over things
+that do not influence this. For some calls, a change of option is important.
+For some calls, it makes sense to skip negative forward and all backward
+assertions, and also the \b assertion; for others it does not.
+
+Arguments:
+  code         pointer to the start of the group
+  options      pointer to external options
+  optbit       the option bit whose changing is significant, or
+                 zero if none are
+  skipassert   TRUE if certain assertions are to be skipped
+
+Returns:       pointer to the first significant opcode
+*/
+
+static const uschar*
+first_significant_code(const uschar *code, int *options, int optbit,
+  BOOL skipassert)
+{
+for (;;)
+  {
+  switch ((int)*code)
+    {
+    case OP_OPT:
+    if (optbit > 0 && ((int)code[1] & optbit) != (*options & optbit))
+      *options = (int)code[1];
+    code += 2;
+    break;
+
+    case OP_ASSERT_NOT:
+    case OP_ASSERTBACK:
+    case OP_ASSERTBACK_NOT:
+    if (!skipassert) return code;
+    do code += GET(code, 1); while (*code == OP_ALT);
+    code += OP_lengths[*code];
+    break;
+
+    case OP_WORD_BOUNDARY:
+    case OP_NOT_WORD_BOUNDARY:
+    if (!skipassert) return code;
+    /* Fall through */
+
+    case OP_CALLOUT:
+    case OP_CREF:
+    case OP_BRANUMBER:
+    code += OP_lengths[*code];
+    break;
+
+    default:
+    return code;
+    }
+  }
+/* Control never reaches here */
+}
+
+
+
+
+/*************************************************
+*        Find the fixed length of a pattern      *
+*************************************************/
+
+/* Scan a pattern and compute the fixed length of subject that will match it,
+if the length is fixed. This is needed for dealing with backward assertions.
+In UTF8 mode, the result is in characters rather than bytes.
+
+Arguments:
+  code     points to the start of the pattern (the bracket)
+  options  the compiling options
+
+Returns:   the fixed length, or -1 if there is no fixed length,
+             or -2 if \C was encountered
+*/
+
+static int
+find_fixedlength(uschar *code, int options)
+{
+int length = -1;
+
+register int branchlength = 0;
+register uschar *cc = code + 1 + LINK_SIZE;
+
+/* Scan along the opcodes for this branch. If we get to the end of the
+branch, check the length against that of the other branches. */
+
+for (;;)
+  {
+  int d;
+  register int op = *cc;
+  if (op >= OP_BRA) op = OP_BRA;
+
+  switch (op)
+    {
+    case OP_BRA:
+    case OP_ONCE:
+    case OP_COND:
+    d = find_fixedlength(cc, options);
+    if (d < 0) return d;
+    branchlength += d;
+    do cc += GET(cc, 1); while (*cc == OP_ALT);
+    cc += 1 + LINK_SIZE;
+    break;
+
+    /* Reached end of a branch; if it's a ket it is the end of a nested
+    call. If it's ALT it is an alternation in a nested call. If it is
+    END it's the end of the outer call. All can be handled by the same code. */
+
+    case OP_ALT:
+    case OP_KET:
+    case OP_KETRMAX:
+    case OP_KETRMIN:
+    case OP_END:
+    if (length < 0) length = branchlength;
+      else if (length != branchlength) return -1;
+    if (*cc != OP_ALT) return length;
+    cc += 1 + LINK_SIZE;
+    branchlength = 0;
+    break;
+
+    /* Skip over assertive subpatterns */
+
+    case OP_ASSERT:
+    case OP_ASSERT_NOT:
+    case OP_ASSERTBACK:
+    case OP_ASSERTBACK_NOT:
+    do cc += GET(cc, 1); while (*cc == OP_ALT);
+    /* Fall through */
+
+    /* Skip over things that don't match chars */
+
+    case OP_REVERSE:
+    case OP_BRANUMBER:
+    case OP_CREF:
+    case OP_OPT:
+    case OP_CALLOUT:
+    case OP_SOD:
+    case OP_SOM:
+    case OP_EOD:
+    case OP_EODN:
+    case OP_CIRC:
+    case OP_DOLL:
+    case OP_NOT_WORD_BOUNDARY:
+    case OP_WORD_BOUNDARY:
+    cc += OP_lengths[*cc];
+    break;
+
+    /* Handle literal characters */
+
+    case OP_CHAR:
+    case OP_CHARNC:
+    branchlength++;
+    cc += 2;
+#ifdef SUPPORT_UTF8
+    if ((options & PCRE_UTF8) != 0)
+      {
+      while ((*cc & 0xc0) == 0x80) cc++;
+      }
+#endif
+    break;
+
+    /* Handle exact repetitions. The count is already in characters, but we
+    need to skip over a multibyte character in UTF8 mode.  */
+
+    case OP_EXACT:
+    branchlength += GET2(cc,1);
+    cc += 4;
+#ifdef SUPPORT_UTF8
+    if ((options & PCRE_UTF8) != 0)
+      {
+      while((*cc & 0x80) == 0x80) cc++;
+      }
+#endif
+    break;
+
+    case OP_TYPEEXACT:
+    branchlength += GET2(cc,1);
+    cc += 4;
+    break;
+
+    /* Handle single-char matchers */
+
+    case OP_PROP:
+    case OP_NOTPROP:
+    cc++;
+    /* Fall through */
+
+    case OP_NOT_DIGIT:
+    case OP_DIGIT:
+    case OP_NOT_WHITESPACE:
+    case OP_WHITESPACE:
+    case OP_NOT_WORDCHAR:
+    case OP_WORDCHAR:
+    case OP_ANY:
+    branchlength++;
+    cc++;
+    break;
+
+    /* The single-byte matcher isn't allowed */
+
+    case OP_ANYBYTE:
+    return -2;
+
+    /* Check a class for variable quantification */
+
+#ifdef SUPPORT_UTF8
+    case OP_XCLASS:
+    cc += GET(cc, 1) - 33;
+    /* Fall through */
+#endif
+
+    case OP_CLASS:
+    case OP_NCLASS:
+    cc += 33;
+
+    switch (*cc)
+      {
+      case OP_CRSTAR:
+      case OP_CRMINSTAR:
+      case OP_CRQUERY:
+      case OP_CRMINQUERY:
+      return -1;
+
+      case OP_CRRANGE:
+      case OP_CRMINRANGE:
+      if (GET2(cc,1) != GET2(cc,3)) return -1;
+      branchlength += GET2(cc,1);
+      cc += 5;
+      break;
+
+      default:
+      branchlength++;
+      }
+    break;
+
+    /* Anything else is variable length */
+
+    default:
+    return -1;
+    }
+  }
+/* Control never gets here */
+}
+
+
+
+
+/*************************************************
+*    Scan compiled regex for numbered bracket    *
+*************************************************/
+
+/* This little function scans through a compiled pattern until it finds a
+capturing bracket with the given number.
+
+Arguments:
+  code        points to start of expression
+  utf8        TRUE in UTF-8 mode
+  number      the required bracket number
+
+Returns:      pointer to the opcode for the bracket, or NULL if not found
+*/
+
+static const uschar *
+find_bracket(const uschar *code, BOOL utf8, int number)
+{
+#ifndef SUPPORT_UTF8
+utf8 = utf8;               /* Stop pedantic compilers complaining */
+#endif
+
+for (;;)
+  {
+  register int c = *code;
+  if (c == OP_END) return NULL;
+  else if (c > OP_BRA)
+    {
+    int n = c - OP_BRA;
+    if (n > EXTRACT_BASIC_MAX) n = GET2(code, 2+LINK_SIZE);
+    if (n == number) return (uschar *)code;
+    code += OP_lengths[OP_BRA];
+    }
+  else
+    {
+    code += OP_lengths[c];
+
+#ifdef SUPPORT_UTF8
+
+    /* In UTF-8 mode, opcodes that are followed by a character may be followed
+    by a multi-byte character. The length in the table is a minimum, so we have
+    to scan along to skip the extra bytes. All opcodes are less than 128, so we
+    can use relatively efficient code. */
+
+    if (utf8) switch(c)
+      {
+      case OP_CHAR:
+      case OP_CHARNC:
+      case OP_EXACT:
+      case OP_UPTO:
+      case OP_MINUPTO:
+      case OP_STAR:
+      case OP_MINSTAR:
+      case OP_PLUS:
+      case OP_MINPLUS:
+      case OP_QUERY:
+      case OP_MINQUERY:
+      while ((*code & 0xc0) == 0x80) code++;
+      break;
+
+      /* XCLASS is used for classes that cannot be represented just by a bit
+      map. This includes negated single high-valued characters. The length in
+      the table is zero; the actual length is stored in the compiled code. */
+
+      case OP_XCLASS:
+      code += GET(code, 1) + 1;
+      break;
+      }
+#endif
+    }
+  }
+}
+
+
+
+/*************************************************
+*   Scan compiled regex for recursion reference  *
+*************************************************/
+
+/* This little function scans through a compiled pattern until it finds an
+instance of OP_RECURSE.
+
+Arguments:
+  code        points to start of expression
+  utf8        TRUE in UTF-8 mode
+
+Returns:      pointer to the opcode for OP_RECURSE, or NULL if not found
+*/
+
+static const uschar *
+find_recurse(const uschar *code, BOOL utf8)
+{
+#ifndef SUPPORT_UTF8
+utf8 = utf8;               /* Stop pedantic compilers complaining */
+#endif
+
+for (;;)
+  {
+  register int c = *code;
+  if (c == OP_END) return NULL;
+  else if (c == OP_RECURSE) return code;
+  else if (c > OP_BRA)
+    {
+    code += OP_lengths[OP_BRA];
+    }
+  else
+    {
+    code += OP_lengths[c];
+
+#ifdef SUPPORT_UTF8
+
+    /* In UTF-8 mode, opcodes that are followed by a character may be followed
+    by a multi-byte character. The length in the table is a minimum, so we have
+    to scan along to skip the extra bytes. All opcodes are less than 128, so we
+    can use relatively efficient code. */
+
+    if (utf8) switch(c)
+      {
+      case OP_CHAR:
+      case OP_CHARNC:
+      case OP_EXACT:
+      case OP_UPTO:
+      case OP_MINUPTO:
+      case OP_STAR:
+      case OP_MINSTAR:
+      case OP_PLUS:
+      case OP_MINPLUS:
+      case OP_QUERY:
+      case OP_MINQUERY:
+      while ((*code & 0xc0) == 0x80) code++;
+      break;
+
+      /* XCLASS is used for classes that cannot be represented just by a bit
+      map. This includes negated single high-valued characters. The length in
+      the table is zero; the actual length is stored in the compiled code. */
+
+      case OP_XCLASS:
+      code += GET(code, 1) + 1;
+      break;
+      }
+#endif
+    }
+  }
+}
+
+
+
+/*************************************************
+*    Scan compiled branch for non-emptiness      *
+*************************************************/
+
+/* This function scans through a branch of a compiled pattern to see whether it
+can match the empty string or not. It is called only from could_be_empty()
+below. Note that first_significant_code() skips over assertions. If we hit an
+unclosed bracket, we return "empty" - this means we've struck an inner bracket
+whose current branch will already have been scanned.
+
+Arguments:
+  code        points to start of search
+  endcode     points to where to stop
+  utf8        TRUE if in UTF8 mode
+
+Returns:      TRUE if what is matched could be empty
+*/
+
+static BOOL
+could_be_empty_branch(const uschar *code, const uschar *endcode, BOOL utf8)
+{
+register int c;
+for (code = first_significant_code(code + 1 + LINK_SIZE, NULL, 0, TRUE);
+     code < endcode;
+     code = first_significant_code(code + OP_lengths[c], NULL, 0, TRUE))
+  {
+  const uschar *ccode;
+
+  c = *code;
+
+  if (c >= OP_BRA)
+    {
+    BOOL empty_branch;
+    if (GET(code, 1) == 0) return TRUE;    /* Hit unclosed bracket */
+
+    /* Scan a closed bracket */
+
+    empty_branch = FALSE;
+    do
+      {
+      if (!empty_branch && could_be_empty_branch(code, endcode, utf8))
+        empty_branch = TRUE;
+      code += GET(code, 1);
+      }
+    while (*code == OP_ALT);
+    if (!empty_branch) return FALSE;   /* All branches are non-empty */
+    code += 1 + LINK_SIZE;
+    c = *code;
+    }
+
+  else switch (c)
+    {
+    /* Check for quantifiers after a class */
+
+#ifdef SUPPORT_UTF8
+    case OP_XCLASS:
+    ccode = code + GET(code, 1);
+    goto CHECK_CLASS_REPEAT;
+#endif
+
+    case OP_CLASS:
+    case OP_NCLASS:
+    ccode = code + 33;
+
+#ifdef SUPPORT_UTF8
+    CHECK_CLASS_REPEAT:
+#endif
+
+    switch (*ccode)
+      {
+      case OP_CRSTAR:            /* These could be empty; continue */
+      case OP_CRMINSTAR:
+      case OP_CRQUERY:
+      case OP_CRMINQUERY:
+      break;
+
+      default:                   /* Non-repeat => class must match */
+      case OP_CRPLUS:            /* These repeats aren't empty */
+      case OP_CRMINPLUS:
+      return FALSE;
+
+      case OP_CRRANGE:
+      case OP_CRMINRANGE:
+      if (GET2(ccode, 1) > 0) return FALSE;  /* Minimum > 0 */
+      break;
+      }
+    break;
+
+    /* Opcodes that must match a character */
+
+    case OP_PROP:
+    case OP_NOTPROP:
+    case OP_EXTUNI:
+    case OP_NOT_DIGIT:
+    case OP_DIGIT:
+    case OP_NOT_WHITESPACE:
+    case OP_WHITESPACE:
+    case OP_NOT_WORDCHAR:
+    case OP_WORDCHAR:
+    case OP_ANY:
+    case OP_ANYBYTE:
+    case OP_CHAR:
+    case OP_CHARNC:
+    case OP_NOT:
+    case OP_PLUS:
+    case OP_MINPLUS:
+    case OP_EXACT:
+    case OP_NOTPLUS:
+    case OP_NOTMINPLUS:
+    case OP_NOTEXACT:
+    case OP_TYPEPLUS:
+    case OP_TYPEMINPLUS:
+    case OP_TYPEEXACT:
+    return FALSE;
+
+    /* End of branch */
+
+    case OP_KET:
+    case OP_KETRMAX:
+    case OP_KETRMIN:
+    case OP_ALT:
+    return TRUE;
+
+    /* In UTF-8 mode, STAR, MINSTAR, QUERY, MINQUERY, UPTO, and MINUPTO  may be
+    followed by a multibyte character */
+
+#ifdef SUPPORT_UTF8
+    case OP_STAR:
+    case OP_MINSTAR:
+    case OP_QUERY:
+    case OP_MINQUERY:
+    case OP_UPTO:
+    case OP_MINUPTO:
+    if (utf8) while ((code[2] & 0xc0) == 0x80) code++;
+    break;
+#endif
+    }
+  }
+
+return TRUE;
+}
+
+
+
+/*************************************************
+*    Scan compiled regex for non-emptiness       *
+*************************************************/
+
+/* This function is called to check for left recursive calls. We want to check
+the current branch of the current pattern to see if it could match the empty
+string. If it could, we must look outwards for branches at other levels,
+stopping when we pass beyond the bracket which is the subject of the recursion.
+
+Arguments:
+  code        points to start of the recursion
+  endcode     points to where to stop (current RECURSE item)
+  bcptr       points to the chain of current (unclosed) branch starts
+  utf8        TRUE if in UTF-8 mode
+
+Returns:      TRUE if what is matched could be empty
+*/
+
+static BOOL
+could_be_empty(const uschar *code, const uschar *endcode, branch_chain *bcptr,
+  BOOL utf8)
+{
+while (bcptr != NULL && bcptr->current >= code)
+  {
+  if (!could_be_empty_branch(bcptr->current, endcode, utf8)) return FALSE;
+  bcptr = bcptr->outer;
+  }
+return TRUE;
+}
+
+
+
+/*************************************************
+*           Check for POSIX class syntax         *
+*************************************************/
+
+/* This function is called when the sequence "[:" or "[." or "[=" is
+encountered in a character class. It checks whether this is followed by an
+optional ^ and then a sequence of letters, terminated by a matching ":]" or
+".]" or "=]".
+
+Argument:
+  ptr      pointer to the initial [
+  endptr   where to return the end pointer
+  cd       pointer to compile data
+
+Returns:   TRUE or FALSE
+*/
+
+static BOOL
+check_posix_syntax(const uschar *ptr, const uschar **endptr, compile_data *cd)
+{
+int terminator;          /* Don't combine these lines; the Solaris cc */
+terminator = *(++ptr);   /* compiler warns about "non-constant" initializer. */
+if (*(++ptr) == '^') ptr++;
+while ((cd->ctypes[*ptr] & ctype_letter) != 0) ptr++;
+if (*ptr == terminator && ptr[1] == ']')
+  {
+  *endptr = ptr;
+  return TRUE;
+  }
+return FALSE;
+}
+
+
+
+
+/*************************************************
+*          Check POSIX class name                *
+*************************************************/
+
+/* This function is called to check the name given in a POSIX-style class entry
+such as [:alnum:].
+
+Arguments:
+  ptr        points to the first letter
+  len        the length of the name
+
+Returns:     a value representing the name, or -1 if unknown
+*/
+
+static int
+check_posix_name(const uschar *ptr, int len)
+{
+register int yield = 0;
+while (posix_name_lengths[yield] != 0)
+  {
+  if (len == posix_name_lengths[yield] &&
+    strncmp((const char *)ptr, posix_names[yield], len) == 0) return yield;
+  yield++;
+  }
+return -1;
+}
+
+
+/*************************************************
+*    Adjust OP_RECURSE items in repeated group   *
+*************************************************/
+
+/* OP_RECURSE items contain an offset from the start of the regex to the group
+that is referenced. This means that groups can be replicated for fixed
+repetition simply by copying (because the recursion is allowed to refer to
+earlier groups that are outside the current group). However, when a group is
+optional (i.e. the minimum quantifier is zero), OP_BRAZERO is inserted before
+it, after it has been compiled. This means that any OP_RECURSE items within it
+that refer to the group itself or any contained groups have to have their
+offsets adjusted. That is the job of this function. Before it is called, the
+partially compiled regex must be temporarily terminated with OP_END.
+
+Arguments:
+  group      points to the start of the group
+  adjust     the amount by which the group is to be moved
+  utf8       TRUE in UTF-8 mode
+  cd         contains pointers to tables etc.
+
+Returns:     nothing
+*/
+
+static void
+adjust_recurse(uschar *group, int adjust, BOOL utf8, compile_data *cd)
+{
+uschar *ptr = group;
+while ((ptr = (uschar *)find_recurse(ptr, utf8)) != NULL)
+  {
+  int offset = GET(ptr, 1);
+  if (cd->start_code + offset >= group) PUT(ptr, 1, offset + adjust);
+  ptr += 1 + LINK_SIZE;
+  }
+}
+
+
+
+/*************************************************
+*        Insert an automatic callout point       *
+*************************************************/
+
+/* This function is called when the PCRE_AUTO_CALLOUT option is set, to insert
+callout points before each pattern item.
+
+Arguments:
+  code           current code pointer
+  ptr            current pattern pointer
+  cd             pointers to tables etc
+
+Returns:         new code pointer
+*/
+
+static uschar *
+auto_callout(uschar *code, const uschar *ptr, compile_data *cd)
+{
+*code++ = OP_CALLOUT;
+*code++ = 255;
+PUT(code, 0, ptr - cd->start_pattern);  /* Pattern offset */
+PUT(code, LINK_SIZE, 0);                /* Default length */
+return code + 2*LINK_SIZE;
+}
+
+
+
+/*************************************************
+*         Complete a callout item                *
+*************************************************/
+
+/* A callout item contains the length of the next item in the pattern, which
+we can't fill in till after we have reached the relevant point. This is used
+for both automatic and manual callouts.
+
+Arguments:
+  previous_callout   points to previous callout item
+  ptr                current pattern pointer
+  cd                 pointers to tables etc
+
+Returns:             nothing
+*/
+
+static void
+complete_callout(uschar *previous_callout, const uschar *ptr, compile_data *cd)
+{
+int length = ptr - cd->start_pattern - GET(previous_callout, 2);
+PUT(previous_callout, 2 + LINK_SIZE, length);
+}
+
+
+
+#ifdef SUPPORT_UCP
+/*************************************************
+*           Get othercase range                  *
+*************************************************/
+
+/* This function is passed the start and end of a class range, in UTF-8 mode
+with UCP support. It searches up the characters, looking for internal ranges of
+characters in the "other" case. Each call returns the next one, updating the
+start address.
+
+Arguments:
+  cptr        points to starting character value; updated
+  d           end value
+  ocptr       where to put start of othercase range
+  odptr       where to put end of othercase range
+
+Yield:        TRUE when range returned; FALSE when no more
+*/
+
+static BOOL
+get_othercase_range(int *cptr, int d, int *ocptr, int *odptr)
+{
+int c, chartype, othercase, next;
+
+for (c = *cptr; c <= d; c++)
+  {
+  if (ucp_findchar(c, &chartype, &othercase) == ucp_L && othercase != 0) break;
+  }
+
+if (c > d) return FALSE;
+
+*ocptr = othercase;
+next = othercase + 1;
+
+for (++c; c <= d; c++)
+  {
+  if (ucp_findchar(c, &chartype, &othercase) != ucp_L || othercase != next)
+    break;
+  next++;
+  }
+
+*odptr = next - 1;
+*cptr = c;
+
+return TRUE;
+}
+#endif  /* SUPPORT_UCP */
+
+
+/*************************************************
+*           Compile one branch                   *
+*************************************************/
+
+/* Scan the pattern, compiling it into the code vector. If the options are
+changed during the branch, the pointer is used to change the external options
+bits.
+
+Arguments:
+  optionsptr     pointer to the option bits
+  brackets       points to number of extracting brackets used
+  codeptr        points to the pointer to the current code point
+  ptrptr         points to the current pattern pointer
+  errorptr       points to pointer to error message
+  firstbyteptr   set to initial literal character, or < 0 (REQ_UNSET, REQ_NONE)
+  reqbyteptr     set to the last literal character required, else < 0
+  bcptr          points to current branch chain
+  cd             contains pointers to tables etc.
+
+Returns:         TRUE on success
+                 FALSE, with *errorptr set on error
+*/
+
+static BOOL
+compile_branch(int *optionsptr, int *brackets, uschar **codeptr,
+  const uschar **ptrptr, const char **errorptr, int *firstbyteptr,
+  int *reqbyteptr, branch_chain *bcptr, compile_data *cd)
+{
+int repeat_type, op_type;
+int repeat_min = 0, repeat_max = 0;      /* To please picky compilers */
+int bravalue = 0;
+int greedy_default, greedy_non_default;
+int firstbyte, reqbyte;
+int zeroreqbyte, zerofirstbyte;
+int req_caseopt, reqvary, tempreqvary;
+int condcount = 0;
+int options = *optionsptr;
+int after_manual_callout = 0;
+register int c;
+register uschar *code = *codeptr;
+uschar *tempcode;
+BOOL inescq = FALSE;
+BOOL groupsetfirstbyte = FALSE;
+const uschar *ptr = *ptrptr;
+const uschar *tempptr;
+uschar *previous = NULL;
+uschar *previous_callout = NULL;
+uschar classbits[32];
+
+#ifdef SUPPORT_UTF8
+BOOL class_utf8;
+BOOL utf8 = (options & PCRE_UTF8) != 0;
+uschar *class_utf8data;
+uschar utf8_char[6];
+#else
+BOOL utf8 = FALSE;
+#endif
+
+/* Set up the default and non-default settings for greediness */
+
+greedy_default = ((options & PCRE_UNGREEDY) != 0);
+greedy_non_default = greedy_default ^ 1;
+
+/* Initialize no first byte, no required byte. REQ_UNSET means "no char
+matching encountered yet". It gets changed to REQ_NONE if we hit something that
+matches a non-fixed char first char; reqbyte just remains unset if we never
+find one.
+
+When we hit a repeat whose minimum is zero, we may have to adjust these values
+to take the zero repeat into account. This is implemented by setting them to
+zerofirstbyte and zeroreqbyte when such a repeat is encountered. The individual
+item types that can be repeated set these backoff variables appropriately. */
+
+firstbyte = reqbyte = zerofirstbyte = zeroreqbyte = REQ_UNSET;
+
+/* The variable req_caseopt contains either the REQ_CASELESS value or zero,
+according to the current setting of the caseless flag. REQ_CASELESS is a bit
+value > 255. It is added into the firstbyte or reqbyte variables to record the
+case status of the value. This is used only for ASCII characters. */
+
+req_caseopt = ((options & PCRE_CASELESS) != 0)? REQ_CASELESS : 0;
+
+/* Switch on next character until the end of the branch */
+
+for (;; ptr++)
+  {
+  BOOL negate_class;
+  BOOL possessive_quantifier;
+  BOOL is_quantifier;
+  int class_charcount;
+  int class_lastchar;
+  int newoptions;
+  int recno;
+  int skipbytes;
+  int subreqbyte;
+  int subfirstbyte;
+  int mclength;
+  uschar mcbuffer[8];
+
+  /* Next byte in the pattern */
+
+  c = *ptr;
+
+  /* If in \Q...\E, check for the end; if not, we have a literal */
+
+  if (inescq && c != 0)
+    {
+    if (c == '\\' && ptr[1] == 'E')
+      {
+      inescq = FALSE;
+      ptr++;
+      continue;
+      }
+    else
+      {
+      if (previous_callout != NULL)
+        {
+        complete_callout(previous_callout, ptr, cd);
+        previous_callout = NULL;
+        }
+      if ((options & PCRE_AUTO_CALLOUT) != 0)
+        {
+        previous_callout = code;
+        code = auto_callout(code, ptr, cd);
+        }
+      goto NORMAL_CHAR;
+      }
+    }
+
+  /* Fill in length of a previous callout, except when the next thing is
+  a quantifier. */
+
+  is_quantifier = c == '*' || c == '+' || c == '?' ||
+    (c == '{' && is_counted_repeat(ptr+1));
+
+  if (!is_quantifier && previous_callout != NULL &&
+       after_manual_callout-- <= 0)
+    {
+    complete_callout(previous_callout, ptr, cd);
+    previous_callout = NULL;
+    }
+
+  /* In extended mode, skip white space and comments */
+
+  if ((options & PCRE_EXTENDED) != 0)
+    {
+    if ((cd->ctypes[c] & ctype_space) != 0) continue;
+    if (c == '#')
+      {
+      /* The space before the ; is to avoid a warning on a silly compiler
+      on the Macintosh. */
+      while ((c = *(++ptr)) != 0 && c != NEWLINE) ;
+      if (c != 0) continue;   /* Else fall through to handle end of string */
+      }
+    }
+
+  /* No auto callout for quantifiers. */
+
+  if ((options & PCRE_AUTO_CALLOUT) != 0 && !is_quantifier)
+    {
+    previous_callout = code;
+    code = auto_callout(code, ptr, cd);
+    }
+
+  switch(c)
+    {
+    /* The branch terminates at end of string, |, or ). */
+
+    case 0:
+    case '|':
+    case ')':
+    *firstbyteptr = firstbyte;
+    *reqbyteptr = reqbyte;
+    *codeptr = code;
+    *ptrptr = ptr;
+    return TRUE;
+
+    /* Handle single-character metacharacters. In multiline mode, ^ disables
+    the setting of any following char as a first character. */
+
+    case '^':
+    if ((options & PCRE_MULTILINE) != 0)
+      {
+      if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE;
+      }
+    previous = NULL;
+    *code++ = OP_CIRC;
+    break;
+
+    case '$':
+    previous = NULL;
+    *code++ = OP_DOLL;
+    break;
+
+    /* There can never be a first char if '.' is first, whatever happens about
+    repeats. The value of reqbyte doesn't change either. */
+
+    case '.':
+    if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE;
+    zerofirstbyte = firstbyte;
+    zeroreqbyte = reqbyte;
+    previous = code;
+    *code++ = OP_ANY;
+    break;
+
+    /* Character classes. If the included characters are all < 255 in value, we
+    build a 32-byte bitmap of the permitted characters, except in the special
+    case where there is only one such character. For negated classes, we build
+    the map as usual, then invert it at the end. However, we use a different
+    opcode so that data characters > 255 can be handled correctly.
+
+    If the class contains characters outside the 0-255 range, a different
+    opcode is compiled. It may optionally have a bit map for characters < 256,
+    but those above are are explicitly listed afterwards. A flag byte tells
+    whether the bitmap is present, and whether this is a negated class or not.
+    */
+
+    case '[':
+    previous = code;
+
+    /* PCRE supports POSIX class stuff inside a class. Perl gives an error if
+    they are encountered at the top level, so we'll do that too. */
+
+    if ((ptr[1] == ':' || ptr[1] == '.' || ptr[1] == '=') &&
+        check_posix_syntax(ptr, &tempptr, cd))
+      {
+      *errorptr = (ptr[1] == ':')? ERR13 : ERR31;
+      goto FAILED;
+      }
+
+    /* If the first character is '^', set the negation flag and skip it. */
+
+    if ((c = *(++ptr)) == '^')
+      {
+      negate_class = TRUE;
+      c = *(++ptr);
+      }
+    else
+      {
+      negate_class = FALSE;
+      }
+
+    /* Keep a count of chars with values < 256 so that we can optimize the case
+    of just a single character (as long as it's < 256). For higher valued UTF-8
+    characters, we don't yet do any optimization. */
+
+    class_charcount = 0;
+    class_lastchar = -1;
+
+#ifdef SUPPORT_UTF8
+    class_utf8 = FALSE;                       /* No chars >= 256 */
+    class_utf8data = code + LINK_SIZE + 34;   /* For UTF-8 items */
+#endif
+
+    /* Initialize the 32-char bit map to all zeros. We have to build the
+    map in a temporary bit of store, in case the class contains only 1
+    character (< 256), because in that case the compiled code doesn't use the
+    bit map. */
+
+    memset(classbits, 0, 32 * sizeof(uschar));
+
+    /* Process characters until ] is reached. By writing this as a "do" it
+    means that an initial ] is taken as a data character. The first pass
+    through the regex checked the overall syntax, so we don't need to be very
+    strict here. At the start of the loop, c contains the first byte of the
+    character. */
+
+    do
+      {
+#ifdef SUPPORT_UTF8
+      if (utf8 && c > 127)
+        {                           /* Braces are required because the */
+        GETCHARLEN(c, ptr, ptr);    /* macro generates multiple statements */
+        }
+#endif
+
+      /* Inside \Q...\E everything is literal except \E */
+
+      if (inescq)
+        {
+        if (c == '\\' && ptr[1] == 'E')
+          {
+          inescq = FALSE;
+          ptr++;
+          continue;
+          }
+        else goto LONE_SINGLE_CHARACTER;
+        }
+
+      /* Handle POSIX class names. Perl allows a negation extension of the
+      form [:^name:]. A square bracket that doesn't match the syntax is
+      treated as a literal. We also recognize the POSIX constructions
+      [.ch.] and [=ch=] ("collating elements") and fault them, as Perl
+      5.6 and 5.8 do. */
+
+      if (c == '[' &&
+          (ptr[1] == ':' || ptr[1] == '.' || ptr[1] == '=') &&
+          check_posix_syntax(ptr, &tempptr, cd))
+        {
+        BOOL local_negate = FALSE;
+        int posix_class, i;
+        register const uschar *cbits = cd->cbits;
+
+        if (ptr[1] != ':')
+          {
+          *errorptr = ERR31;
+          goto FAILED;
+          }
+
+        ptr += 2;
+        if (*ptr == '^')
+          {
+          local_negate = TRUE;
+          ptr++;
+          }
+
+        posix_class = check_posix_name(ptr, tempptr - ptr);
+        if (posix_class < 0)
+          {
+          *errorptr = ERR30;
+          goto FAILED;
+          }
+
+        /* If matching is caseless, upper and lower are converted to
+        alpha. This relies on the fact that the class table starts with
+        alpha, lower, upper as the first 3 entries. */
+
+        if ((options & PCRE_CASELESS) != 0 && posix_class <= 2)
+          posix_class = 0;
+
+        /* Or into the map we are building up to 3 of the static class
+        tables, or their negations. The [:blank:] class sets up the same
+        chars as the [:space:] class (all white space). We remove the vertical
+        white space chars afterwards. */
+
+        posix_class *= 3;
+        for (i = 0; i < 3; i++)
+          {
+          BOOL blankclass = strncmp((char *)ptr, "blank", 5) == 0;
+          int taboffset = posix_class_maps[posix_class + i];
+          if (taboffset < 0) break;
+          if (local_negate)
+            {
+            if (i == 0)
+              for (c = 0; c < 32; c++) classbits[c] |= ~cbits[c+taboffset];
+            else
+              for (c = 0; c < 32; c++) classbits[c] &= ~cbits[c+taboffset];
+            if (blankclass) classbits[1] |= 0x3c;
+            }
+          else
+            {
+            for (c = 0; c < 32; c++) classbits[c] |= cbits[c+taboffset];
+            if (blankclass) classbits[1] &= ~0x3c;
+            }
+          }
+
+        ptr = tempptr + 1;
+        class_charcount = 10;  /* Set > 1; assumes more than 1 per class */
+        continue;    /* End of POSIX syntax handling */
+        }
+
+      /* Backslash may introduce a single character, or it may introduce one
+      of the specials, which just set a flag. Escaped items are checked for
+      validity in the pre-compiling pass. The sequence \b is a special case.
+      Inside a class (and only there) it is treated as backspace. Elsewhere
+      it marks a word boundary. Other escapes have preset maps ready to
+      or into the one we are building. We assume they have more than one
+      character in them, so set class_charcount bigger than one. */
+
+      if (c == '\\')
+        {
+        c = check_escape(&ptr, errorptr, *brackets, options, TRUE);
+
+        if (-c == ESC_b) c = '\b';       /* \b is backslash in a class */
+        else if (-c == ESC_X) c = 'X';   /* \X is literal X in a class */
+        else if (-c == ESC_Q)            /* Handle start of quoted string */
+          {
+          if (ptr[1] == '\\' && ptr[2] == 'E')
+            {
+            ptr += 2; /* avoid empty string */
+            }
+          else inescq = TRUE;
+          continue;
+          }
+
+        if (c < 0)
+          {
+          register const uschar *cbits = cd->cbits;
+          class_charcount += 2;     /* Greater than 1 is what matters */
+          switch (-c)
+            {
+            case ESC_d:
+            for (c = 0; c < 32; c++) classbits[c] |= cbits[c+cbit_digit];
+            continue;
+
+            case ESC_D:
+            for (c = 0; c < 32; c++) classbits[c] |= ~cbits[c+cbit_digit];
+            continue;
+
+            case ESC_w:
+            for (c = 0; c < 32; c++) classbits[c] |= cbits[c+cbit_word];
+            continue;
+
+            case ESC_W:
+            for (c = 0; c < 32; c++) classbits[c] |= ~cbits[c+cbit_word];
+            continue;
+
+            case ESC_s:
+            for (c = 0; c < 32; c++) classbits[c] |= cbits[c+cbit_space];
+            classbits[1] &= ~0x08;   /* Perl 5.004 onwards omits VT from \s */
+            continue;
+
+            case ESC_S:
+            for (c = 0; c < 32; c++) classbits[c] |= ~cbits[c+cbit_space];
+            classbits[1] |= 0x08;    /* Perl 5.004 onwards omits VT from \s */
+            continue;
+
+#ifdef SUPPORT_UCP
+            case ESC_p:
+            case ESC_P:
+              {
+              BOOL negated;
+              int property = get_ucp(&ptr, &negated, errorptr);
+              if (property < 0) goto FAILED;
+              class_utf8 = TRUE;
+              *class_utf8data++ = ((-c == ESC_p) != negated)?
+                XCL_PROP : XCL_NOTPROP;
+              *class_utf8data++ = property;
+              class_charcount -= 2;   /* Not a < 256 character */
+              }
+            continue;
+#endif
+
+            /* Unrecognized escapes are faulted if PCRE is running in its
+            strict mode. By default, for compatibility with Perl, they are
+            treated as literals. */
+
+            default:
+            if ((options & PCRE_EXTRA) != 0)
+              {
+              *errorptr = ERR7;
+              goto FAILED;
+              }
+            c = *ptr;              /* The final character */
+            class_charcount -= 2;  /* Undo the default count from above */
+            }
+          }
+
+        /* Fall through if we have a single character (c >= 0). This may be
+        > 256 in UTF-8 mode. */
+
+        }   /* End of backslash handling */
+
+      /* A single character may be followed by '-' to form a range. However,
+      Perl does not permit ']' to be the end of the range. A '-' character
+      here is treated as a literal. */
+
+      if (ptr[1] == '-' && ptr[2] != ']')
+        {
+        int d;
+        ptr += 2;
+
+#ifdef SUPPORT_UTF8
+        if (utf8)
+          {                           /* Braces are required because the */
+          GETCHARLEN(d, ptr, ptr);    /* macro generates multiple statements */
+          }
+        else
+#endif
+        d = *ptr;  /* Not UTF-8 mode */
+
+        /* The second part of a range can be a single-character escape, but
+        not any of the other escapes. Perl 5.6 treats a hyphen as a literal
+        in such circumstances. */
+
+        if (d == '\\')
+          {
+          const uschar *oldptr = ptr;
+          d = check_escape(&ptr, errorptr, *brackets, options, TRUE);
+
+          /* \b is backslash; \X is literal X; any other special means the '-'
+          was literal */
+
+          if (d < 0)
+            {
+            if (d == -ESC_b) d = '\b';
+            else if (d == -ESC_X) d = 'X'; else
+              {
+              ptr = oldptr - 2;
+              goto LONE_SINGLE_CHARACTER;  /* A few lines below */
+              }
+            }
+          }
+
+        /* The check that the two values are in the correct order happens in
+        the pre-pass. Optimize one-character ranges */
+
+        if (d == c) goto LONE_SINGLE_CHARACTER;  /* A few lines below */
+
+        /* In UTF-8 mode, if the upper limit is > 255, or > 127 for caseless
+        matching, we have to use an XCLASS with extra data items. Caseless
+        matching for characters > 127 is available only if UCP support is
+        available. */
+
+#ifdef SUPPORT_UTF8
+        if (utf8 && (d > 255 || ((options & PCRE_CASELESS) != 0 && d > 127)))
+          {
+          class_utf8 = TRUE;
+
+          /* With UCP support, we can find the other case equivalents of
+          the relevant characters. There may be several ranges. Optimize how
+          they fit with the basic range. */
+
+#ifdef SUPPORT_UCP
+          if ((options & PCRE_CASELESS) != 0)
+            {
+            int occ, ocd;
+            int cc = c;
+            int origd = d;
+            while (get_othercase_range(&cc, origd, &occ, &ocd))
+              {
+              if (occ >= c && ocd <= d) continue;  /* Skip embedded ranges */
+
+              if (occ < c  && ocd >= c - 1)        /* Extend the basic range */
+                {                                  /* if there is overlap,   */
+                c = occ;                           /* noting that if occ < c */
+                continue;                          /* we can't have ocd > d  */
+                }                                  /* because a subrange is  */
+              if (ocd > d && occ <= d + 1)         /* always shorter than    */
+                {                                  /* the basic range.       */
+                d = ocd;
+                continue;
+                }
+
+              if (occ == ocd)
+                {
+                *class_utf8data++ = XCL_SINGLE;
+                }
+              else
+                {
+                *class_utf8data++ = XCL_RANGE;
+                class_utf8data += ord2utf8(occ, class_utf8data);
+                }
+              class_utf8data += ord2utf8(ocd, class_utf8data);
+              }
+            }
+#endif  /* SUPPORT_UCP */
+
+          /* Now record the original range, possibly modified for UCP caseless
+          overlapping ranges. */
+
+          *class_utf8data++ = XCL_RANGE;
+          class_utf8data += ord2utf8(c, class_utf8data);
+          class_utf8data += ord2utf8(d, class_utf8data);
+
+          /* With UCP support, we are done. Without UCP support, there is no
+          caseless matching for UTF-8 characters > 127; we can use the bit map
+          for the smaller ones. */
+
+#ifdef SUPPORT_UCP
+          continue;    /* With next character in the class */
+#else
+          if ((options & PCRE_CASELESS) == 0 || c > 127) continue;
+
+          /* Adjust upper limit and fall through to set up the map */
+
+          d = 127;
+
+#endif  /* SUPPORT_UCP */
+          }
+#endif  /* SUPPORT_UTF8 */
+
+        /* We use the bit map for all cases when not in UTF-8 mode; else
+        ranges that lie entirely within 0-127 when there is UCP support; else
+        for partial ranges without UCP support. */
+
+        for (; c <= d; c++)
+          {
+          classbits[c/8] |= (1 << (c&7));
+          if ((options & PCRE_CASELESS) != 0)
+            {
+            int uc = cd->fcc[c];           /* flip case */
+            classbits[uc/8] |= (1 << (uc&7));
+            }
+          class_charcount++;                /* in case a one-char range */
+          class_lastchar = c;
+          }
+
+        continue;   /* Go get the next char in the class */
+        }
+
+      /* Handle a lone single character - we can get here for a normal
+      non-escape char, or after \ that introduces a single character or for an
+      apparent range that isn't. */
+
+      LONE_SINGLE_CHARACTER:
+
+      /* Handle a character that cannot go in the bit map */
+
+#ifdef SUPPORT_UTF8
+      if (utf8 && (c > 255 || ((options & PCRE_CASELESS) != 0 && c > 127)))
+        {
+        class_utf8 = TRUE;
+        *class_utf8data++ = XCL_SINGLE;
+        class_utf8data += ord2utf8(c, class_utf8data);
+
+#ifdef SUPPORT_UCP
+        if ((options & PCRE_CASELESS) != 0)
+          {
+          int chartype;
+          int othercase;
+          if (ucp_findchar(c, &chartype, &othercase) >= 0 && othercase > 0)
+            {
+            *class_utf8data++ = XCL_SINGLE;
+            class_utf8data += ord2utf8(othercase, class_utf8data);
+            }
+          }
+#endif  /* SUPPORT_UCP */
+
+        }
+      else
+#endif  /* SUPPORT_UTF8 */
+
+      /* Handle a single-byte character */
+        {
+        classbits[c/8] |= (1 << (c&7));
+        if ((options & PCRE_CASELESS) != 0)
+          {
+          c = cd->fcc[c];   /* flip case */
+          classbits[c/8] |= (1 << (c&7));
+          }
+        class_charcount++;
+        class_lastchar = c;
+        }
+      }
+
+    /* Loop until ']' reached; the check for end of string happens inside the
+    loop. This "while" is the end of the "do" above. */
+
+    while ((c = *(++ptr)) != ']' || inescq);
+
+    /* If class_charcount is 1, we saw precisely one character whose value is
+    less than 256. In non-UTF-8 mode we can always optimize. In UTF-8 mode, we
+    can optimize the negative case only if there were no characters >= 128
+    because OP_NOT and the related opcodes like OP_NOTSTAR operate on
+    single-bytes only. This is an historical hangover. Maybe one day we can
+    tidy these opcodes to handle multi-byte characters.
+
+    The optimization throws away the bit map. We turn the item into a
+    1-character OP_CHAR[NC] if it's positive, or OP_NOT if it's negative. Note
+    that OP_NOT does not support multibyte characters. In the positive case, it
+    can cause firstbyte to be set. Otherwise, there can be no first char if
+    this item is first, whatever repeat count may follow. In the case of
+    reqbyte, save the previous value for reinstating. */
+
+#ifdef SUPPORT_UTF8
+    if (class_charcount == 1 &&
+          (!utf8 ||
+          (!class_utf8 && (!negate_class || class_lastchar < 128))))
+
+#else
+    if (class_charcount == 1)
+#endif
+      {
+      zeroreqbyte = reqbyte;
+
+      /* The OP_NOT opcode works on one-byte characters only. */
+
+      if (negate_class)
+        {
+        if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE;
+        zerofirstbyte = firstbyte;
+        *code++ = OP_NOT;
+        *code++ = class_lastchar;
+        break;
+        }
+
+      /* For a single, positive character, get the value into mcbuffer, and
+      then we can handle this with the normal one-character code. */
+
+#ifdef SUPPORT_UTF8
+      if (utf8 && class_lastchar > 127)
+        mclength = ord2utf8(class_lastchar, mcbuffer);
+      else
+#endif
+        {
+        mcbuffer[0] = class_lastchar;
+        mclength = 1;
+        }
+      goto ONE_CHAR;
+      }       /* End of 1-char optimization */
+
+    /* The general case - not the one-char optimization. If this is the first
+    thing in the branch, there can be no first char setting, whatever the
+    repeat count. Any reqbyte setting must remain unchanged after any kind of
+    repeat. */
+
+    if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE;
+    zerofirstbyte = firstbyte;
+    zeroreqbyte = reqbyte;
+
+    /* If there are characters with values > 255, we have to compile an
+    extended class, with its own opcode. If there are no characters < 256,
+    we can omit the bitmap. */
+
+#ifdef SUPPORT_UTF8
+    if (class_utf8)
+      {
+      *class_utf8data++ = XCL_END;    /* Marks the end of extra data */
+      *code++ = OP_XCLASS;
+      code += LINK_SIZE;
+      *code = negate_class? XCL_NOT : 0;
+
+      /* If the map is required, install it, and move on to the end of
+      the extra data */
+
+      if (class_charcount > 0)
+        {
+        *code++ |= XCL_MAP;
+        memcpy(code, classbits, 32);
+        code = class_utf8data;
+        }
+
+      /* If the map is not required, slide down the extra data. */
+
+      else
+        {
+        int len = class_utf8data - (code + 33);
+        memmove(code + 1, code + 33, len);
+        code += len + 1;
+        }
+
+      /* Now fill in the complete length of the item */
+
+      PUT(previous, 1, code - previous);
+      break;   /* End of class handling */
+      }
+#endif
+
+    /* If there are no characters > 255, negate the 32-byte map if necessary,
+    and copy it into the code vector. If this is the first thing in the branch,
+    there can be no first char setting, whatever the repeat count. Any reqbyte
+    setting must remain unchanged after any kind of repeat. */
+
+    if (negate_class)
+      {
+      *code++ = OP_NCLASS;
+      for (c = 0; c < 32; c++) code[c] = ~classbits[c];
+      }
+    else
+      {
+      *code++ = OP_CLASS;
+      memcpy(code, classbits, 32);
+      }
+    code += 32;
+    break;
+
+    /* Various kinds of repeat; '{' is not necessarily a quantifier, but this
+    has been tested above. */
+
+    case '{':
+    if (!is_quantifier) goto NORMAL_CHAR;
+    ptr = read_repeat_counts(ptr+1, &repeat_min, &repeat_max, errorptr);
+    if (*errorptr != NULL) goto FAILED;
+    goto REPEAT;
+
+    case '*':
+    repeat_min = 0;
+    repeat_max = -1;
+    goto REPEAT;
+
+    case '+':
+    repeat_min = 1;
+    repeat_max = -1;
+    goto REPEAT;
+
+    case '?':
+    repeat_min = 0;
+    repeat_max = 1;
+
+    REPEAT:
+    if (previous == NULL)
+      {
+      *errorptr = ERR9;
+      goto FAILED;
+      }
+
+    if (repeat_min == 0)
+      {
+      firstbyte = zerofirstbyte;    /* Adjust for zero repeat */
+      reqbyte = zeroreqbyte;        /* Ditto */
+      }
+
+    /* Remember whether this is a variable length repeat */
+
+    reqvary = (repeat_min == repeat_max)? 0 : REQ_VARY;
+
+    op_type = 0;                    /* Default single-char op codes */
+    possessive_quantifier = FALSE;  /* Default not possessive quantifier */
+
+    /* Save start of previous item, in case we have to move it up to make space
+    for an inserted OP_ONCE for the additional '+' extension. */
+
+    tempcode = previous;
+
+    /* If the next character is '+', we have a possessive quantifier. This
+    implies greediness, whatever the setting of the PCRE_UNGREEDY option.
+    If the next character is '?' this is a minimizing repeat, by default,
+    but if PCRE_UNGREEDY is set, it works the other way round. We change the
+    repeat type to the non-default. */
+
+    if (ptr[1] == '+')
+      {
+      repeat_type = 0;                  /* Force greedy */
+      possessive_quantifier = TRUE;
+      ptr++;
+      }
+    else if (ptr[1] == '?')
+      {
+      repeat_type = greedy_non_default;
+      ptr++;
+      }
+    else repeat_type = greedy_default;
+
+    /* If previous was a recursion, we need to wrap it inside brackets so that
+    it can be replicated if necessary. */
+
+    if (*previous == OP_RECURSE)
+      {
+      memmove(previous + 1 + LINK_SIZE, previous, 1 + LINK_SIZE);
+      code += 1 + LINK_SIZE;
+      *previous = OP_BRA;
+      PUT(previous, 1, code - previous);
+      *code = OP_KET;
+      PUT(code, 1, code - previous);
+      code += 1 + LINK_SIZE;
+      }
+
+    /* If previous was a character match, abolish the item and generate a
+    repeat item instead. If a char item has a minumum of more than one, ensure
+    that it is set in reqbyte - it might not be if a sequence such as x{3} is
+    the first thing in a branch because the x will have gone into firstbyte
+    instead.  */
+
+    if (*previous == OP_CHAR || *previous == OP_CHARNC)
+      {
+      /* Deal with UTF-8 characters that take up more than one byte. It's
+      easier to write this out separately than try to macrify it. Use c to
+      hold the length of the character in bytes, plus 0x80 to flag that it's a
+      length rather than a small character. */
+
+#ifdef SUPPORT_UTF8
+      if (utf8 && (code[-1] & 0x80) != 0)
+        {
+        uschar *lastchar = code - 1;
+        while((*lastchar & 0xc0) == 0x80) lastchar--;
+        c = code - lastchar;            /* Length of UTF-8 character */
+        memcpy(utf8_char, lastchar, c); /* Save the char */
+        c |= 0x80;                      /* Flag c as a length */
+        }
+      else
+#endif
+
+      /* Handle the case of a single byte - either with no UTF8 support, or
+      with UTF-8 disabled, or for a UTF-8 character < 128. */
+
+        {
+        c = code[-1];
+        if (repeat_min > 1) reqbyte = c | req_caseopt | cd->req_varyopt;
+        }
+
+      goto OUTPUT_SINGLE_REPEAT;   /* Code shared with single character types */
+      }
+
+    /* If previous was a single negated character ([^a] or similar), we use
+    one of the special opcodes, replacing it. The code is shared with single-
+    character repeats by setting opt_type to add a suitable offset into
+    repeat_type. OP_NOT is currently used only for single-byte chars. */
+
+    else if (*previous == OP_NOT)
+      {
+      op_type = OP_NOTSTAR - OP_STAR;  /* Use "not" opcodes */
+      c = previous[1];
+      goto OUTPUT_SINGLE_REPEAT;
+      }
+
+    /* If previous was a character type match (\d or similar), abolish it and
+    create a suitable repeat item. The code is shared with single-character
+    repeats by setting op_type to add a suitable offset into repeat_type. Note
+    the the Unicode property types will be present only when SUPPORT_UCP is
+    defined, but we don't wrap the little bits of code here because it just
+    makes it horribly messy. */
+
+    else if (*previous < OP_EODN)
+      {
+      uschar *oldcode;
+      int prop_type;
+      op_type = OP_TYPESTAR - OP_STAR;  /* Use type opcodes */
+      c = *previous;
+
+      OUTPUT_SINGLE_REPEAT:
+      prop_type = (*previous == OP_PROP || *previous == OP_NOTPROP)?
+        previous[1] : -1;
+
+      oldcode = code;
+      code = previous;                  /* Usually overwrite previous item */
+
+      /* If the maximum is zero then the minimum must also be zero; Perl allows
+      this case, so we do too - by simply omitting the item altogether. */
+
+      if (repeat_max == 0) goto END_REPEAT;
+
+      /* All real repeats make it impossible to handle partial matching (maybe
+      one day we will be able to remove this restriction). */
+
+      if (repeat_max != 1) cd->nopartial = TRUE;
+
+      /* Combine the op_type with the repeat_type */
+
+      repeat_type += op_type;
+
+      /* A minimum of zero is handled either as the special case * or ?, or as
+      an UPTO, with the maximum given. */
+
+      if (repeat_min == 0)
+        {
+        if (repeat_max == -1) *code++ = OP_STAR + repeat_type;
+          else if (repeat_max == 1) *code++ = OP_QUERY + repeat_type;
+        else
+          {
+          *code++ = OP_UPTO + repeat_type;
+          PUT2INC(code, 0, repeat_max);
+          }
+        }
+
+      /* A repeat minimum of 1 is optimized into some special cases. If the
+      maximum is unlimited, we use OP_PLUS. Otherwise, the original item it
+      left in place and, if the maximum is greater than 1, we use OP_UPTO with
+      one less than the maximum. */
+
+      else if (repeat_min == 1)
+        {
+        if (repeat_max == -1)
+          *code++ = OP_PLUS + repeat_type;
+        else
+          {
+          code = oldcode;                 /* leave previous item in place */
+          if (repeat_max == 1) goto END_REPEAT;
+          *code++ = OP_UPTO + repeat_type;
+          PUT2INC(code, 0, repeat_max - 1);
+          }
+        }
+
+      /* The case {n,n} is just an EXACT, while the general case {n,m} is
+      handled as an EXACT followed by an UPTO. */
+
+      else
+        {
+        *code++ = OP_EXACT + op_type;  /* NB EXACT doesn't have repeat_type */
+        PUT2INC(code, 0, repeat_min);
+
+        /* If the maximum is unlimited, insert an OP_STAR. Before doing so,
+        we have to insert the character for the previous code. For a repeated
+        Unicode property match, there is an extra byte that defines the
+        required property. In UTF-8 mode, long characters have their length in
+        c, with the 0x80 bit as a flag. */
+
+        if (repeat_max < 0)
+          {
+#ifdef SUPPORT_UTF8
+          if (utf8 && c >= 128)
+            {
+            memcpy(code, utf8_char, c & 7);
+            code += c & 7;
+            }
+          else
+#endif
+            {
+            *code++ = c;
+            if (prop_type >= 0) *code++ = prop_type;
+            }
+          *code++ = OP_STAR + repeat_type;
+          }
+
+        /* Else insert an UPTO if the max is greater than the min, again
+        preceded by the character, for the previously inserted code. */
+
+        else if (repeat_max != repeat_min)
+          {
+#ifdef SUPPORT_UTF8
+          if (utf8 && c >= 128)
+            {
+            memcpy(code, utf8_char, c & 7);
+            code += c & 7;
+            }
+          else
+#endif
+          *code++ = c;
+          if (prop_type >= 0) *code++ = prop_type;
+          repeat_max -= repeat_min;
+          *code++ = OP_UPTO + repeat_type;
+          PUT2INC(code, 0, repeat_max);
+          }
+        }
+
+      /* The character or character type itself comes last in all cases. */
+
+#ifdef SUPPORT_UTF8
+      if (utf8 && c >= 128)
+        {
+        memcpy(code, utf8_char, c & 7);
+        code += c & 7;
+        }
+      else
+#endif
+      *code++ = c;
+
+      /* For a repeated Unicode property match, there is an extra byte that
+      defines the required property. */
+
+#ifdef SUPPORT_UCP
+      if (prop_type >= 0) *code++ = prop_type;
+#endif
+      }
+
+    /* If previous was a character class or a back reference, we put the repeat
+    stuff after it, but just skip the item if the repeat was {0,0}. */
+
+    else if (*previous == OP_CLASS ||
+             *previous == OP_NCLASS ||
+#ifdef SUPPORT_UTF8
+             *previous == OP_XCLASS ||
+#endif
+             *previous == OP_REF)
+      {
+      if (repeat_max == 0)
+        {
+        code = previous;
+        goto END_REPEAT;
+        }
+
+      /* All real repeats make it impossible to handle partial matching (maybe
+      one day we will be able to remove this restriction). */
+
+      if (repeat_max != 1) cd->nopartial = TRUE;
+
+      if (repeat_min == 0 && repeat_max == -1)
+        *code++ = OP_CRSTAR + repeat_type;
+      else if (repeat_min == 1 && repeat_max == -1)
+        *code++ = OP_CRPLUS + repeat_type;
+      else if (repeat_min == 0 && repeat_max == 1)
+        *code++ = OP_CRQUERY + repeat_type;
+      else
+        {
+        *code++ = OP_CRRANGE + repeat_type;
+        PUT2INC(code, 0, repeat_min);
+        if (repeat_max == -1) repeat_max = 0;  /* 2-byte encoding for max */
+        PUT2INC(code, 0, repeat_max);
+        }
+      }
+
+    /* If previous was a bracket group, we may have to replicate it in certain
+    cases. */
+
+    else if (*previous >= OP_BRA || *previous == OP_ONCE ||
+             *previous == OP_COND)
+      {
+      register int i;
+      int ketoffset = 0;
+      int len = code - previous;
+      uschar *bralink = NULL;
+
+      /* If the maximum repeat count is unlimited, find the end of the bracket
+      by scanning through from the start, and compute the offset back to it
+      from the current code pointer. There may be an OP_OPT setting following
+      the final KET, so we can't find the end just by going back from the code
+      pointer. */
+
+      if (repeat_max == -1)
+        {
+        register uschar *ket = previous;
+        do ket += GET(ket, 1); while (*ket != OP_KET);
+        ketoffset = code - ket;
+        }
+
+      /* The case of a zero minimum is special because of the need to stick
+      OP_BRAZERO in front of it, and because the group appears once in the
+      data, whereas in other cases it appears the minimum number of times. For
+      this reason, it is simplest to treat this case separately, as otherwise
+      the code gets far too messy. There are several special subcases when the
+      minimum is zero. */
+
+      if (repeat_min == 0)
+        {
+        /* If the maximum is also zero, we just omit the group from the output
+        altogether. */
+
+        if (repeat_max == 0)
+          {
+          code = previous;
+          goto END_REPEAT;
+          }
+
+        /* If the maximum is 1 or unlimited, we just have to stick in the
+        BRAZERO and do no more at this point. However, we do need to adjust
+        any OP_RECURSE calls inside the group that refer to the group itself or
+        any internal group, because the offset is from the start of the whole
+        regex. Temporarily terminate the pattern while doing this. */
+
+        if (repeat_max <= 1)
+          {
+          *code = OP_END;
+          adjust_recurse(previous, 1, utf8, cd);
+          memmove(previous+1, previous, len);
+          code++;
+          *previous++ = OP_BRAZERO + repeat_type;
+          }
+
+        /* If the maximum is greater than 1 and limited, we have to replicate
+        in a nested fashion, sticking OP_BRAZERO before each set of brackets.
+        The first one has to be handled carefully because it's the original
+        copy, which has to be moved up. The remainder can be handled by code
+        that is common with the non-zero minimum case below. We have to
+        adjust the value or repeat_max, since one less copy is required. Once
+        again, we may have to adjust any OP_RECURSE calls inside the group. */
+
+        else
+          {
+          int offset;
+          *code = OP_END;
+          adjust_recurse(previous, 2 + LINK_SIZE, utf8, cd);
+          memmove(previous + 2 + LINK_SIZE, previous, len);
+          code += 2 + LINK_SIZE;
+          *previous++ = OP_BRAZERO + repeat_type;
+          *previous++ = OP_BRA;
+
+          /* We chain together the bracket offset fields that have to be
+          filled in later when the ends of the brackets are reached. */
+
+          offset = (bralink == NULL)? 0 : previous - bralink;
+          bralink = previous;
+          PUTINC(previous, 0, offset);
+          }
+
+        repeat_max--;
+        }
+
+      /* If the minimum is greater than zero, replicate the group as many
+      times as necessary, and adjust the maximum to the number of subsequent
+      copies that we need. If we set a first char from the group, and didn't
+      set a required char, copy the latter from the former. */
+
+      else
+        {
+        if (repeat_min > 1)
+          {
+          if (groupsetfirstbyte && reqbyte < 0) reqbyte = firstbyte;
+          for (i = 1; i < repeat_min; i++)
+            {
+            memcpy(code, previous, len);
+            code += len;
+            }
+          }
+        if (repeat_max > 0) repeat_max -= repeat_min;
+        }
+
+      /* This code is common to both the zero and non-zero minimum cases. If
+      the maximum is limited, it replicates the group in a nested fashion,
+      remembering the bracket starts on a stack. In the case of a zero minimum,
+      the first one was set up above. In all cases the repeat_max now specifies
+      the number of additional copies needed. */
+
+      if (repeat_max >= 0)
+        {
+        for (i = repeat_max - 1; i >= 0; i--)
+          {
+          *code++ = OP_BRAZERO + repeat_type;
+
+          /* All but the final copy start a new nesting, maintaining the
+          chain of brackets outstanding. */
+
+          if (i != 0)
+            {
+            int offset;
+            *code++ = OP_BRA;
+            offset = (bralink == NULL)? 0 : code - bralink;
+            bralink = code;
+            PUTINC(code, 0, offset);
+            }
+
+          memcpy(code, previous, len);
+          code += len;
+          }
+
+        /* Now chain through the pending brackets, and fill in their length
+        fields (which are holding the chain links pro tem). */
+
+        while (bralink != NULL)
+          {
+          int oldlinkoffset;
+          int offset = code - bralink + 1;
+          uschar *bra = code - offset;
+          oldlinkoffset = GET(bra, 1);
+          bralink = (oldlinkoffset == 0)? NULL : bralink - oldlinkoffset;
+          *code++ = OP_KET;
+          PUTINC(code, 0, offset);
+          PUT(bra, 1, offset);
+          }
+        }
+
+      /* If the maximum is unlimited, set a repeater in the final copy. We
+      can't just offset backwards from the current code point, because we
+      don't know if there's been an options resetting after the ket. The
+      correct offset was computed above. */
+
+      else code[-ketoffset] = OP_KETRMAX + repeat_type;
+      }
+
+    /* Else there's some kind of shambles */
+
+    else
+      {
+      *errorptr = ERR11;
+      goto FAILED;
+      }
+
+    /* If the character following a repeat is '+', we wrap the entire repeated
+    item inside OP_ONCE brackets. This is just syntactic sugar, taken from
+    Sun's Java package. The repeated item starts at tempcode, not at previous,
+    which might be the first part of a string whose (former) last char we
+    repeated. However, we don't support '+' after a greediness '?'. */
+
+    if (possessive_quantifier)
+      {
+      int len = code - tempcode;
+      memmove(tempcode + 1+LINK_SIZE, tempcode, len);
+      code += 1 + LINK_SIZE;
+      len += 1 + LINK_SIZE;
+      tempcode[0] = OP_ONCE;
+      *code++ = OP_KET;
+      PUTINC(code, 0, len);
+      PUT(tempcode, 1, len);
+      }
+
+    /* In all case we no longer have a previous item. We also set the
+    "follows varying string" flag for subsequently encountered reqbytes if
+    it isn't already set and we have just passed a varying length item. */
+
+    END_REPEAT:
+    previous = NULL;
+    cd->req_varyopt |= reqvary;
+    break;
+
+
+    /* Start of nested bracket sub-expression, or comment or lookahead or
+    lookbehind or option setting or condition. First deal with special things
+    that can come after a bracket; all are introduced by ?, and the appearance
+    of any of them means that this is not a referencing group. They were
+    checked for validity in the first pass over the string, so we don't have to
+    check for syntax errors here.  */
+
+    case '(':
+    newoptions = options;
+    skipbytes = 0;
+
+    if (*(++ptr) == '?')
+      {
+      int set, unset;
+      int *optset;
+
+      switch (*(++ptr))
+        {
+        case '#':                 /* Comment; skip to ket */
+        ptr++;
+        while (*ptr != ')') ptr++;
+        continue;
+
+        case ':':                 /* Non-extracting bracket */
+        bravalue = OP_BRA;
+        ptr++;
+        break;
+
+        case '(':
+        bravalue = OP_COND;       /* Conditional group */
+
+        /* Condition to test for recursion */
+
+        if (ptr[1] == 'R')
+          {
+          code[1+LINK_SIZE] = OP_CREF;
+          PUT2(code, 2+LINK_SIZE, CREF_RECURSE);
+          skipbytes = 3;
+          ptr += 3;
+          }
+
+        /* Condition to test for a numbered subpattern match. We know that
+        if a digit follows ( then there will just be digits until ) because
+        the syntax was checked in the first pass. */
+
+        else if ((digitab[ptr[1]] && ctype_digit) != 0)
+          {
+          int condref;                 /* Don't amalgamate; some compilers */
+          condref = *(++ptr) - '0';    /* grumble at autoincrement in declaration */
+          while (*(++ptr) != ')') condref = condref*10 + *ptr - '0';
+          if (condref == 0)
+            {
+            *errorptr = ERR35;
+            goto FAILED;
+            }
+          ptr++;
+          code[1+LINK_SIZE] = OP_CREF;
+          PUT2(code, 2+LINK_SIZE, condref);
+          skipbytes = 3;
+          }
+        /* For conditions that are assertions, we just fall through, having
+        set bravalue above. */
+        break;
+
+        case '=':                 /* Positive lookahead */
+        bravalue = OP_ASSERT;
+        ptr++;
+        break;
+
+        case '!':                 /* Negative lookahead */
+        bravalue = OP_ASSERT_NOT;
+        ptr++;
+        break;
+
+        case '<':                 /* Lookbehinds */
+        switch (*(++ptr))
+          {
+          case '=':               /* Positive lookbehind */
+          bravalue = OP_ASSERTBACK;
+          ptr++;
+          break;
+
+          case '!':               /* Negative lookbehind */
+          bravalue = OP_ASSERTBACK_NOT;
+          ptr++;
+          break;
+          }
+        break;
+
+        case '>':                 /* One-time brackets */
+        bravalue = OP_ONCE;
+        ptr++;
+        break;
+
+        case 'C':                 /* Callout - may be followed by digits; */
+        previous_callout = code;  /* Save for later completion */
+        after_manual_callout = 1; /* Skip one item before completing */
+        *code++ = OP_CALLOUT;     /* Already checked that the terminating */
+          {                       /* closing parenthesis is present. */
+          int n = 0;
+          while ((digitab[*(++ptr)] & ctype_digit) != 0)
+            n = n * 10 + *ptr - '0';
+          if (n > 255)
+            {
+            *errorptr = ERR38;
+            goto FAILED;
+            }
+          *code++ = n;
+          PUT(code, 0, ptr - cd->start_pattern + 1);  /* Pattern offset */
+          PUT(code, LINK_SIZE, 0);                    /* Default length */
+          code += 2 * LINK_SIZE;
+          }
+        previous = NULL;
+        continue;
+
+        case 'P':                 /* Named subpattern handling */
+        if (*(++ptr) == '<')      /* Definition */
+          {
+          int i, namelen;
+          uschar *slot = cd->name_table;
+          const uschar *name;     /* Don't amalgamate; some compilers */
+          name = ++ptr;           /* grumble at autoincrement in declaration */
+
+          while (*ptr++ != '>');
+          namelen = ptr - name - 1;
+
+          for (i = 0; i < cd->names_found; i++)
+            {
+            int crc = memcmp(name, slot+2, namelen);
+            if (crc == 0)
+              {
+              if (slot[2+namelen] == 0)
+                {
+                *errorptr = ERR43;
+                goto FAILED;
+                }
+              crc = -1;             /* Current name is substring */
+              }
+            if (crc < 0)
+              {
+              memmove(slot + cd->name_entry_size, slot,
+                (cd->names_found - i) * cd->name_entry_size);
+              break;
+              }
+            slot += cd->name_entry_size;
+            }
+
+          PUT2(slot, 0, *brackets + 1);
+          memcpy(slot + 2, name, namelen);
+          slot[2+namelen] = 0;
+          cd->names_found++;
+          goto NUMBERED_GROUP;
+          }
+
+        if (*ptr == '=' || *ptr == '>')  /* Reference or recursion */
+          {
+          int i, namelen;
+          int type = *ptr++;
+          const uschar *name = ptr;
+          uschar *slot = cd->name_table;
+
+          while (*ptr != ')') ptr++;
+          namelen = ptr - name;
+
+          for (i = 0; i < cd->names_found; i++)
+            {
+            if (strncmp((char *)name, (char *)slot+2, namelen) == 0) break;
+            slot += cd->name_entry_size;
+            }
+          if (i >= cd->names_found)
+            {
+            *errorptr = ERR15;
+            goto FAILED;
+            }
+
+          recno = GET2(slot, 0);
+
+          if (type == '>') goto HANDLE_RECURSION;  /* A few lines below */
+
+          /* Back reference */
+
+          previous = code;
+          *code++ = OP_REF;
+          PUT2INC(code, 0, recno);
+          cd->backref_map |= (recno < 32)? (1 << recno) : 1;
+          if (recno > cd->top_backref) cd->top_backref = recno;
+          continue;
+          }
+
+        /* Should never happen */
+        break;
+
+        case 'R':                 /* Pattern recursion */
+        ptr++;                    /* Same as (?0)      */
+        /* Fall through */
+
+        /* Recursion or "subroutine" call */
+
+        case '0': case '1': case '2': case '3': case '4':
+        case '5': case '6': case '7': case '8': case '9':
+          {
+          const uschar *called;
+          recno = 0;
+          while((digitab[*ptr] & ctype_digit) != 0)
+            recno = recno * 10 + *ptr++ - '0';
+
+          /* Come here from code above that handles a named recursion */
+
+          HANDLE_RECURSION:
+
+          previous = code;
+
+          /* Find the bracket that is being referenced. Temporarily end the
+          regex in case it doesn't exist. */
+
+          *code = OP_END;
+          called = (recno == 0)?
+            cd->start_code : find_bracket(cd->start_code, utf8, recno);
+
+          if (called == NULL)
+            {
+            *errorptr = ERR15;
+            goto FAILED;
+            }
+
+          /* If the subpattern is still open, this is a recursive call. We
+          check to see if this is a left recursion that could loop for ever,
+          and diagnose that case. */
+
+          if (GET(called, 1) == 0 && could_be_empty(called, code, bcptr, utf8))
+            {
+            *errorptr = ERR40;
+            goto FAILED;
+            }
+
+          /* Insert the recursion/subroutine item */
+
+          *code = OP_RECURSE;
+          PUT(code, 1, called - cd->start_code);
+          code += 1 + LINK_SIZE;
+          }
+        continue;
+
+        /* Character after (? not specially recognized */
+
+        default:                  /* Option setting */
+        set = unset = 0;
+        optset = &set;
+
+        while (*ptr != ')' && *ptr != ':')
+          {
+          switch (*ptr++)
+            {
+            case '-': optset = &unset; break;
+
+            case 'i': *optset |= PCRE_CASELESS; break;
+            case 'm': *optset |= PCRE_MULTILINE; break;
+            case 's': *optset |= PCRE_DOTALL; break;
+            case 'x': *optset |= PCRE_EXTENDED; break;
+            case 'U': *optset |= PCRE_UNGREEDY; break;
+            case 'X': *optset |= PCRE_EXTRA; break;
+            }
+          }
+
+        /* Set up the changed option bits, but don't change anything yet. */
+
+        newoptions = (options | set) & (~unset);
+
+        /* If the options ended with ')' this is not the start of a nested
+        group with option changes, so the options change at this level. Compile
+        code to change the ims options if this setting actually changes any of
+        them. We also pass the new setting back so that it can be put at the
+        start of any following branches, and when this group ends (if we are in
+        a group), a resetting item can be compiled.
+
+        Note that if this item is right at the start of the pattern, the
+        options will have been abstracted and made global, so there will be no
+        change to compile. */
+
+        if (*ptr == ')')
+          {
+          if ((options & PCRE_IMS) != (newoptions & PCRE_IMS))
+            {
+            *code++ = OP_OPT;
+            *code++ = newoptions & PCRE_IMS;
+            }
+
+          /* Change options at this level, and pass them back for use
+          in subsequent branches. Reset the greedy defaults and the case
+          value for firstbyte and reqbyte. */
+
+          *optionsptr = options = newoptions;
+          greedy_default = ((newoptions & PCRE_UNGREEDY) != 0);
+          greedy_non_default = greedy_default ^ 1;
+          req_caseopt = ((options & PCRE_CASELESS) != 0)? REQ_CASELESS : 0;
+
+          previous = NULL;       /* This item can't be repeated */
+          continue;              /* It is complete */
+          }
+
+        /* If the options ended with ':' we are heading into a nested group
+        with possible change of options. Such groups are non-capturing and are
+        not assertions of any kind. All we need to do is skip over the ':';
+        the newoptions value is handled below. */
+
+        bravalue = OP_BRA;
+        ptr++;
+        }
+      }
+
+    /* If PCRE_NO_AUTO_CAPTURE is set, all unadorned brackets become
+    non-capturing and behave like (?:...) brackets */
+
+    else if ((options & PCRE_NO_AUTO_CAPTURE) != 0)
+      {
+      bravalue = OP_BRA;
+      }
+
+    /* Else we have a referencing group; adjust the opcode. If the bracket
+    number is greater than EXTRACT_BASIC_MAX, we set the opcode one higher, and
+    arrange for the true number to follow later, in an OP_BRANUMBER item. */
+
+    else
+      {
+      NUMBERED_GROUP:
+      if (++(*brackets) > EXTRACT_BASIC_MAX)
+        {
+        bravalue = OP_BRA + EXTRACT_BASIC_MAX + 1;
+        code[1+LINK_SIZE] = OP_BRANUMBER;
+        PUT2(code, 2+LINK_SIZE, *brackets);
+        skipbytes = 3;
+        }
+      else bravalue = OP_BRA + *brackets;
+      }
+
+    /* Process nested bracketed re. Assertions may not be repeated, but other
+    kinds can be. We copy code into a non-register variable in order to be able
+    to pass its address because some compilers complain otherwise. Pass in a
+    new setting for the ims options if they have changed. */
+
+    previous = (bravalue >= OP_ONCE)? code : NULL;
+    *code = bravalue;
+    tempcode = code;
+    tempreqvary = cd->req_varyopt;     /* Save value before bracket */
+
+    if (!compile_regex(
+         newoptions,                   /* The complete new option state */
+         options & PCRE_IMS,           /* The previous ims option state */
+         brackets,                     /* Extracting bracket count */
+         &tempcode,                    /* Where to put code (updated) */
+         &ptr,                         /* Input pointer (updated) */
+         errorptr,                     /* Where to put an error message */
+         (bravalue == OP_ASSERTBACK ||
+          bravalue == OP_ASSERTBACK_NOT), /* TRUE if back assert */
+         skipbytes,                    /* Skip over OP_COND/OP_BRANUMBER */
+         &subfirstbyte,                /* For possible first char */
+         &subreqbyte,                  /* For possible last char */
+         bcptr,                        /* Current branch chain */
+         cd))                          /* Tables block */
+      goto FAILED;
+
+    /* At the end of compiling, code is still pointing to the start of the
+    group, while tempcode has been updated to point past the end of the group
+    and any option resetting that may follow it. The pattern pointer (ptr)
+    is on the bracket. */
+
+    /* If this is a conditional bracket, check that there are no more than
+    two branches in the group. */
+
+    else if (bravalue == OP_COND)
+      {
+      uschar *tc = code;
+      condcount = 0;
+
+      do {
+         condcount++;
+         tc += GET(tc,1);
+         }
+      while (*tc != OP_KET);
+
+      if (condcount > 2)
+        {
+        *errorptr = ERR27;
+        goto FAILED;
+        }
+
+      /* If there is just one branch, we must not make use of its firstbyte or
+      reqbyte, because this is equivalent to an empty second branch. */
+
+      if (condcount == 1) subfirstbyte = subreqbyte = REQ_NONE;
+      }
+
+    /* Handle updating of the required and first characters. Update for normal
+    brackets of all kinds, and conditions with two branches (see code above).
+    If the bracket is followed by a quantifier with zero repeat, we have to
+    back off. Hence the definition of zeroreqbyte and zerofirstbyte outside the
+    main loop so that they can be accessed for the back off. */
+
+    zeroreqbyte = reqbyte;
+    zerofirstbyte = firstbyte;
+    groupsetfirstbyte = FALSE;
+
+    if (bravalue >= OP_BRA || bravalue == OP_ONCE || bravalue == OP_COND)
+      {
+      /* If we have not yet set a firstbyte in this branch, take it from the
+      subpattern, remembering that it was set here so that a repeat of more
+      than one can replicate it as reqbyte if necessary. If the subpattern has
+      no firstbyte, set "none" for the whole branch. In both cases, a zero
+      repeat forces firstbyte to "none". */
+
+      if (firstbyte == REQ_UNSET)
+        {
+        if (subfirstbyte >= 0)
+          {
+          firstbyte = subfirstbyte;
+          groupsetfirstbyte = TRUE;
+          }
+        else firstbyte = REQ_NONE;
+        zerofirstbyte = REQ_NONE;
+        }
+
+      /* If firstbyte was previously set, convert the subpattern's firstbyte
+      into reqbyte if there wasn't one, using the vary flag that was in
+      existence beforehand. */
+
+      else if (subfirstbyte >= 0 && subreqbyte < 0)
+        subreqbyte = subfirstbyte | tempreqvary;
+
+      /* If the subpattern set a required byte (or set a first byte that isn't
+      really the first byte - see above), set it. */
+
+      if (subreqbyte >= 0) reqbyte = subreqbyte;
+      }
+
+    /* For a forward assertion, we take the reqbyte, if set. This can be
+    helpful if the pattern that follows the assertion doesn't set a different
+    char. For example, it's useful for /(?=abcde).+/. We can't set firstbyte
+    for an assertion, however because it leads to incorrect effect for patterns
+    such as /(?=a)a.+/ when the "real" "a" would then become a reqbyte instead
+    of a firstbyte. This is overcome by a scan at the end if there's no
+    firstbyte, looking for an asserted first char. */
+
+    else if (bravalue == OP_ASSERT && subreqbyte >= 0) reqbyte = subreqbyte;
+
+    /* Now update the main code pointer to the end of the group. */
+
+    code = tempcode;
+
+    /* Error if hit end of pattern */
+
+    if (*ptr != ')')
+      {
+      *errorptr = ERR14;
+      goto FAILED;
+      }
+    break;
+
+    /* Check \ for being a real metacharacter; if not, fall through and handle
+    it as a data character at the start of a string. Escape items are checked
+    for validity in the pre-compiling pass. */
+
+    case '\\':
+    tempptr = ptr;
+    c = check_escape(&ptr, errorptr, *brackets, options, FALSE);
+
+    /* Handle metacharacters introduced by \. For ones like \d, the ESC_ values
+    are arranged to be the negation of the corresponding OP_values. For the
+    back references, the values are ESC_REF plus the reference number. Only
+    back references and those types that consume a character may be repeated.
+    We can test for values between ESC_b and ESC_Z for the latter; this may
+    have to change if any new ones are ever created. */
+
+    if (c < 0)
+      {
+      if (-c == ESC_Q)            /* Handle start of quoted string */
+        {
+        if (ptr[1] == '\\' && ptr[2] == 'E') ptr += 2; /* avoid empty string */
+          else inescq = TRUE;
+        continue;
+        }
+
+      /* For metasequences that actually match a character, we disable the
+      setting of a first character if it hasn't already been set. */
+
+      if (firstbyte == REQ_UNSET && -c > ESC_b && -c < ESC_Z)
+        firstbyte = REQ_NONE;
+
+      /* Set values to reset to if this is followed by a zero repeat. */
+
+      zerofirstbyte = firstbyte;
+      zeroreqbyte = reqbyte;
+
+      /* Back references are handled specially */
+
+      if (-c >= ESC_REF)
+        {
+        int number = -c - ESC_REF;
+        previous = code;
+        *code++ = OP_REF;
+        PUT2INC(code, 0, number);
+        }
+
+      /* So are Unicode property matches, if supported. We know that get_ucp
+      won't fail because it was tested in the pre-pass. */
+
+#ifdef SUPPORT_UCP
+      else if (-c == ESC_P || -c == ESC_p)
+        {
+        BOOL negated;
+        int value = get_ucp(&ptr, &negated, errorptr);
+        previous = code;
+        *code++ = ((-c == ESC_p) != negated)? OP_PROP : OP_NOTPROP;
+        *code++ = value;
+        }
+#endif
+
+      /* For the rest, we can obtain the OP value by negating the escape
+      value */
+
+      else
+        {
+        previous = (-c > ESC_b && -c < ESC_Z)? code : NULL;
+        *code++ = -c;
+        }
+      continue;
+      }
+
+    /* We have a data character whose value is in c. In UTF-8 mode it may have
+    a value > 127. We set its representation in the length/buffer, and then
+    handle it as a data character. */
+
+#ifdef SUPPORT_UTF8
+    if (utf8 && c > 127)
+      mclength = ord2utf8(c, mcbuffer);
+    else
+#endif
+
+     {
+     mcbuffer[0] = c;
+     mclength = 1;
+     }
+
+    goto ONE_CHAR;
+
+    /* Handle a literal character. It is guaranteed not to be whitespace or #
+    when the extended flag is set. If we are in UTF-8 mode, it may be a
+    multi-byte literal character. */
+
+    default:
+    NORMAL_CHAR:
+    mclength = 1;
+    mcbuffer[0] = c;
+
+#ifdef SUPPORT_UTF8
+    if (utf8 && (c & 0xc0) == 0xc0)
+      {
+      while ((ptr[1] & 0xc0) == 0x80)
+        mcbuffer[mclength++] = *(++ptr);
+      }
+#endif
+
+    /* At this point we have the character's bytes in mcbuffer, and the length
+    in mclength. When not in UTF-8 mode, the length is always 1. */
+
+    ONE_CHAR:
+    previous = code;
+    *code++ = ((options & PCRE_CASELESS) != 0)? OP_CHARNC : OP_CHAR;
+    for (c = 0; c < mclength; c++) *code++ = mcbuffer[c];
+
+    /* Set the first and required bytes appropriately. If no previous first
+    byte, set it from this character, but revert to none on a zero repeat.
+    Otherwise, leave the firstbyte value alone, and don't change it on a zero
+    repeat. */
+
+    if (firstbyte == REQ_UNSET)
+      {
+      zerofirstbyte = REQ_NONE;
+      zeroreqbyte = reqbyte;
+
+      /* If the character is more than one byte long, we can set firstbyte
+      only if it is not to be matched caselessly. */
+
+      if (mclength == 1 || req_caseopt == 0)
+        {
+        firstbyte = mcbuffer[0] | req_caseopt;
+        if (mclength != 1) reqbyte = code[-1] | cd->req_varyopt;
+        }
+      else firstbyte = reqbyte = REQ_NONE;
+      }
+
+    /* firstbyte was previously set; we can set reqbyte only the length is
+    1 or the matching is caseful. */
+
+    else
+      {
+      zerofirstbyte = firstbyte;
+      zeroreqbyte = reqbyte;
+      if (mclength == 1 || req_caseopt == 0)
+        reqbyte = code[-1] | req_caseopt | cd->req_varyopt;
+      }
+
+    break;            /* End of literal character handling */
+    }
+  }                   /* end of big loop */
+
+/* Control never reaches here by falling through, only by a goto for all the
+error states. Pass back the position in the pattern so that it can be displayed
+to the user for diagnosing the error. */
+
+FAILED:
+*ptrptr = ptr;
+return FALSE;
+}
+
+
+
+
+/*************************************************
+*     Compile sequence of alternatives           *
+*************************************************/
+
+/* On entry, ptr is pointing past the bracket character, but on return
+it points to the closing bracket, or vertical bar, or end of string.
+The code variable is pointing at the byte into which the BRA operator has been
+stored. If the ims options are changed at the start (for a (?ims: group) or
+during any branch, we need to insert an OP_OPT item at the start of every
+following branch to ensure they get set correctly at run time, and also pass
+the new options into every subsequent branch compile.
+
+Argument:
+  options        option bits, including any changes for this subpattern
+  oldims         previous settings of ims option bits
+  brackets       -> int containing the number of extracting brackets used
+  codeptr        -> the address of the current code pointer
+  ptrptr         -> the address of the current pattern pointer
+  errorptr       -> pointer to error message
+  lookbehind     TRUE if this is a lookbehind assertion
+  skipbytes      skip this many bytes at start (for OP_COND, OP_BRANUMBER)
+  firstbyteptr   place to put the first required character, or a negative number
+  reqbyteptr     place to put the last required character, or a negative number
+  bcptr          pointer to the chain of currently open branches
+  cd             points to the data block with tables pointers etc.
+
+Returns:      TRUE on success
+*/
+
+static BOOL
+compile_regex(int options, int oldims, int *brackets, uschar **codeptr,
+  const uschar **ptrptr, const char **errorptr, BOOL lookbehind, int skipbytes,
+  int *firstbyteptr, int *reqbyteptr, branch_chain *bcptr, compile_data *cd)
+{
+const uschar *ptr = *ptrptr;
+uschar *code = *codeptr;
+uschar *last_branch = code;
+uschar *start_bracket = code;
+uschar *reverse_count = NULL;
+int firstbyte, reqbyte;
+int branchfirstbyte, branchreqbyte;
+branch_chain bc;
+
+bc.outer = bcptr;
+bc.current = code;
+
+firstbyte = reqbyte = REQ_UNSET;
+
+/* Offset is set zero to mark that this bracket is still open */
+
+PUT(code, 1, 0);
+code += 1 + LINK_SIZE + skipbytes;
+
+/* Loop for each alternative branch */
+
+for (;;)
+  {
+  /* Handle a change of ims options at the start of the branch */
+
+  if ((options & PCRE_IMS) != oldims)
+    {
+    *code++ = OP_OPT;
+    *code++ = options & PCRE_IMS;
+    }
+
+  /* Set up dummy OP_REVERSE if lookbehind assertion */
+
+  if (lookbehind)
+    {
+    *code++ = OP_REVERSE;
+    reverse_count = code;
+    PUTINC(code, 0, 0);
+    }
+
+  /* Now compile the branch */
+
+  if (!compile_branch(&options, brackets, &code, &ptr, errorptr,
+        &branchfirstbyte, &branchreqbyte, &bc, cd))
+    {
+    *ptrptr = ptr;
+    return FALSE;
+    }
+
+  /* If this is the first branch, the firstbyte and reqbyte values for the
+  branch become the values for the regex. */
+
+  if (*last_branch != OP_ALT)
+    {
+    firstbyte = branchfirstbyte;
+    reqbyte = branchreqbyte;
+    }
+
+  /* If this is not the first branch, the first char and reqbyte have to
+  match the values from all the previous branches, except that if the previous
+  value for reqbyte didn't have REQ_VARY set, it can still match, and we set
+  REQ_VARY for the regex. */
+
+  else
+    {
+    /* If we previously had a firstbyte, but it doesn't match the new branch,
+    we have to abandon the firstbyte for the regex, but if there was previously
+    no reqbyte, it takes on the value of the old firstbyte. */
+
+    if (firstbyte >= 0 && firstbyte != branchfirstbyte)
+      {
+      if (reqbyte < 0) reqbyte = firstbyte;
+      firstbyte = REQ_NONE;
+      }
+
+    /* If we (now or from before) have no firstbyte, a firstbyte from the
+    branch becomes a reqbyte if there isn't a branch reqbyte. */
+
+    if (firstbyte < 0 && branchfirstbyte >= 0 && branchreqbyte < 0)
+        branchreqbyte = branchfirstbyte;
+
+    /* Now ensure that the reqbytes match */
+
+    if ((reqbyte & ~REQ_VARY) != (branchreqbyte & ~REQ_VARY))
+      reqbyte = REQ_NONE;
+    else reqbyte |= branchreqbyte;   /* To "or" REQ_VARY */
+    }
+
+  /* If lookbehind, check that this branch matches a fixed-length string,
+  and put the length into the OP_REVERSE item. Temporarily mark the end of
+  the branch with OP_END. */
+
+  if (lookbehind)
+    {
+    int length;
+    *code = OP_END;
+    length = find_fixedlength(last_branch, options);
+    DPRINTF(("fixed length = %d\n", length));
+    if (length < 0)
+      {
+      *errorptr = (length == -2)? ERR36 : ERR25;
+      *ptrptr = ptr;
+      return FALSE;
+      }
+    PUT(reverse_count, 0, length);
+    }
+
+  /* Reached end of expression, either ')' or end of pattern. Go back through
+  the alternative branches and reverse the chain of offsets, with the field in
+  the BRA item now becoming an offset to the first alternative. If there are
+  no alternatives, it points to the end of the group. The length in the
+  terminating ket is always the length of the whole bracketed item. If any of
+  the ims options were changed inside the group, compile a resetting op-code
+  following, except at the very end of the pattern. Return leaving the pointer
+  at the terminating char. */
+
+  if (*ptr != '|')
+    {
+    int length = code - last_branch;
+    do
+      {
+      int prev_length = GET(last_branch, 1);
+      PUT(last_branch, 1, length);
+      length = prev_length;
+      last_branch -= length;
+      }
+    while (length > 0);
+
+    /* Fill in the ket */
+
+    *code = OP_KET;
+    PUT(code, 1, code - start_bracket);
+    code += 1 + LINK_SIZE;
+
+    /* Resetting option if needed */
+
+    if ((options & PCRE_IMS) != oldims && *ptr == ')')
+      {
+      *code++ = OP_OPT;
+      *code++ = oldims;
+      }
+
+    /* Set values to pass back */
+
+    *codeptr = code;
+    *ptrptr = ptr;
+    *firstbyteptr = firstbyte;
+    *reqbyteptr = reqbyte;
+    return TRUE;
+    }
+
+  /* Another branch follows; insert an "or" node. Its length field points back
+  to the previous branch while the bracket remains open. At the end the chain
+  is reversed. It's done like this so that the start of the bracket has a
+  zero offset until it is closed, making it possible to detect recursion. */
+
+  *code = OP_ALT;
+  PUT(code, 1, code - last_branch);
+  bc.current = last_branch = code;
+  code += 1 + LINK_SIZE;
+  ptr++;
+  }
+/* Control never reaches here */
+}
+
+
+
+
+/*************************************************
+*          Check for anchored expression         *
+*************************************************/
+
+/* Try to find out if this is an anchored regular expression. Consider each
+alternative branch. If they all start with OP_SOD or OP_CIRC, or with a bracket
+all of whose alternatives start with OP_SOD or OP_CIRC (recurse ad lib), then
+it's anchored. However, if this is a multiline pattern, then only OP_SOD
+counts, since OP_CIRC can match in the middle.
+
+We can also consider a regex to be anchored if OP_SOM starts all its branches.
+This is the code for \G, which means "match at start of match position, taking
+into account the match offset".
+
+A branch is also implicitly anchored if it starts with .* and DOTALL is set,
+because that will try the rest of the pattern at all possible matching points,
+so there is no point trying again.... er ....
+
+.... except when the .* appears inside capturing parentheses, and there is a
+subsequent back reference to those parentheses. We haven't enough information
+to catch that case precisely.
+
+At first, the best we could do was to detect when .* was in capturing brackets
+and the highest back reference was greater than or equal to that level.
+However, by keeping a bitmap of the first 31 back references, we can catch some
+of the more common cases more precisely.
+
+Arguments:
+  code           points to start of expression (the bracket)
+  options        points to the options setting
+  bracket_map    a bitmap of which brackets we are inside while testing; this
+                  handles up to substring 31; after that we just have to take
+                  the less precise approach
+  backref_map    the back reference bitmap
+
+Returns:     TRUE or FALSE
+*/
+
+static BOOL
+is_anchored(register const uschar *code, int *options, unsigned int bracket_map,
+  unsigned int backref_map)
+{
+do {
+   const uschar *scode =
+     first_significant_code(code + 1+LINK_SIZE, options, PCRE_MULTILINE, FALSE);
+   register int op = *scode;
+
+   /* Capturing brackets */
+
+   if (op > OP_BRA)
+     {
+     int new_map;
+     op -= OP_BRA;
+     if (op > EXTRACT_BASIC_MAX) op = GET2(scode, 2+LINK_SIZE);
+     new_map = bracket_map | ((op < 32)? (1 << op) : 1);
+     if (!is_anchored(scode, options, new_map, backref_map)) return FALSE;
+     }
+
+   /* Other brackets */
+
+   else if (op == OP_BRA || op == OP_ASSERT || op == OP_ONCE || op == OP_COND)
+     {
+     if (!is_anchored(scode, options, bracket_map, backref_map)) return FALSE;
+     }
+
+   /* .* is not anchored unless DOTALL is set and it isn't in brackets that
+   are or may be referenced. */
+
+   else if ((op == OP_TYPESTAR || op == OP_TYPEMINSTAR) &&
+            (*options & PCRE_DOTALL) != 0)
+     {
+     if (scode[1] != OP_ANY || (bracket_map & backref_map) != 0) return FALSE;
+     }
+
+   /* Check for explicit anchoring */
+
+   else if (op != OP_SOD && op != OP_SOM &&
+           ((*options & PCRE_MULTILINE) != 0 || op != OP_CIRC))
+     return FALSE;
+   code += GET(code, 1);
+   }
+while (*code == OP_ALT);   /* Loop for each alternative */
+return TRUE;
+}
+
+
+
+/*************************************************
+*         Check for starting with ^ or .*        *
+*************************************************/
+
+/* This is called to find out if every branch starts with ^ or .* so that
+"first char" processing can be done to speed things up in multiline
+matching and for non-DOTALL patterns that start with .* (which must start at
+the beginning or after \n). As in the case of is_anchored() (see above), we
+have to take account of back references to capturing brackets that contain .*
+because in that case we can't make the assumption.
+
+Arguments:
+  code           points to start of expression (the bracket)
+  bracket_map    a bitmap of which brackets we are inside while testing; this
+                  handles up to substring 31; after that we just have to take
+                  the less precise approach
+  backref_map    the back reference bitmap
+
+Returns:         TRUE or FALSE
+*/
+
+static BOOL
+is_startline(const uschar *code, unsigned int bracket_map,
+  unsigned int backref_map)
+{
+do {
+   const uschar *scode = first_significant_code(code + 1+LINK_SIZE, NULL, 0,
+     FALSE);
+   register int op = *scode;
+
+   /* Capturing brackets */
+
+   if (op > OP_BRA)
+     {
+     int new_map;
+     op -= OP_BRA;
+     if (op > EXTRACT_BASIC_MAX) op = GET2(scode, 2+LINK_SIZE);
+     new_map = bracket_map | ((op < 32)? (1 << op) : 1);
+     if (!is_startline(scode, new_map, backref_map)) return FALSE;
+     }
+
+   /* Other brackets */
+
+   else if (op == OP_BRA || op == OP_ASSERT || op == OP_ONCE || op == OP_COND)
+     { if (!is_startline(scode, bracket_map, backref_map)) return FALSE; }
+
+   /* .* means "start at start or after \n" if it isn't in brackets that
+   may be referenced. */
+
+   else if (op == OP_TYPESTAR || op == OP_TYPEMINSTAR)
+     {
+     if (scode[1] != OP_ANY || (bracket_map & backref_map) != 0) return FALSE;
+     }
+
+   /* Check for explicit circumflex */
+
+   else if (op != OP_CIRC) return FALSE;
+
+   /* Move on to the next alternative */
+
+   code += GET(code, 1);
+   }
+while (*code == OP_ALT);  /* Loop for each alternative */
+return TRUE;
+}
+
+
+
+/*************************************************
+*       Check for asserted fixed first char      *
+*************************************************/
+
+/* During compilation, the "first char" settings from forward assertions are
+discarded, because they can cause conflicts with actual literals that follow.
+However, if we end up without a first char setting for an unanchored pattern,
+it is worth scanning the regex to see if there is an initial asserted first
+char. If all branches start with the same asserted char, or with a bracket all
+of whose alternatives start with the same asserted char (recurse ad lib), then
+we return that char, otherwise -1.
+
+Arguments:
+  code       points to start of expression (the bracket)
+  options    pointer to the options (used to check casing changes)
+  inassert   TRUE if in an assertion
+
+Returns:     -1 or the fixed first char
+*/
+
+static int
+find_firstassertedchar(const uschar *code, int *options, BOOL inassert)
+{
+register int c = -1;
+do {
+   int d;
+   const uschar *scode =
+     first_significant_code(code + 1+LINK_SIZE, options, PCRE_CASELESS, TRUE);
+   register int op = *scode;
+
+   if (op >= OP_BRA) op = OP_BRA;
+
+   switch(op)
+     {
+     default:
+     return -1;
+
+     case OP_BRA:
+     case OP_ASSERT:
+     case OP_ONCE:
+     case OP_COND:
+     if ((d = find_firstassertedchar(scode, options, op == OP_ASSERT)) < 0)
+       return -1;
+     if (c < 0) c = d; else if (c != d) return -1;
+     break;
+
+     case OP_EXACT:       /* Fall through */
+     scode += 2;
+
+     case OP_CHAR:
+     case OP_CHARNC:
+     case OP_PLUS:
+     case OP_MINPLUS:
+     if (!inassert) return -1;
+     if (c < 0)
+       {
+       c = scode[1];
+       if ((*options & PCRE_CASELESS) != 0) c |= REQ_CASELESS;
+       }
+     else if (c != scode[1]) return -1;
+     break;
+     }
+
+   code += GET(code, 1);
+   }
+while (*code == OP_ALT);
+return c;
+}
+
+
+
+
+#ifdef SUPPORT_UTF8
+/*************************************************
+*         Validate a UTF-8 string                *
+*************************************************/
+
+/* This function is called (optionally) at the start of compile or match, to
+validate that a supposed UTF-8 string is actually valid. The early check means
+that subsequent code can assume it is dealing with a valid string. The check
+can be turned off for maximum performance, but then consequences of supplying
+an invalid string are then undefined.
+
+Arguments:
+  string       points to the string
+  length       length of string, or -1 if the string is zero-terminated
+
+Returns:       < 0    if the string is a valid UTF-8 string
+               >= 0   otherwise; the value is the offset of the bad byte
+*/
+
+static int
+valid_utf8(const uschar *string, int length)
+{
+register const uschar *p;
+
+if (length < 0)
+  {
+  for (p = string; *p != 0; p++);
+  length = p - string;
+  }
+
+for (p = string; length-- > 0; p++)
+  {
+  register int ab;
+  register int c = *p;
+  if (c < 128) continue;
+  if ((c & 0xc0) != 0xc0) return p - string;
+  ab = utf8_table4[c & 0x3f];  /* Number of additional bytes */
+  if (length < ab) return p - string;
+  length -= ab;
+
+  /* Check top bits in the second byte */
+  if ((*(++p) & 0xc0) != 0x80) return p - string;
+
+  /* Check for overlong sequences for each different length */
+  switch (ab)
+    {
+    /* Check for xx00 000x */
+    case 1:
+    if ((c & 0x3e) == 0) return p - string;
+    continue;   /* We know there aren't any more bytes to check */
+
+    /* Check for 1110 0000, xx0x xxxx */
+    case 2:
+    if (c == 0xe0 && (*p & 0x20) == 0) return p - string;
+    break;
+
+    /* Check for 1111 0000, xx00 xxxx */
+    case 3:
+    if (c == 0xf0 && (*p & 0x30) == 0) return p - string;
+    break;
+
+    /* Check for 1111 1000, xx00 0xxx */
+    case 4:
+    if (c == 0xf8 && (*p & 0x38) == 0) return p - string;
+    break;
+
+    /* Check for leading 0xfe or 0xff, and then for 1111 1100, xx00 00xx */
+    case 5:
+    if (c == 0xfe || c == 0xff ||
+       (c == 0xfc && (*p & 0x3c) == 0)) return p - string;
+    break;
+    }
+
+  /* Check for valid bytes after the 2nd, if any; all must start 10 */
+  while (--ab > 0)
+    {
+    if ((*(++p) & 0xc0) != 0x80) return p - string;
+    }
+  }
+
+return -1;
+}
+#endif
+
+
+
+/*************************************************
+*        Compile a Regular Expression            *
+*************************************************/
+
+/* This function takes a string and returns a pointer to a block of store
+holding a compiled version of the expression.
+
+Arguments:
+  pattern      the regular expression
+  options      various option bits
+  errorptr     pointer to pointer to error text
+  erroroffset  ptr offset in pattern where error was detected
+  tables       pointer to character tables or NULL
+
+Returns:       pointer to compiled data block, or NULL on error,
+               with errorptr and erroroffset set
+*/
+
+EXPORT pcre *
+pcre_compile(const char *pattern, int options, const char **errorptr,
+  int *erroroffset, const unsigned char *tables)
+{
+real_pcre *re;
+int length = 1 + LINK_SIZE;      /* For initial BRA plus length */
+int c, firstbyte, reqbyte;
+int bracount = 0;
+int branch_extra = 0;
+int branch_newextra;
+int item_count = -1;
+int name_count = 0;
+int max_name_size = 0;
+int lastitemlength = 0;
+#ifdef SUPPORT_UTF8
+BOOL utf8;
+BOOL class_utf8;
+#endif
+BOOL inescq = FALSE;
+unsigned int brastackptr = 0;
+size_t size;
+uschar *code;
+const uschar *codestart;
+const uschar *ptr;
+compile_data compile_block;
+int brastack[BRASTACK_SIZE];
+uschar bralenstack[BRASTACK_SIZE];
+
+/* We can't pass back an error message if errorptr is NULL; I guess the best we
+can do is just return NULL. */
+
+if (errorptr == NULL) return NULL;
+*errorptr = NULL;
+
+/* However, we can give a message for this error */
+
+if (erroroffset == NULL)
+  {
+  *errorptr = ERR16;
+  return NULL;
+  }
+*erroroffset = 0;
+
+/* Can't support UTF8 unless PCRE has been compiled to include the code. */
+
+#ifdef SUPPORT_UTF8
+utf8 = (options & PCRE_UTF8) != 0;
+if (utf8 && (options & PCRE_NO_UTF8_CHECK) == 0 &&
+     (*erroroffset = valid_utf8((uschar *)pattern, -1)) >= 0)
+  {
+  *errorptr = ERR44;
+  return NULL;
+  }
+#else
+if ((options & PCRE_UTF8) != 0)
+  {
+  *errorptr = ERR32;
+  return NULL;
+  }
+#endif
+
+if ((options & ~PUBLIC_OPTIONS) != 0)
+  {
+  *errorptr = ERR17;
+  return NULL;
+  }
+
+/* Set up pointers to the individual character tables */
+
+if (tables == NULL) tables = pcre_default_tables;
+compile_block.lcc = tables + lcc_offset;
+compile_block.fcc = tables + fcc_offset;
+compile_block.cbits = tables + cbits_offset;
+compile_block.ctypes = tables + ctypes_offset;
+
+/* Maximum back reference and backref bitmap. This is updated for numeric
+references during the first pass, but for named references during the actual
+compile pass. The bitmap records up to 31 back references to help in deciding
+whether (.*) can be treated as anchored or not. */
+
+compile_block.top_backref = 0;
+compile_block.backref_map = 0;
+
+/* Reflect pattern for debugging output */
+
+DPRINTF(("------------------------------------------------------------------\n"));
+DPRINTF(("%s\n", pattern));
+
+/* The first thing to do is to make a pass over the pattern to compute the
+amount of store required to hold the compiled code. This does not have to be
+perfect as long as errors are overestimates. At the same time we can detect any
+flag settings right at the start, and extract them. Make an attempt to correct
+for any counted white space if an "extended" flag setting appears late in the
+pattern. We can't be so clever for #-comments. */
+
+ptr = (const uschar *)(pattern - 1);
+while ((c = *(++ptr)) != 0)
+  {
+  int min, max;
+  int class_optcount;
+  int bracket_length;
+  int duplength;
+
+  /* If we are inside a \Q...\E sequence, all chars are literal */
+
+  if (inescq)
+    {
+    if ((options & PCRE_AUTO_CALLOUT) != 0) length += 2 + 2*LINK_SIZE;
+    goto NORMAL_CHAR;
+    }
+
+  /* Otherwise, first check for ignored whitespace and comments */
+
+  if ((options & PCRE_EXTENDED) != 0)
+    {
+    if ((compile_block.ctypes[c] & ctype_space) != 0) continue;
+    if (c == '#')
+      {
+      /* The space before the ; is to avoid a warning on a silly compiler
+      on the Macintosh. */
+      while ((c = *(++ptr)) != 0 && c != NEWLINE) ;
+      if (c == 0) break;
+      continue;
+      }
+    }
+
+  item_count++;    /* Is zero for the first non-comment item */
+
+  /* Allow space for auto callout before every item except quantifiers. */
+
+  if ((options & PCRE_AUTO_CALLOUT) != 0 &&
+       c != '*' && c != '+' && c != '?' &&
+       (c != '{' || !is_counted_repeat(ptr + 1)))
+    length += 2 + 2*LINK_SIZE;
+
+  switch(c)
+    {
+    /* A backslashed item may be an escaped data character or it may be a
+    character type. */
+
+    case '\\':
+    c = check_escape(&ptr, errorptr, bracount, options, FALSE);
+    if (*errorptr != NULL) goto PCRE_ERROR_RETURN;
+
+    lastitemlength = 1;     /* Default length of last item for repeats */
+
+    if (c >= 0)             /* Data character */
+      {
+      length += 2;          /* For a one-byte character */
+
+#ifdef SUPPORT_UTF8
+      if (utf8 && c > 127)
+        {
+        int i;
+        for (i = 0; i < sizeof(utf8_table1)/sizeof(int); i++)
+          if (c <= utf8_table1[i]) break;
+        length += i;
+        lastitemlength += i;
+        }
+#endif
+
+      continue;
+      }
+
+    /* If \Q, enter "literal" mode */
+
+    if (-c == ESC_Q)
+      {
+      inescq = TRUE;
+      continue;
+      }
+
+    /* \X is supported only if Unicode property support is compiled */
+
+#ifndef SUPPORT_UCP
+    if (-c == ESC_X)
+      {
+      *errorptr = ERR45;
+      goto PCRE_ERROR_RETURN;
+      }
+#endif
+
+    /* \P and \p are for Unicode properties, but only when the support has
+    been compiled. Each item needs 2 bytes. */
+
+    else if (-c == ESC_P || -c == ESC_p)
+      {
+#ifdef SUPPORT_UCP
+      BOOL negated;
+      length += 2;
+      lastitemlength = 2;
+      if (get_ucp(&ptr, &negated, errorptr) < 0) goto PCRE_ERROR_RETURN;
+      continue;
+#else
+      *errorptr = ERR45;
+      goto PCRE_ERROR_RETURN;
+#endif
+      }
+
+    /* Other escapes need one byte */
+
+    length++;
+
+    /* A back reference needs an additional 2 bytes, plus either one or 5
+    bytes for a repeat. We also need to keep the value of the highest
+    back reference. */
+
+    if (c <= -ESC_REF)
+      {
+      int refnum = -c - ESC_REF;
+      compile_block.backref_map |= (refnum < 32)? (1 << refnum) : 1;
+      if (refnum > compile_block.top_backref)
+        compile_block.top_backref = refnum;
+      length += 2;   /* For single back reference */
+      if (ptr[1] == '{' && is_counted_repeat(ptr+2))
+        {
+        ptr = read_repeat_counts(ptr+2, &min, &max, errorptr);
+        if (*errorptr != NULL) goto PCRE_ERROR_RETURN;
+        if ((min == 0 && (max == 1 || max == -1)) ||
+          (min == 1 && max == -1))
+            length++;
+        else length += 5;
+        if (ptr[1] == '?') ptr++;
+        }
+      }
+    continue;
+
+    case '^':     /* Single-byte metacharacters */
+    case '.':
+    case '$':
+    length++;
+    lastitemlength = 1;
+    continue;
+
+    case '*':            /* These repeats won't be after brackets; */
+    case '+':            /* those are handled separately */
+    case '?':
+    length++;
+    goto POSESSIVE;      /* A few lines below */
+
+    /* This covers the cases of braced repeats after a single char, metachar,
+    class, or back reference. */
+
+    case '{':
+    if (!is_counted_repeat(ptr+1)) goto NORMAL_CHAR;
+    ptr = read_repeat_counts(ptr+1, &min, &max, errorptr);
+    if (*errorptr != NULL) goto PCRE_ERROR_RETURN;
+
+    /* These special cases just insert one extra opcode */
+
+    if ((min == 0 && (max == 1 || max == -1)) ||
+      (min == 1 && max == -1))
+        length++;
+
+    /* These cases might insert additional copies of a preceding character. */
+
+    else
+      {
+      if (min != 1)
+        {
+        length -= lastitemlength;   /* Uncount the original char or metachar */
+        if (min > 0) length += 3 + lastitemlength;
+        }
+      length += lastitemlength + ((max > 0)? 3 : 1);
+      }
+
+    if (ptr[1] == '?') ptr++;      /* Needs no extra length */
+
+    POSESSIVE:                     /* Test for possessive quantifier */
+    if (ptr[1] == '+')
+      {
+      ptr++;
+      length += 2 + 2*LINK_SIZE;   /* Allow for atomic brackets */
+      }
+    continue;
+
+    /* An alternation contains an offset to the next branch or ket. If any ims
+    options changed in the previous branch(es), and/or if we are in a
+    lookbehind assertion, extra space will be needed at the start of the
+    branch. This is handled by branch_extra. */
+
+    case '|':
+    length += 1 + LINK_SIZE + branch_extra;
+    continue;
+
+    /* A character class uses 33 characters provided that all the character
+    values are less than 256. Otherwise, it uses a bit map for low valued
+    characters, and individual items for others. Don't worry about character
+    types that aren't allowed in classes - they'll get picked up during the
+    compile. A character class that contains only one single-byte character
+    uses 2 or 3 bytes, depending on whether it is negated or not. Notice this
+    where we can. (In UTF-8 mode we can do this only for chars < 128.) */
+
+    case '[':
+    if (*(++ptr) == '^')
+      {
+      class_optcount = 10;  /* Greater than one */
+      ptr++;
+      }
+    else class_optcount = 0;
+
+#ifdef SUPPORT_UTF8
+    class_utf8 = FALSE;
+#endif
+
+    /* Written as a "do" so that an initial ']' is taken as data */
+
+    if (*ptr != 0) do
+      {
+      /* Inside \Q...\E everything is literal except \E */
+
+      if (inescq)
+        {
+        if (*ptr != '\\' || ptr[1] != 'E') goto GET_ONE_CHARACTER;
+        inescq = FALSE;
+        ptr += 1;
+        continue;
+        }
+
+      /* Outside \Q...\E, check for escapes */
+
+      if (*ptr == '\\')
+        {
+        c = check_escape(&ptr, errorptr, bracount, options, TRUE);
+        if (*errorptr != NULL) goto PCRE_ERROR_RETURN;
+
+        /* \b is backspace inside a class; \X is literal */
+
+        if (-c == ESC_b) c = '\b';
+        else if (-c == ESC_X) c = 'X';
+
+        /* \Q enters quoting mode */
+
+        else if (-c == ESC_Q)
+          {
+          inescq = TRUE;
+          continue;
+          }
+
+        /* Handle escapes that turn into characters */
+
+        if (c >= 0) goto NON_SPECIAL_CHARACTER;
+
+        /* Escapes that are meta-things. The normal ones just affect the
+        bit map, but Unicode properties require an XCLASS extended item. */
+
+        else
+          {
+          class_optcount = 10;         /* \d, \s etc; make sure > 1 */
+#ifdef SUPPORT_UTF8
+          if (-c == ESC_p || -c == ESC_P)
+            {
+            if (!class_utf8)
+              {
+              class_utf8 = TRUE;
+              length += LINK_SIZE + 2;
+              }
+            length += 2;
+            }
+#endif
+          }
+        }
+
+      /* Check the syntax for POSIX stuff. The bits we actually handle are
+      checked during the real compile phase. */
+
+      else if (*ptr == '[' && check_posix_syntax(ptr, &ptr, &compile_block))
+        {
+        ptr++;
+        class_optcount = 10;    /* Make sure > 1 */
+        }
+
+      /* Anything else increments the possible optimization count. We have to
+      detect ranges here so that we can compute the number of extra ranges for
+      caseless wide characters when UCP support is available. If there are wide
+      characters, we are going to have to use an XCLASS, even for single
+      characters. */
+
+      else
+        {
+        int d;
+
+        GET_ONE_CHARACTER:
+
+#ifdef SUPPORT_UTF8
+        if (utf8)
+          {
+          int extra = 0;
+          GETCHARLEN(c, ptr, extra);
+          ptr += extra;
+          }
+        else c = *ptr;
+#else
+        c = *ptr;
+#endif
+
+        /* Come here from handling \ above when it escapes to a char value */
+
+        NON_SPECIAL_CHARACTER:
+        class_optcount++;
+
+        d = -1;
+        if (ptr[1] == '-')
+          {
+          uschar const *hyptr = ptr++;
+          if (ptr[1] == '\\')
+            {
+            ptr++;
+            d = check_escape(&ptr, errorptr, bracount, options, TRUE);
+            if (*errorptr != NULL) goto PCRE_ERROR_RETURN;
+            if (-d == ESC_b) d = '\b';        /* backspace */
+            else if (-d == ESC_X) d = 'X';    /* literal X in a class */
+            }
+          else if (ptr[1] != 0 && ptr[1] != ']')
+            {
+            ptr++;
+#ifdef SUPPORT_UTF8
+            if (utf8)
+              {
+              int extra = 0;
+              GETCHARLEN(d, ptr, extra);
+              ptr += extra;
+              }
+            else
+#endif
+            d = *ptr;
+            }
+          if (d < 0) ptr = hyptr;      /* go back to hyphen as data */
+          }
+
+        /* If d >= 0 we have a range. In UTF-8 mode, if the end is > 255, or >
+        127 for caseless matching, we will need to use an XCLASS. */
+
+        if (d >= 0)
+          {
+          class_optcount = 10;     /* Ensure > 1 */
+          if (d < c)
+            {
+            *errorptr = ERR8;
+            goto PCRE_ERROR_RETURN;
+            }
+
+#ifdef SUPPORT_UTF8
+          if (utf8 && (d > 255 || ((options & PCRE_CASELESS) != 0 && d > 127)))
+            {
+            uschar buffer[6];
+            if (!class_utf8)         /* Allow for XCLASS overhead */
+              {
+              class_utf8 = TRUE;
+              length += LINK_SIZE + 2;
+              }
+
+#ifdef SUPPORT_UCP
+            /* If we have UCP support, find out how many extra ranges are
+            needed to map the other case of characters within this range. We
+            have to mimic the range optimization here, because extending the
+            range upwards might push d over a boundary that makes is use
+            another byte in the UTF-8 representation. */
+
+            if ((options & PCRE_CASELESS) != 0)
+              {
+              int occ, ocd;
+              int cc = c;
+              int origd = d;
+              while (get_othercase_range(&cc, origd, &occ, &ocd))
+                {
+                if (occ >= c && ocd <= d) continue;   /* Skip embedded */
+
+                if (occ < c  && ocd >= c - 1)  /* Extend the basic range */
+                  {                            /* if there is overlap,   */
+                  c = occ;                     /* noting that if occ < c */
+                  continue;                    /* we can't have ocd > d  */
+                  }                            /* because a subrange is  */
+                if (ocd > d && occ <= d + 1)   /* always shorter than    */
+                  {                            /* the basic range.       */
+                  d = ocd;
+                  continue;
+                  }
+
+                /* An extra item is needed */
+
+                length += 1 + ord2utf8(occ, buffer) +
+                  ((occ == ocd)? 0 : ord2utf8(ocd, buffer));
+                }
+              }
+#endif  /* SUPPORT_UCP */
+
+            /* The length of the (possibly extended) range */
+
+            length += 1 + ord2utf8(c, buffer) + ord2utf8(d, buffer);
+            }
+#endif  /* SUPPORT_UTF8 */
+
+          }
+
+        /* We have a single character. There is nothing to be done unless we
+        are in UTF-8 mode. If the char is > 255, or 127 when caseless, we must
+        allow for an XCL_SINGLE item, doubled for caselessness if there is UCP
+        support. */
+
+        else
+          {
+#ifdef SUPPORT_UTF8
+          if (utf8 && (c > 255 || ((options & PCRE_CASELESS) != 0 && c > 127)))
+            {
+            uschar buffer[6];
+            class_optcount = 10;     /* Ensure > 1 */
+            if (!class_utf8)         /* Allow for XCLASS overhead */
+              {
+              class_utf8 = TRUE;
+              length += LINK_SIZE + 2;
+              }
+#ifdef SUPPORT_UCP
+            length += (((options & PCRE_CASELESS) != 0)? 2 : 1) *
+              (1 + ord2utf8(c, buffer));
+#else   /* SUPPORT_UCP */
+            length += 1 + ord2utf8(c, buffer);
+#endif  /* SUPPORT_UCP */
+            }
+#endif  /* SUPPORT_UTF8 */
+          }
+        }
+      }
+    while (*(++ptr) != 0 && (inescq || *ptr != ']')); /* Concludes "do" above */
+
+    if (*ptr == 0)                          /* Missing terminating ']' */
+      {
+      *errorptr = ERR6;
+      goto PCRE_ERROR_RETURN;
+      }
+
+    /* We can optimize when there was only one optimizable character. Repeats
+    for positive and negated single one-byte chars are handled by the general
+    code. Here, we handle repeats for the class opcodes. */
+
+    if (class_optcount == 1) length += 3; else
+      {
+      length += 33;
+
+      /* A repeat needs either 1 or 5 bytes. If it is a possessive quantifier,
+      we also need extra for wrapping the whole thing in a sub-pattern. */
+
+      if (*ptr != 0 && ptr[1] == '{' && is_counted_repeat(ptr+2))
+        {
+        ptr = read_repeat_counts(ptr+2, &min, &max, errorptr);
+        if (*errorptr != NULL) goto PCRE_ERROR_RETURN;
+        if ((min == 0 && (max == 1 || max == -1)) ||
+          (min == 1 && max == -1))
+            length++;
+        else length += 5;
+        if (ptr[1] == '+')
+          {
+          ptr++;
+          length += 2 + 2*LINK_SIZE;
+          }
+        else if (ptr[1] == '?') ptr++;
+        }
+      }
+    continue;
+
+    /* Brackets may be genuine groups or special things */
+
+    case '(':
+    branch_newextra = 0;
+    bracket_length = 1 + LINK_SIZE;
+
+    /* Handle special forms of bracket, which all start (? */
+
+    if (ptr[1] == '?')
+      {
+      int set, unset;
+      int *optset;
+
+      switch (c = ptr[2])
+        {
+        /* Skip over comments entirely */
+        case '#':
+        ptr += 3;
+        while (*ptr != 0 && *ptr != ')') ptr++;
+        if (*ptr == 0)
+          {
+          *errorptr = ERR18;
+          goto PCRE_ERROR_RETURN;
+          }
+        continue;
+
+        /* Non-referencing groups and lookaheads just move the pointer on, and
+        then behave like a non-special bracket, except that they don't increment
+        the count of extracting brackets. Ditto for the "once only" bracket,
+        which is in Perl from version 5.005. */
+
+        case ':':
+        case '=':
+        case '!':
+        case '>':
+        ptr += 2;
+        break;
+
+        /* (?R) specifies a recursive call to the regex, which is an extension
+        to provide the facility which can be obtained by (?p{perl-code}) in
+        Perl 5.6. In Perl 5.8 this has become (??{perl-code}).
+
+        From PCRE 4.00, items such as (?3) specify subroutine-like "calls" to
+        the appropriate numbered brackets. This includes both recursive and
+        non-recursive calls. (?R) is now synonymous with (?0). */
+
+        case 'R':
+        ptr++;
+
+        case '0': case '1': case '2': case '3': case '4':
+        case '5': case '6': case '7': case '8': case '9':
+        ptr += 2;
+        if (c != 'R')
+          while ((digitab[*(++ptr)] & ctype_digit) != 0);
+        if (*ptr != ')')
+          {
+          *errorptr = ERR29;
+          goto PCRE_ERROR_RETURN;
+          }
+        length += 1 + LINK_SIZE;
+
+        /* If this item is quantified, it will get wrapped inside brackets so
+        as to use the code for quantified brackets. We jump down and use the
+        code that handles this for real brackets. */
+
+        if (ptr[1] == '+' || ptr[1] == '*' || ptr[1] == '?' || ptr[1] == '{')
+          {
+          length += 2 + 2 * LINK_SIZE;       /* to make bracketed */
+          duplength = 5 + 3 * LINK_SIZE;
+          goto HANDLE_QUANTIFIED_BRACKETS;
+          }
+        continue;
+
+        /* (?C) is an extension which provides "callout" - to provide a bit of
+        the functionality of the Perl (?{...}) feature. An optional number may
+        follow (default is zero). */
+
+        case 'C':
+        ptr += 2;
+        while ((digitab[*(++ptr)] & ctype_digit) != 0);
+        if (*ptr != ')')
+          {
+          *errorptr = ERR39;
+          goto PCRE_ERROR_RETURN;
+          }
+        length += 2 + 2*LINK_SIZE;
+        continue;
+
+        /* Named subpatterns are an extension copied from Python */
+
+        case 'P':
+        ptr += 3;
+        if (*ptr == '<')
+          {
+          const uschar *p;    /* Don't amalgamate; some compilers */
+          p = ++ptr;          /* grumble at autoincrement in declaration */
+          while ((compile_block.ctypes[*ptr] & ctype_word) != 0) ptr++;
+          if (*ptr != '>')
+            {
+            *errorptr = ERR42;
+            goto PCRE_ERROR_RETURN;
+            }
+          name_count++;
+          if (ptr - p > max_name_size) max_name_size = (ptr - p);
+          break;
+          }
+
+        if (*ptr == '=' || *ptr == '>')
+          {
+          while ((compile_block.ctypes[*(++ptr)] & ctype_word) != 0);
+          if (*ptr != ')')
+            {
+            *errorptr = ERR42;
+            goto PCRE_ERROR_RETURN;
+            }
+          break;
+          }
+
+        /* Unknown character after (?P */
+
+        *errorptr = ERR41;
+        goto PCRE_ERROR_RETURN;
+
+        /* Lookbehinds are in Perl from version 5.005 */
+
+        case '<':
+        ptr += 3;
+        if (*ptr == '=' || *ptr == '!')
+          {
+          branch_newextra = 1 + LINK_SIZE;
+          length += 1 + LINK_SIZE;         /* For the first branch */
+          break;
+          }
+        *errorptr = ERR24;
+        goto PCRE_ERROR_RETURN;
+
+        /* Conditionals are in Perl from version 5.005. The bracket must either
+        be followed by a number (for bracket reference) or by an assertion
+        group, or (a PCRE extension) by 'R' for a recursion test. */
+
+        case '(':
+        if (ptr[3] == 'R' && ptr[4] == ')')
+          {
+          ptr += 4;
+          length += 3;
+          }
+        else if ((digitab[ptr[3]] & ctype_digit) != 0)
+          {
+          ptr += 4;
+          length += 3;
+          while ((digitab[*ptr] & ctype_digit) != 0) ptr++;
+          if (*ptr != ')')
+            {
+            *errorptr = ERR26;
+            goto PCRE_ERROR_RETURN;
+            }
+          }
+        else   /* An assertion must follow */
+          {
+          ptr++;   /* Can treat like ':' as far as spacing is concerned */
+          if (ptr[2] != '?' ||
+             (ptr[3] != '=' && ptr[3] != '!' && ptr[3] != '<') )
+            {
+            ptr += 2;    /* To get right offset in message */
+            *errorptr = ERR28;
+            goto PCRE_ERROR_RETURN;
+            }
+          }
+        break;
+
+        /* Else loop checking valid options until ) is met. Anything else is an
+        error. If we are without any brackets, i.e. at top level, the settings
+        act as if specified in the options, so massage the options immediately.
+        This is for backward compatibility with Perl 5.004. */
+
+        default:
+        set = unset = 0;
+        optset = &set;
+        ptr += 2;
+
+        for (;; ptr++)
+          {
+          c = *ptr;
+          switch (c)
+            {
+            case 'i':
+            *optset |= PCRE_CASELESS;
+            continue;
+
+            case 'm':
+            *optset |= PCRE_MULTILINE;
+            continue;
+
+            case 's':
+            *optset |= PCRE_DOTALL;
+            continue;
+
+            case 'x':
+            *optset |= PCRE_EXTENDED;
+            continue;
+
+            case 'X':
+            *optset |= PCRE_EXTRA;
+            continue;
+
+            case 'U':
+            *optset |= PCRE_UNGREEDY;
+            continue;
+
+            case '-':
+            optset = &unset;
+            continue;
+
+            /* A termination by ')' indicates an options-setting-only item; if
+            this is at the very start of the pattern (indicated by item_count
+            being zero), we use it to set the global options. This is helpful
+            when analyzing the pattern for first characters, etc. Otherwise
+            nothing is done here and it is handled during the compiling
+            process.
+
+            [Historical note: Up to Perl 5.8, options settings at top level
+            were always global settings, wherever they appeared in the pattern.
+            That is, they were equivalent to an external setting. From 5.8
+            onwards, they apply only to what follows (which is what you might
+            expect).] */
+
+            case ')':
+            if (item_count == 0)
+              {
+              options = (options | set) & (~unset);
+              set = unset = 0;     /* To save length */
+              item_count--;        /* To allow for several */
+              }
+
+            /* Fall through */
+
+            /* A termination by ':' indicates the start of a nested group with
+            the given options set. This is again handled at compile time, but
+            we must allow for compiled space if any of the ims options are
+            set. We also have to allow for resetting space at the end of
+            the group, which is why 4 is added to the length and not just 2.
+            If there are several changes of options within the same group, this
+            will lead to an over-estimate on the length, but this shouldn't
+            matter very much. We also have to allow for resetting options at
+            the start of any alternations, which we do by setting
+            branch_newextra to 2. Finally, we record whether the case-dependent
+            flag ever changes within the regex. This is used by the "required
+            character" code. */
+
+            case ':':
+            if (((set|unset) & PCRE_IMS) != 0)
+              {
+              length += 4;
+              branch_newextra = 2;
+              if (((set|unset) & PCRE_CASELESS) != 0) options |= PCRE_ICHANGED;
+              }
+            goto END_OPTIONS;
+
+            /* Unrecognized option character */
+
+            default:
+            *errorptr = ERR12;
+            goto PCRE_ERROR_RETURN;
+            }
+          }
+
+        /* If we hit a closing bracket, that's it - this is a freestanding
+        option-setting. We need to ensure that branch_extra is updated if
+        necessary. The only values branch_newextra can have here are 0 or 2.
+        If the value is 2, then branch_extra must either be 2 or 5, depending
+        on whether this is a lookbehind group or not. */
+
+        END_OPTIONS:
+        if (c == ')')
+          {
+          if (branch_newextra == 2 &&
+              (branch_extra == 0 || branch_extra == 1+LINK_SIZE))
+            branch_extra += branch_newextra;
+          continue;
+          }
+
+        /* If options were terminated by ':' control comes here. Fall through
+        to handle the group below. */
+        }
+      }
+
+    /* Extracting brackets must be counted so we can process escapes in a
+    Perlish way. If the number exceeds EXTRACT_BASIC_MAX we are going to
+    need an additional 3 bytes of store per extracting bracket. However, if
+    PCRE_NO_AUTO)CAPTURE is set, unadorned brackets become non-capturing, so we
+    must leave the count alone (it will aways be zero). */
+
+    else if ((options & PCRE_NO_AUTO_CAPTURE) == 0)
+      {
+      bracount++;
+      if (bracount > EXTRACT_BASIC_MAX) bracket_length += 3;
+      }
+
+    /* Save length for computing whole length at end if there's a repeat that
+    requires duplication of the group. Also save the current value of
+    branch_extra, and start the new group with the new value. If non-zero, this
+    will either be 2 for a (?imsx: group, or 3 for a lookbehind assertion. */
+
+    if (brastackptr >= sizeof(brastack)/sizeof(int))
+      {
+      *errorptr = ERR19;
+      goto PCRE_ERROR_RETURN;
+      }
+
+    bralenstack[brastackptr] = branch_extra;
+    branch_extra = branch_newextra;
+
+    brastack[brastackptr++] = length;
+    length += bracket_length;
+    continue;
+
+    /* Handle ket. Look for subsequent max/min; for certain sets of values we
+    have to replicate this bracket up to that many times. If brastackptr is
+    0 this is an unmatched bracket which will generate an error, but take care
+    not to try to access brastack[-1] when computing the length and restoring
+    the branch_extra value. */
+
+    case ')':
+    length += 1 + LINK_SIZE;
+    if (brastackptr > 0)
+      {
+      duplength = length - brastack[--brastackptr];
+      branch_extra = bralenstack[brastackptr];
+      }
+    else duplength = 0;
+
+    /* The following code is also used when a recursion such as (?3) is
+    followed by a quantifier, because in that case, it has to be wrapped inside
+    brackets so that the quantifier works. The value of duplength must be
+    set before arrival. */
+
+    HANDLE_QUANTIFIED_BRACKETS:
+
+    /* Leave ptr at the final char; for read_repeat_counts this happens
+    automatically; for the others we need an increment. */
+
+    if ((c = ptr[1]) == '{' && is_counted_repeat(ptr+2))
+      {
+      ptr = read_repeat_counts(ptr+2, &min, &max, errorptr);
+      if (*errorptr != NULL) goto PCRE_ERROR_RETURN;
+      }
+    else if (c == '*') { min = 0; max = -1; ptr++; }
+    else if (c == '+') { min = 1; max = -1; ptr++; }
+    else if (c == '?') { min = 0; max = 1;  ptr++; }
+    else { min = 1; max = 1; }
+
+    /* If the minimum is zero, we have to allow for an OP_BRAZERO before the
+    group, and if the maximum is greater than zero, we have to replicate
+    maxval-1 times; each replication acquires an OP_BRAZERO plus a nesting
+    bracket set. */
+
+    if (min == 0)
+      {
+      length++;
+      if (max > 0) length += (max - 1) * (duplength + 3 + 2*LINK_SIZE);
+      }
+
+    /* When the minimum is greater than zero, we have to replicate up to
+    minval-1 times, with no additions required in the copies. Then, if there
+    is a limited maximum we have to replicate up to maxval-1 times allowing
+    for a BRAZERO item before each optional copy and nesting brackets for all
+    but one of the optional copies. */
+
+    else
+      {
+      length += (min - 1) * duplength;
+      if (max > min)   /* Need this test as max=-1 means no limit */
+        length += (max - min) * (duplength + 3 + 2*LINK_SIZE)
+          - (2 + 2*LINK_SIZE);
+      }
+
+    /* Allow space for once brackets for "possessive quantifier" */
+
+    if (ptr[1] == '+')
+      {
+      ptr++;
+      length += 2 + 2*LINK_SIZE;
+      }
+    continue;
+
+    /* Non-special character. It won't be space or # in extended mode, so it is
+    always a genuine character. If we are in a \Q...\E sequence, check for the
+    end; if not, we have a literal. */
+
+    default:
+    NORMAL_CHAR:
+
+    if (inescq && c == '\\' && ptr[1] == 'E')
+      {
+      inescq = FALSE;
+      ptr++;
+      continue;
+      }
+
+    length += 2;          /* For a one-byte character */
+    lastitemlength = 1;   /* Default length of last item for repeats */
+
+    /* In UTF-8 mode, check for additional bytes. */
+
+#ifdef SUPPORT_UTF8
+    if (utf8 && (c & 0xc0) == 0xc0)
+      {
+      while ((ptr[1] & 0xc0) == 0x80)         /* Can't flow over the end */
+        {                                     /* because the end is marked */
+        lastitemlength++;                     /* by a zero byte. */
+        length++;
+        ptr++;
+        }
+      }
+#endif
+
+    continue;
+    }
+  }
+
+length += 2 + LINK_SIZE;    /* For final KET and END */
+
+if ((options & PCRE_AUTO_CALLOUT) != 0)
+  length += 2 + 2*LINK_SIZE;  /* For final callout */
+
+if (length > MAX_PATTERN_SIZE)
+  {
+  *errorptr = ERR20;
+  return NULL;
+  }
+
+/* Compute the size of data block needed and get it, either from malloc or
+externally provided function. */
+
+size = length + sizeof(real_pcre) + name_count * (max_name_size + 3);
+re = (real_pcre *)(pcre_malloc)(size);
+
+if (re == NULL)
+  {
+  *errorptr = ERR21;
+  return NULL;
+  }
+
+/* Put in the magic number, and save the sizes, options, and character table
+pointer. NULL is used for the default character tables. The nullpad field is at
+the end; it's there to help in the case when a regex compiled on a system with
+4-byte pointers is run on another with 8-byte pointers. */
+
+re->magic_number = MAGIC_NUMBER;
+re->size = size;
+re->options = options;
+re->dummy1 = re->dummy2 = 0;
+re->name_table_offset = sizeof(real_pcre);
+re->name_entry_size = max_name_size + 3;
+re->name_count = name_count;
+re->tables = (tables == pcre_default_tables)? NULL : tables;
+re->nullpad = NULL;
+
+/* The starting points of the name/number translation table and of the code are
+passed around in the compile data block. */
+
+compile_block.names_found = 0;
+compile_block.name_entry_size = max_name_size + 3;
+compile_block.name_table = (uschar *)re + re->name_table_offset;
+codestart = compile_block.name_table + re->name_entry_size * re->name_count;
+compile_block.start_code = codestart;
+compile_block.start_pattern = (const uschar *)pattern;
+compile_block.req_varyopt = 0;
+compile_block.nopartial = FALSE;
+
+/* Set up a starting, non-extracting bracket, then compile the expression. On
+error, *errorptr will be set non-NULL, so we don't need to look at the result
+of the function here. */
+
+ptr = (const uschar *)pattern;
+code = (uschar *)codestart;
+*code = OP_BRA;
+bracount = 0;
+(void)compile_regex(options, options & PCRE_IMS, &bracount, &code, &ptr,
+  errorptr, FALSE, 0, &firstbyte, &reqbyte, NULL, &compile_block);
+re->top_bracket = bracount;
+re->top_backref = compile_block.top_backref;
+
+if (compile_block.nopartial) re->options |= PCRE_NOPARTIAL;
+
+/* If not reached end of pattern on success, there's an excess bracket. */
+
+if (*errorptr == NULL && *ptr != 0) *errorptr = ERR22;
+
+/* Fill in the terminating state and check for disastrous overflow, but
+if debugging, leave the test till after things are printed out. */
+
+*code++ = OP_END;
+
+#ifndef DEBUG
+if (code - codestart > length) *errorptr = ERR23;
+#endif
+
+/* Give an error if there's back reference to a non-existent capturing
+subpattern. */
+
+if (re->top_backref > re->top_bracket) *errorptr = ERR15;
+
+/* Failed to compile, or error while post-processing */
+
+if (*errorptr != NULL)
+  {
+  (pcre_free)(re);
+  PCRE_ERROR_RETURN:
+  *erroroffset = ptr - (const uschar *)pattern;
+  return NULL;
+  }
+
+/* If the anchored option was not passed, set the flag if we can determine that
+the pattern is anchored by virtue of ^ characters or \A or anything else (such
+as starting with .* when DOTALL is set).
+
+Otherwise, if we know what the first character has to be, save it, because that
+speeds up unanchored matches no end. If not, see if we can set the
+PCRE_STARTLINE flag. This is helpful for multiline matches when all branches
+start with ^. and also when all branches start with .* for non-DOTALL matches.
+*/
+
+if ((options & PCRE_ANCHORED) == 0)
+  {
+  int temp_options = options;
+  if (is_anchored(codestart, &temp_options, 0, compile_block.backref_map))
+    re->options |= PCRE_ANCHORED;
+  else
+    {
+    if (firstbyte < 0)
+      firstbyte = find_firstassertedchar(codestart, &temp_options, FALSE);
+    if (firstbyte >= 0)   /* Remove caseless flag for non-caseable chars */
+      {
+      int ch = firstbyte & 255;
+      re->first_byte = ((firstbyte & REQ_CASELESS) != 0 &&
+         compile_block.fcc[ch] == ch)? ch : firstbyte;
+      re->options |= PCRE_FIRSTSET;
+      }
+    else if (is_startline(codestart, 0, compile_block.backref_map))
+      re->options |= PCRE_STARTLINE;
+    }
+  }
+
+/* For an anchored pattern, we use the "required byte" only if it follows a
+variable length item in the regex. Remove the caseless flag for non-caseable
+bytes. */
+
+if (reqbyte >= 0 &&
+     ((re->options & PCRE_ANCHORED) == 0 || (reqbyte & REQ_VARY) != 0))
+  {
+  int ch = reqbyte & 255;
+  re->req_byte = ((reqbyte & REQ_CASELESS) != 0 &&
+    compile_block.fcc[ch] == ch)? (reqbyte & ~REQ_CASELESS) : reqbyte;
+  re->options |= PCRE_REQCHSET;
+  }
+
+/* Print out the compiled data for debugging */
+
+#ifdef DEBUG
+
+printf("Length = %d top_bracket = %d top_backref = %d\n",
+  length, re->top_bracket, re->top_backref);
+
+if (re->options != 0)
+  {
+  printf("%s%s%s%s%s%s%s%s%s%s\n",
+    ((re->options & PCRE_NOPARTIAL) != 0)? "nopartial " : "",
+    ((re->options & PCRE_ANCHORED) != 0)? "anchored " : "",
+    ((re->options & PCRE_CASELESS) != 0)? "caseless " : "",
+    ((re->options & PCRE_ICHANGED) != 0)? "case state changed " : "",
+    ((re->options & PCRE_EXTENDED) != 0)? "extended " : "",
+    ((re->options & PCRE_MULTILINE) != 0)? "multiline " : "",
+    ((re->options & PCRE_DOTALL) != 0)? "dotall " : "",
+    ((re->options & PCRE_DOLLAR_ENDONLY) != 0)? "endonly " : "",
+    ((re->options & PCRE_EXTRA) != 0)? "extra " : "",
+    ((re->options & PCRE_UNGREEDY) != 0)? "ungreedy " : "");
+  }
+
+if ((re->options & PCRE_FIRSTSET) != 0)
+  {
+  int ch = re->first_byte & 255;
+  const char *caseless = ((re->first_byte & REQ_CASELESS) == 0)? "" : " (caseless)";
+  if (isprint(ch)) printf("First char = %c%s\n", ch, caseless);
+    else printf("First char = \\x%02x%s\n", ch, caseless);
+  }
+
+if ((re->options & PCRE_REQCHSET) != 0)
+  {
+  int ch = re->req_byte & 255;
+  const char *caseless = ((re->req_byte & REQ_CASELESS) == 0)? "" : " (caseless)";
+  if (isprint(ch)) printf("Req char = %c%s\n", ch, caseless);
+    else printf("Req char = \\x%02x%s\n", ch, caseless);
+  }
+
+print_internals(re, stdout);
+
+/* This check is done here in the debugging case so that the code that
+was compiled can be seen. */
+
+if (code - codestart > length)
+  {
+  *errorptr = ERR23;
+  (pcre_free)(re);
+  *erroroffset = ptr - (uschar *)pattern;
+  return NULL;
+  }
+#endif
+
+return (pcre *)re;
+}
+
+
+
+/*************************************************
+*          Match a back-reference                *
+*************************************************/
+
+/* If a back reference hasn't been set, the length that is passed is greater
+than the number of characters left in the string, so the match fails.
+
+Arguments:
+  offset      index into the offset vector
+  eptr        points into the subject
+  length      length to be matched
+  md          points to match data block
+  ims         the ims flags
+
+Returns:      TRUE if matched
+*/
+
+static BOOL
+match_ref(int offset, register const uschar *eptr, int length, match_data *md,
+  unsigned long int ims)
+{
+const uschar *p = md->start_subject + md->offset_vector[offset];
+
+#ifdef DEBUG
+if (eptr >= md->end_subject)
+  printf("matching subject <null>");
+else
+  {
+  printf("matching subject ");
+  pchars(eptr, length, TRUE, md);
+  }
+printf(" against backref ");
+pchars(p, length, FALSE, md);
+printf("\n");
+#endif
+
+/* Always fail if not enough characters left */
+
+if (length > md->end_subject - eptr) return FALSE;
+
+/* Separate the caselesss case for speed */
+
+if ((ims & PCRE_CASELESS) != 0)
+  {
+  while (length-- > 0)
+    if (md->lcc[*p++] != md->lcc[*eptr++]) return FALSE;
+  }
+else
+  { while (length-- > 0) if (*p++ != *eptr++) return FALSE; }
+
+return TRUE;
+}
+
+
+#ifdef SUPPORT_UTF8
+/*************************************************
+*       Match character against an XCLASS        *
+*************************************************/
+
+/* This function is called from within the XCLASS code below, to match a
+character against an extended class which might match values > 255.
+
+Arguments:
+  c           the character
+  data        points to the flag byte of the XCLASS data
+
+Returns:      TRUE if character matches, else FALSE
+*/
+
+static BOOL
+match_xclass(int c, const uschar *data)
+{
+int t;
+BOOL negated = (*data & XCL_NOT) != 0;
+
+/* Character values < 256 are matched against a bitmap, if one is present. If
+not, we still carry on, because there may be ranges that start below 256 in the
+additional data. */
+
+if (c < 256)
+  {
+  if ((*data & XCL_MAP) != 0 && (data[1 + c/8] & (1 << (c&7))) != 0)
+    return !negated;   /* char found */
+  }
+
+/* First skip the bit map if present. Then match against the list of Unicode
+properties or large chars or ranges that end with a large char. We won't ever
+encounter XCL_PROP or XCL_NOTPROP when UCP support is not compiled. */
+
+if ((*data++ & XCL_MAP) != 0) data += 32;
+
+while ((t = *data++) != XCL_END)
+  {
+  int x, y;
+  if (t == XCL_SINGLE)
+    {
+    GETCHARINC(x, data);
+    if (c == x) return !negated;
+    }
+  else if (t == XCL_RANGE)
+    {
+    GETCHARINC(x, data);
+    GETCHARINC(y, data);
+    if (c >= x && c <= y) return !negated;
+    }
+
+#ifdef SUPPORT_UCP
+  else  /* XCL_PROP & XCL_NOTPROP */
+    {
+    int chartype, othercase;
+    int rqdtype = *data++;
+    int category = ucp_findchar(c, &chartype, &othercase);
+    if (rqdtype >= 128)
+      {
+      if ((rqdtype - 128 == category) == (t == XCL_PROP)) return !negated;
+      }
+    else
+      {
+      if ((rqdtype == chartype) == (t == XCL_PROP)) return !negated;
+      }
+    }
+#endif  /* SUPPORT_UCP */
+  }
+
+return negated;   /* char did not match */
+}
+#endif
+
+
+/***************************************************************************
+****************************************************************************
+                   RECURSION IN THE match() FUNCTION
+
+The match() function is highly recursive. Some regular expressions can cause
+it to recurse thousands of times. I was writing for Unix, so I just let it
+call itself recursively. This uses the stack for saving everything that has
+to be saved for a recursive call. On Unix, the stack can be large, and this
+works fine.
+
+It turns out that on non-Unix systems there are problems with programs that
+use a lot of stack. (This despite the fact that every last chip has oodles
+of memory these days, and techniques for extending the stack have been known
+for decades.) So....
+
+There is a fudge, triggered by defining NO_RECURSE, which avoids recursive
+calls by keeping local variables that need to be preserved in blocks of memory
+obtained from malloc instead instead of on the stack. Macros are used to
+achieve this so that the actual code doesn't look very different to what it
+always used to.
+****************************************************************************
+***************************************************************************/
+
+
+/* These versions of the macros use the stack, as normal */
+
+#ifndef NO_RECURSE
+#define REGISTER register
+#define RMATCH(rx,ra,rb,rc,rd,re,rf,rg) rx = match(ra,rb,rc,rd,re,rf,rg)
+#define RRETURN(ra) return ra
+#else
+
+
+/* These versions of the macros manage a private stack on the heap. Note
+that the rd argument of RMATCH isn't actually used. It's the md argument of
+match(), which never changes. */
+
+#define REGISTER
+
+#define RMATCH(rx,ra,rb,rc,rd,re,rf,rg)\
+  {\
+  heapframe *newframe = (pcre_stack_malloc)(sizeof(heapframe));\
+  if (setjmp(frame->Xwhere) == 0)\
+    {\
+    newframe->Xeptr = ra;\
+    newframe->Xecode = rb;\
+    newframe->Xoffset_top = rc;\
+    newframe->Xims = re;\
+    newframe->Xeptrb = rf;\
+    newframe->Xflags = rg;\
+    newframe->Xprevframe = frame;\
+    frame = newframe;\
+    DPRINTF(("restarting from line %d\n", __LINE__));\
+    goto HEAP_RECURSE;\
+    }\
+  else\
+    {\
+    DPRINTF(("longjumped back to line %d\n", __LINE__));\
+    frame = md->thisframe;\
+    rx = frame->Xresult;\
+    }\
+  }
+
+#define RRETURN(ra)\
+  {\
+  heapframe *newframe = frame;\
+  frame = newframe->Xprevframe;\
+  (pcre_stack_free)(newframe);\
+  if (frame != NULL)\
+    {\
+    frame->Xresult = ra;\
+    md->thisframe = frame;\
+    longjmp(frame->Xwhere, 1);\
+    }\
+  return ra;\
+  }
+
+
+/* Structure for remembering the local variables in a private frame */
+
+typedef struct heapframe {
+  struct heapframe *Xprevframe;
+
+  /* Function arguments that may change */
+
+  const uschar *Xeptr;
+  const uschar *Xecode;
+  int Xoffset_top;
+  long int Xims;
+  eptrblock *Xeptrb;
+  int Xflags;
+
+  /* Function local variables */
+
+  const uschar *Xcallpat;
+  const uschar *Xcharptr;
+  const uschar *Xdata;
+  const uschar *Xnext;
+  const uschar *Xpp;
+  const uschar *Xprev;
+  const uschar *Xsaved_eptr;
+
+  recursion_info Xnew_recursive;
+
+  BOOL Xcur_is_word;
+  BOOL Xcondition;
+  BOOL Xminimize;
+  BOOL Xprev_is_word;
+
+  unsigned long int Xoriginal_ims;
+
+#ifdef SUPPORT_UCP
+  int Xprop_type;
+  int Xprop_fail_result;
+  int Xprop_category;
+  int Xprop_chartype;
+  int Xprop_othercase;
+  int Xprop_test_against;
+  int *Xprop_test_variable;
+#endif
+
+  int Xctype;
+  int Xfc;
+  int Xfi;
+  int Xlength;
+  int Xmax;
+  int Xmin;
+  int Xnumber;
+  int Xoffset;
+  int Xop;
+  int Xsave_capture_last;
+  int Xsave_offset1, Xsave_offset2, Xsave_offset3;
+  int Xstacksave[REC_STACK_SAVE_MAX];
+
+  eptrblock Xnewptrb;
+
+  /* Place to pass back result, and where to jump back to */
+
+  int  Xresult;
+  jmp_buf Xwhere;
+
+} heapframe;
+
+#endif
+
+
+/***************************************************************************
+***************************************************************************/
+
+
+
+/*************************************************
+*         Match from current position            *
+*************************************************/
+
+/* On entry ecode points to the first opcode, and eptr to the first character
+in the subject string, while eptrb holds the value of eptr at the start of the
+last bracketed group - used for breaking infinite loops matching zero-length
+strings. This function is called recursively in many circumstances. Whenever it
+returns a negative (error) response, the outer incarnation must also return the
+same response.
+
+Performance note: It might be tempting to extract commonly used fields from the
+md structure (e.g. utf8, end_subject) into individual variables to improve
+performance. Tests using gcc on a SPARC disproved this; in the first case, it
+made performance worse.
+
+Arguments:
+   eptr        pointer in subject
+   ecode       position in code
+   offset_top  current top pointer
+   md          pointer to "static" info for the match
+   ims         current /i, /m, and /s options
+   eptrb       pointer to chain of blocks containing eptr at start of
+                 brackets - for testing for empty matches
+   flags       can contain
+                 match_condassert - this is an assertion condition
+                 match_isgroup - this is the start of a bracketed group
+
+Returns:       MATCH_MATCH if matched            )  these values are >= 0
+               MATCH_NOMATCH if failed to match  )
+               a negative PCRE_ERROR_xxx value if aborted by an error condition
+                 (e.g. stopped by recursion limit)
+*/
+
+static int
+match(REGISTER const uschar *eptr, REGISTER const uschar *ecode,
+  int offset_top, match_data *md, unsigned long int ims, eptrblock *eptrb,
+  int flags)
+{
+/* These variables do not need to be preserved over recursion in this function,
+so they can be ordinary variables in all cases. Mark them with "register"
+because they are used a lot in loops. */
+
+register int rrc;    /* Returns from recursive calls */
+register int i;      /* Used for loops not involving calls to RMATCH() */
+register int c;      /* Character values not kept over RMATCH() calls */
+
+/* When recursion is not being used, all "local" variables that have to be
+preserved over calls to RMATCH() are part of a "frame" which is obtained from
+heap storage. Set up the top-level frame here; others are obtained from the
+heap whenever RMATCH() does a "recursion". See the macro definitions above. */
+
+#ifdef NO_RECURSE
+heapframe *frame = (pcre_stack_malloc)(sizeof(heapframe));
+frame->Xprevframe = NULL;            /* Marks the top level */
+
+/* Copy in the original argument variables */
+
+frame->Xeptr = eptr;
+frame->Xecode = ecode;
+frame->Xoffset_top = offset_top;
+frame->Xims = ims;
+frame->Xeptrb = eptrb;
+frame->Xflags = flags;
+
+/* This is where control jumps back to to effect "recursion" */
+
+HEAP_RECURSE:
+
+/* Macros make the argument variables come from the current frame */
+
+#define eptr               frame->Xeptr
+#define ecode              frame->Xecode
+#define offset_top         frame->Xoffset_top
+#define ims                frame->Xims
+#define eptrb              frame->Xeptrb
+#define flags              frame->Xflags
+
+/* Ditto for the local variables */
+
+#ifdef SUPPORT_UTF8
+#define charptr            frame->Xcharptr
+#endif
+#define callpat            frame->Xcallpat
+#define data               frame->Xdata
+#define next               frame->Xnext
+#define pp                 frame->Xpp
+#define prev               frame->Xprev
+#define saved_eptr         frame->Xsaved_eptr
+
+#define new_recursive      frame->Xnew_recursive
+
+#define cur_is_word        frame->Xcur_is_word
+#define condition          frame->Xcondition
+#define minimize           frame->Xminimize
+#define prev_is_word       frame->Xprev_is_word
+
+#define original_ims       frame->Xoriginal_ims
+
+#ifdef SUPPORT_UCP
+#define prop_type          frame->Xprop_type
+#define prop_fail_result   frame->Xprop_fail_result
+#define prop_category      frame->Xprop_category
+#define prop_chartype      frame->Xprop_chartype
+#define prop_othercase     frame->Xprop_othercase
+#define prop_test_against  frame->Xprop_test_against
+#define prop_test_variable frame->Xprop_test_variable
+#endif
+
+#define ctype              frame->Xctype
+#define fc                 frame->Xfc
+#define fi                 frame->Xfi
+#define length             frame->Xlength
+#define max                frame->Xmax
+#define min                frame->Xmin
+#define number             frame->Xnumber
+#define offset             frame->Xoffset
+#define op                 frame->Xop
+#define save_capture_last  frame->Xsave_capture_last
+#define save_offset1       frame->Xsave_offset1
+#define save_offset2       frame->Xsave_offset2
+#define save_offset3       frame->Xsave_offset3
+#define stacksave          frame->Xstacksave
+
+#define newptrb            frame->Xnewptrb
+
+/* When recursion is being used, local variables are allocated on the stack and
+get preserved during recursion in the normal way. In this environment, fi and
+i, and fc and c, can be the same variables. */
+
+#else
+#define fi i
+#define fc c
+
+
+#ifdef SUPPORT_UTF8                /* Many of these variables are used ony */
+const uschar *charptr;             /* small blocks of the code. My normal  */
+#endif                             /* style of coding would have declared  */
+const uschar *callpat;             /* them within each of those blocks.    */
+const uschar *data;                /* However, in order to accommodate the */
+const uschar *next;                /* version of this code that uses an    */
+const uschar *pp;                  /* external "stack" implemented on the  */
+const uschar *prev;                /* heap, it is easier to declare them   */
+const uschar *saved_eptr;          /* all here, so the declarations can    */
+                                   /* be cut out in a block. The only      */
+recursion_info new_recursive;      /* declarations within blocks below are */
+                                   /* for variables that do not have to    */
+BOOL cur_is_word;                  /* be preserved over a recursive call   */
+BOOL condition;                    /* to RMATCH().                         */
+BOOL minimize;
+BOOL prev_is_word;
+
+unsigned long int original_ims;
+
+#ifdef SUPPORT_UCP
+int prop_type;
+int prop_fail_result;
+int prop_category;
+int prop_chartype;
+int prop_othercase;
+int prop_test_against;
+int *prop_test_variable;
+#endif
+
+int ctype;
+int length;
+int max;
+int min;
+int number;
+int offset;
+int op;
+int save_capture_last;
+int save_offset1, save_offset2, save_offset3;
+int stacksave[REC_STACK_SAVE_MAX];
+
+eptrblock newptrb;
+#endif
+
+/* These statements are here to stop the compiler complaining about unitialized
+variables. */
+
+#ifdef SUPPORT_UCP
+prop_fail_result = 0;
+prop_test_against = 0;
+prop_test_variable = NULL;
+#endif
+
+/* OK, now we can get on with the real code of the function. Recursion is
+specified by the macros RMATCH and RRETURN. When NO_RECURSE is *not* defined,
+these just turn into a recursive call to match() and a "return", respectively.
+However, RMATCH isn't like a function call because it's quite a complicated
+macro. It has to be used in one particular way. This shouldn't, however, impact
+performance when true recursion is being used. */
+
+if (md->match_call_count++ >= md->match_limit) RRETURN(PCRE_ERROR_MATCHLIMIT);
+
+original_ims = ims;    /* Save for resetting on ')' */
+
+/* At the start of a bracketed group, add the current subject pointer to the
+stack of such pointers, to be re-instated at the end of the group when we hit
+the closing ket. When match() is called in other circumstances, we don't add to
+this stack. */
+
+if ((flags & match_isgroup) != 0)
+  {
+  newptrb.epb_prev = eptrb;
+  newptrb.epb_saved_eptr = eptr;
+  eptrb = &newptrb;
+  }
+
+/* Now start processing the operations. */
+
+for (;;)
+  {
+  op = *ecode;
+  minimize = FALSE;
+
+  /* For partial matching, remember if we ever hit the end of the subject after
+  matching at least one subject character. */
+
+  if (md->partial &&
+      eptr >= md->end_subject &&
+      eptr > md->start_match)
+    md->hitend = TRUE;
+
+  /* Opening capturing bracket. If there is space in the offset vector, save
+  the current subject position in the working slot at the top of the vector. We
+  mustn't change the current values of the data slot, because they may be set
+  from a previous iteration of this group, and be referred to by a reference
+  inside the group.
+
+  If the bracket fails to match, we need to restore this value and also the
+  values of the final offsets, in case they were set by a previous iteration of
+  the same bracket.
+
+  If there isn't enough space in the offset vector, treat this as if it were a
+  non-capturing bracket. Don't worry about setting the flag for the error case
+  here; that is handled in the code for KET. */
+
+  if (op > OP_BRA)
+    {
+    number = op - OP_BRA;
+
+    /* For extended extraction brackets (large number), we have to fish out the
+    number from a dummy opcode at the start. */
+
+    if (number > EXTRACT_BASIC_MAX)
+      number = GET2(ecode, 2+LINK_SIZE);
+    offset = number << 1;
+
+#ifdef DEBUG
+    printf("start bracket %d subject=", number);
+    pchars(eptr, 16, TRUE, md);
+    printf("\n");
+#endif
+
+    if (offset < md->offset_max)
+      {
+      save_offset1 = md->offset_vector[offset];
+      save_offset2 = md->offset_vector[offset+1];
+      save_offset3 = md->offset_vector[md->offset_end - number];
+      save_capture_last = md->capture_last;
+
+      DPRINTF(("saving %d %d %d\n", save_offset1, save_offset2, save_offset3));
+      md->offset_vector[md->offset_end - number] = eptr - md->start_subject;
+
+      do
+        {
+        RMATCH(rrc, eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, eptrb,
+          match_isgroup);
+        if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+        md->capture_last = save_capture_last;
+        ecode += GET(ecode, 1);
+        }
+      while (*ecode == OP_ALT);
+
+      DPRINTF(("bracket %d failed\n", number));
+
+      md->offset_vector[offset] = save_offset1;
+      md->offset_vector[offset+1] = save_offset2;
+      md->offset_vector[md->offset_end - number] = save_offset3;
+
+      RRETURN(MATCH_NOMATCH);
+      }
+
+    /* Insufficient room for saving captured contents */
+
+    else op = OP_BRA;
+    }
+
+  /* Other types of node can be handled by a switch */
+
+  switch(op)
+    {
+    case OP_BRA:     /* Non-capturing bracket: optimized */
+    DPRINTF(("start bracket 0\n"));
+    do
+      {
+      RMATCH(rrc, eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, eptrb,
+        match_isgroup);
+      if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+      ecode += GET(ecode, 1);
+      }
+    while (*ecode == OP_ALT);
+    DPRINTF(("bracket 0 failed\n"));
+    RRETURN(MATCH_NOMATCH);
+
+    /* Conditional group: compilation checked that there are no more than
+    two branches. If the condition is false, skipping the first branch takes us
+    past the end if there is only one branch, but that's OK because that is
+    exactly what going to the ket would do. */
+
+    case OP_COND:
+    if (ecode[LINK_SIZE+1] == OP_CREF) /* Condition extract or recurse test */
+      {
+      offset = GET2(ecode, LINK_SIZE+2) << 1;  /* Doubled ref number */
+      condition = (offset == CREF_RECURSE * 2)?
+        (md->recursive != NULL) :
+        (offset < offset_top && md->offset_vector[offset] >= 0);
+      RMATCH(rrc, eptr, ecode + (condition?
+        (LINK_SIZE + 4) : (LINK_SIZE + 1 + GET(ecode, 1))),
+        offset_top, md, ims, eptrb, match_isgroup);
+      RRETURN(rrc);
+      }
+
+    /* The condition is an assertion. Call match() to evaluate it - setting
+    the final argument TRUE causes it to stop at the end of an assertion. */
+
+    else
+      {
+      RMATCH(rrc, eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, NULL,
+          match_condassert | match_isgroup);
+      if (rrc == MATCH_MATCH)
+        {
+        ecode += 1 + LINK_SIZE + GET(ecode, LINK_SIZE+2);
+        while (*ecode == OP_ALT) ecode += GET(ecode, 1);
+        }
+      else if (rrc != MATCH_NOMATCH)
+        {
+        RRETURN(rrc);         /* Need braces because of following else */
+        }
+      else ecode += GET(ecode, 1);
+      RMATCH(rrc, eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, eptrb,
+        match_isgroup);
+      RRETURN(rrc);
+      }
+    /* Control never reaches here */
+
+    /* Skip over conditional reference or large extraction number data if
+    encountered. */
+
+    case OP_CREF:
+    case OP_BRANUMBER:
+    ecode += 3;
+    break;
+
+    /* End of the pattern. If we are in a recursion, we should restore the
+    offsets appropriately and continue from after the call. */
+
+    case OP_END:
+    if (md->recursive != NULL && md->recursive->group_num == 0)
+      {
+      recursion_info *rec = md->recursive;
+      DPRINTF(("Hit the end in a (?0) recursion\n"));
+      md->recursive = rec->prevrec;
+      memmove(md->offset_vector, rec->offset_save,
+        rec->saved_max * sizeof(int));
+      md->start_match = rec->save_start;
+      ims = original_ims;
+      ecode = rec->after_call;
+      break;
+      }
+
+    /* Otherwise, if PCRE_NOTEMPTY is set, fail if we have matched an empty
+    string - backtracking will then try other alternatives, if any. */
+
+    if (md->notempty && eptr == md->start_match) RRETURN(MATCH_NOMATCH);
+    md->end_match_ptr = eptr;          /* Record where we ended */
+    md->end_offset_top = offset_top;   /* and how many extracts were taken */
+    RRETURN(MATCH_MATCH);
+
+    /* Change option settings */
+
+    case OP_OPT:
+    ims = ecode[1];
+    ecode += 2;
+    DPRINTF(("ims set to %02lx\n", ims));
+    break;
+
+    /* Assertion brackets. Check the alternative branches in turn - the
+    matching won't pass the KET for an assertion. If any one branch matches,
+    the assertion is true. Lookbehind assertions have an OP_REVERSE item at the
+    start of each branch to move the current point backwards, so the code at
+    this level is identical to the lookahead case. */
+
+    case OP_ASSERT:
+    case OP_ASSERTBACK:
+    do
+      {
+      RMATCH(rrc, eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, NULL,
+        match_isgroup);
+      if (rrc == MATCH_MATCH) break;
+      if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+      ecode += GET(ecode, 1);
+      }
+    while (*ecode == OP_ALT);
+    if (*ecode == OP_KET) RRETURN(MATCH_NOMATCH);
+
+    /* If checking an assertion for a condition, return MATCH_MATCH. */
+
+    if ((flags & match_condassert) != 0) RRETURN(MATCH_MATCH);
+
+    /* Continue from after the assertion, updating the offsets high water
+    mark, since extracts may have been taken during the assertion. */
+
+    do ecode += GET(ecode,1); while (*ecode == OP_ALT);
+    ecode += 1 + LINK_SIZE;
+    offset_top = md->end_offset_top;
+    continue;
+
+    /* Negative assertion: all branches must fail to match */
+
+    case OP_ASSERT_NOT:
+    case OP_ASSERTBACK_NOT:
+    do
+      {
+      RMATCH(rrc, eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, NULL,
+        match_isgroup);
+      if (rrc == MATCH_MATCH) RRETURN(MATCH_NOMATCH);
+      if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+      ecode += GET(ecode,1);
+      }
+    while (*ecode == OP_ALT);
+
+    if ((flags & match_condassert) != 0) RRETURN(MATCH_MATCH);
+
+    ecode += 1 + LINK_SIZE;
+    continue;
+
+    /* Move the subject pointer back. This occurs only at the start of
+    each branch of a lookbehind assertion. If we are too close to the start to
+    move back, this match function fails. When working with UTF-8 we move
+    back a number of characters, not bytes. */
+
+    case OP_REVERSE:
+#ifdef SUPPORT_UTF8
+    if (md->utf8)
+      {
+      c = GET(ecode,1);
+      for (i = 0; i < c; i++)
+        {
+        eptr--;
+        if (eptr < md->start_subject) RRETURN(MATCH_NOMATCH);
+        BACKCHAR(eptr)
+        }
+      }
+    else
+#endif
+
+    /* No UTF-8 support, or not in UTF-8 mode: count is byte count */
+
+      {
+      eptr -= GET(ecode,1);
+      if (eptr < md->start_subject) RRETURN(MATCH_NOMATCH);
+      }
+
+    /* Skip to next op code */
+
+    ecode += 1 + LINK_SIZE;
+    break;
+
+    /* The callout item calls an external function, if one is provided, passing
+    details of the match so far. This is mainly for debugging, though the
+    function is able to force a failure. */
+
+    case OP_CALLOUT:
+    if (pcre_callout != NULL)
+      {
+      pcre_callout_block cb;
+      cb.version          = 1;   /* Version 1 of the callout block */
+      cb.callout_number   = ecode[1];
+      cb.offset_vector    = md->offset_vector;
+      cb.subject          = (const char *)md->start_subject;
+      cb.subject_length   = md->end_subject - md->start_subject;
+      cb.start_match      = md->start_match - md->start_subject;
+      cb.current_position = eptr - md->start_subject;
+      cb.pattern_position = GET(ecode, 2);
+      cb.next_item_length = GET(ecode, 2 + LINK_SIZE);
+      cb.capture_top      = offset_top/2;
+      cb.capture_last     = md->capture_last;
+      cb.callout_data     = md->callout_data;
+      if ((rrc = (*pcre_callout)(&cb)) > 0) RRETURN(MATCH_NOMATCH);
+      if (rrc < 0) RRETURN(rrc);
+      }
+    ecode += 2 + 2*LINK_SIZE;
+    break;
+
+    /* Recursion either matches the current regex, or some subexpression. The
+    offset data is the offset to the starting bracket from the start of the
+    whole pattern. (This is so that it works from duplicated subpatterns.)
+
+    If there are any capturing brackets started but not finished, we have to
+    save their starting points and reinstate them after the recursion. However,
+    we don't know how many such there are (offset_top records the completed
+    total) so we just have to save all the potential data. There may be up to
+    65535 such values, which is too large to put on the stack, but using malloc
+    for small numbers seems expensive. As a compromise, the stack is used when
+    there are no more than REC_STACK_SAVE_MAX values to store; otherwise malloc
+    is used. A problem is what to do if the malloc fails ... there is no way of
+    returning to the top level with an error. Save the top REC_STACK_SAVE_MAX
+    values on the stack, and accept that the rest may be wrong.
+
+    There are also other values that have to be saved. We use a chained
+    sequence of blocks that actually live on the stack. Thanks to Robin Houston
+    for the original version of this logic. */
+
+    case OP_RECURSE:
+      {
+      callpat = md->start_code + GET(ecode, 1);
+      new_recursive.group_num = *callpat - OP_BRA;
+
+      /* For extended extraction brackets (large number), we have to fish out
+      the number from a dummy opcode at the start. */
+
+      if (new_recursive.group_num > EXTRACT_BASIC_MAX)
+        new_recursive.group_num = GET2(callpat, 2+LINK_SIZE);
+
+      /* Add to "recursing stack" */
+
+      new_recursive.prevrec = md->recursive;
+      md->recursive = &new_recursive;
+
+      /* Find where to continue from afterwards */
+
+      ecode += 1 + LINK_SIZE;
+      new_recursive.after_call = ecode;
+
+      /* Now save the offset data. */
+
+      new_recursive.saved_max = md->offset_end;
+      if (new_recursive.saved_max <= REC_STACK_SAVE_MAX)
+        new_recursive.offset_save = stacksave;
+      else
+        {
+        new_recursive.offset_save =
+          (int *)(pcre_malloc)(new_recursive.saved_max * sizeof(int));
+        if (new_recursive.offset_save == NULL) RRETURN(PCRE_ERROR_NOMEMORY);
+        }
+
+      memcpy(new_recursive.offset_save, md->offset_vector,
+            new_recursive.saved_max * sizeof(int));
+      new_recursive.save_start = md->start_match;
+      md->start_match = eptr;
+
+      /* OK, now we can do the recursion. For each top-level alternative we
+      restore the offset and recursion data. */
+
+      DPRINTF(("Recursing into group %d\n", new_recursive.group_num));
+      do
+        {
+        RMATCH(rrc, eptr, callpat + 1 + LINK_SIZE, offset_top, md, ims,
+            eptrb, match_isgroup);
+        if (rrc == MATCH_MATCH)
+          {
+          md->recursive = new_recursive.prevrec;
+          if (new_recursive.offset_save != stacksave)
+            (pcre_free)(new_recursive.offset_save);
+          RRETURN(MATCH_MATCH);
+          }
+        else if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+
+        md->recursive = &new_recursive;
+        memcpy(md->offset_vector, new_recursive.offset_save,
+            new_recursive.saved_max * sizeof(int));
+        callpat += GET(callpat, 1);
+        }
+      while (*callpat == OP_ALT);
+
+      DPRINTF(("Recursion didn't match\n"));
+      md->recursive = new_recursive.prevrec;
+      if (new_recursive.offset_save != stacksave)
+        (pcre_free)(new_recursive.offset_save);
+      RRETURN(MATCH_NOMATCH);
+      }
+    /* Control never reaches here */
+
+    /* "Once" brackets are like assertion brackets except that after a match,
+    the point in the subject string is not moved back. Thus there can never be
+    a move back into the brackets. Friedl calls these "atomic" subpatterns.
+    Check the alternative branches in turn - the matching won't pass the KET
+    for this kind of subpattern. If any one branch matches, we carry on as at
+    the end of a normal bracket, leaving the subject pointer. */
+
+    case OP_ONCE:
+      {
+      prev = ecode;
+      saved_eptr = eptr;
+
+      do
+        {
+        RMATCH(rrc, eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims,
+          eptrb, match_isgroup);
+        if (rrc == MATCH_MATCH) break;
+        if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+        ecode += GET(ecode,1);
+        }
+      while (*ecode == OP_ALT);
+
+      /* If hit the end of the group (which could be repeated), fail */
+
+      if (*ecode != OP_ONCE && *ecode != OP_ALT) RRETURN(MATCH_NOMATCH);
+
+      /* Continue as from after the assertion, updating the offsets high water
+      mark, since extracts may have been taken. */
+
+      do ecode += GET(ecode,1); while (*ecode == OP_ALT);
+
+      offset_top = md->end_offset_top;
+      eptr = md->end_match_ptr;
+
+      /* For a non-repeating ket, just continue at this level. This also
+      happens for a repeating ket if no characters were matched in the group.
+      This is the forcible breaking of infinite loops as implemented in Perl
+      5.005. If there is an options reset, it will get obeyed in the normal
+      course of events. */
+
+      if (*ecode == OP_KET || eptr == saved_eptr)
+        {
+        ecode += 1+LINK_SIZE;
+        break;
+        }
+
+      /* The repeating kets try the rest of the pattern or restart from the
+      preceding bracket, in the appropriate order. We need to reset any options
+      that changed within the bracket before re-running it, so check the next
+      opcode. */
+
+      if (ecode[1+LINK_SIZE] == OP_OPT)
+        {
+        ims = (ims & ~PCRE_IMS) | ecode[4];
+        DPRINTF(("ims set to %02lx at group repeat\n", ims));
+        }
+
+      if (*ecode == OP_KETRMIN)
+        {
+        RMATCH(rrc, eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, eptrb, 0);
+        if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+        RMATCH(rrc, eptr, prev, offset_top, md, ims, eptrb, match_isgroup);
+        if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+        }
+      else  /* OP_KETRMAX */
+        {
+        RMATCH(rrc, eptr, prev, offset_top, md, ims, eptrb, match_isgroup);
+        if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+        RMATCH(rrc, eptr, ecode + 1+LINK_SIZE, offset_top, md, ims, eptrb, 0);
+        if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+        }
+      }
+    RRETURN(MATCH_NOMATCH);
+
+    /* An alternation is the end of a branch; scan along to find the end of the
+    bracketed group and go to there. */
+
+    case OP_ALT:
+    do ecode += GET(ecode,1); while (*ecode == OP_ALT);
+    break;
+
+    /* BRAZERO and BRAMINZERO occur just before a bracket group, indicating
+    that it may occur zero times. It may repeat infinitely, or not at all -
+    i.e. it could be ()* or ()? in the pattern. Brackets with fixed upper
+    repeat limits are compiled as a number of copies, with the optional ones
+    preceded by BRAZERO or BRAMINZERO. */
+
+    case OP_BRAZERO:
+      {
+      next = ecode+1;
+      RMATCH(rrc, eptr, next, offset_top, md, ims, eptrb, match_isgroup);
+      if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+      do next += GET(next,1); while (*next == OP_ALT);
+      ecode = next + 1+LINK_SIZE;
+      }
+    break;
+
+    case OP_BRAMINZERO:
+      {
+      next = ecode+1;
+      do next += GET(next,1); while (*next == OP_ALT);
+      RMATCH(rrc, eptr, next + 1+LINK_SIZE, offset_top, md, ims, eptrb,
+        match_isgroup);
+      if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+      ecode++;
+      }
+    break;
+
+    /* End of a group, repeated or non-repeating. If we are at the end of
+    an assertion "group", stop matching and return MATCH_MATCH, but record the
+    current high water mark for use by positive assertions. Do this also
+    for the "once" (not-backup up) groups. */
+
+    case OP_KET:
+    case OP_KETRMIN:
+    case OP_KETRMAX:
+      {
+      prev = ecode - GET(ecode, 1);
+      saved_eptr = eptrb->epb_saved_eptr;
+
+      /* Back up the stack of bracket start pointers. */
+
+      eptrb = eptrb->epb_prev;
+
+      if (*prev == OP_ASSERT || *prev == OP_ASSERT_NOT ||
+          *prev == OP_ASSERTBACK || *prev == OP_ASSERTBACK_NOT ||
+          *prev == OP_ONCE)
+        {
+        md->end_match_ptr = eptr;      /* For ONCE */
+        md->end_offset_top = offset_top;
+        RRETURN(MATCH_MATCH);
+        }
+
+      /* In all other cases except a conditional group we have to check the
+      group number back at the start and if necessary complete handling an
+      extraction by setting the offsets and bumping the high water mark. */
+
+      if (*prev != OP_COND)
+        {
+        number = *prev - OP_BRA;
+
+        /* For extended extraction brackets (large number), we have to fish out
+        the number from a dummy opcode at the start. */
+
+        if (number > EXTRACT_BASIC_MAX) number = GET2(prev, 2+LINK_SIZE);
+        offset = number << 1;
+
+#ifdef DEBUG
+        printf("end bracket %d", number);
+        printf("\n");
+#endif
+
+        /* Test for a numbered group. This includes groups called as a result
+        of recursion. Note that whole-pattern recursion is coded as a recurse
+        into group 0, so it won't be picked up here. Instead, we catch it when
+        the OP_END is reached. */
+
+        if (number > 0)
+          {
+          md->capture_last = number;
+          if (offset >= md->offset_max) md->offset_overflow = TRUE; else
+            {
+            md->offset_vector[offset] =
+              md->offset_vector[md->offset_end - number];
+            md->offset_vector[offset+1] = eptr - md->start_subject;
+            if (offset_top <= offset) offset_top = offset + 2;
+            }
+
+          /* Handle a recursively called group. Restore the offsets
+          appropriately and continue from after the call. */
+
+          if (md->recursive != NULL && md->recursive->group_num == number)
+            {
+            recursion_info *rec = md->recursive;
+            DPRINTF(("Recursion (%d) succeeded - continuing\n", number));
+            md->recursive = rec->prevrec;
+            md->start_match = rec->save_start;
+            memcpy(md->offset_vector, rec->offset_save,
+              rec->saved_max * sizeof(int));
+            ecode = rec->after_call;
+            ims = original_ims;
+            break;
+            }
+          }
+        }
+
+      /* Reset the value of the ims flags, in case they got changed during
+      the group. */
+
+      ims = original_ims;
+      DPRINTF(("ims reset to %02lx\n", ims));
+
+      /* For a non-repeating ket, just continue at this level. This also
+      happens for a repeating ket if no characters were matched in the group.
+      This is the forcible breaking of infinite loops as implemented in Perl
+      5.005. If there is an options reset, it will get obeyed in the normal
+      course of events. */
+
+      if (*ecode == OP_KET || eptr == saved_eptr)
+        {
+        ecode += 1 + LINK_SIZE;
+        break;
+        }
+
+      /* The repeating kets try the rest of the pattern or restart from the
+      preceding bracket, in the appropriate order. */
+
+      if (*ecode == OP_KETRMIN)
+        {
+        RMATCH(rrc, eptr, ecode + 1+LINK_SIZE, offset_top, md, ims, eptrb, 0);
+        if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+        RMATCH(rrc, eptr, prev, offset_top, md, ims, eptrb, match_isgroup);
+        if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+        }
+      else  /* OP_KETRMAX */
+        {
+        RMATCH(rrc, eptr, prev, offset_top, md, ims, eptrb, match_isgroup);
+        if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+        RMATCH(rrc, eptr, ecode + 1+LINK_SIZE, offset_top, md, ims, eptrb, 0);
+        if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+        }
+      }
+
+    RRETURN(MATCH_NOMATCH);
+
+    /* Start of subject unless notbol, or after internal newline if multiline */
+
+    case OP_CIRC:
+    if (md->notbol && eptr == md->start_subject) RRETURN(MATCH_NOMATCH);
+    if ((ims & PCRE_MULTILINE) != 0)
+      {
+      if (eptr != md->start_subject && eptr[-1] != NEWLINE)
+        RRETURN(MATCH_NOMATCH);
+      ecode++;
+      break;
+      }
+    /* ... else fall through */
+
+    /* Start of subject assertion */
+
+    case OP_SOD:
+    if (eptr != md->start_subject) RRETURN(MATCH_NOMATCH);
+    ecode++;
+    break;
+
+    /* Start of match assertion */
+
+    case OP_SOM:
+    if (eptr != md->start_subject + md->start_offset) RRETURN(MATCH_NOMATCH);
+    ecode++;
+    break;
+
+    /* Assert before internal newline if multiline, or before a terminating
+    newline unless endonly is set, else end of subject unless noteol is set. */
+
+    case OP_DOLL:
+    if ((ims & PCRE_MULTILINE) != 0)
+      {
+      if (eptr < md->end_subject)
+        { if (*eptr != NEWLINE) RRETURN(MATCH_NOMATCH); }
+      else
+        { if (md->noteol) RRETURN(MATCH_NOMATCH); }
+      ecode++;
+      break;
+      }
+    else
+      {
+      if (md->noteol) RRETURN(MATCH_NOMATCH);
+      if (!md->endonly)
+        {
+        if (eptr < md->end_subject - 1 ||
+           (eptr == md->end_subject - 1 && *eptr != NEWLINE))
+          RRETURN(MATCH_NOMATCH);
+        ecode++;
+        break;
+        }
+      }
+    /* ... else fall through */
+
+    /* End of subject assertion (\z) */
+
+    case OP_EOD:
+    if (eptr < md->end_subject) RRETURN(MATCH_NOMATCH);
+    ecode++;
+    break;
+
+    /* End of subject or ending \n assertion (\Z) */
+
+    case OP_EODN:
+    if (eptr < md->end_subject - 1 ||
+       (eptr == md->end_subject - 1 && *eptr != NEWLINE)) RRETURN(MATCH_NOMATCH);
+    ecode++;
+    break;
+
+    /* Word boundary assertions */
+
+    case OP_NOT_WORD_BOUNDARY:
+    case OP_WORD_BOUNDARY:
+      {
+
+      /* Find out if the previous and current characters are "word" characters.
+      It takes a bit more work in UTF-8 mode. Characters > 255 are assumed to
+      be "non-word" characters. */
+
+#ifdef SUPPORT_UTF8
+      if (md->utf8)
+        {
+        if (eptr == md->start_subject) prev_is_word = FALSE; else
+          {
+          const uschar *lastptr = eptr - 1;
+          while((*lastptr & 0xc0) == 0x80) lastptr--;
+          GETCHAR(c, lastptr);
+          prev_is_word = c < 256 && (md->ctypes[c] & ctype_word) != 0;
+          }
+        if (eptr >= md->end_subject) cur_is_word = FALSE; else
+          {
+          GETCHAR(c, eptr);
+          cur_is_word = c < 256 && (md->ctypes[c] & ctype_word) != 0;
+          }
+        }
+      else
+#endif
+
+      /* More streamlined when not in UTF-8 mode */
+
+        {
+        prev_is_word = (eptr != md->start_subject) &&
+          ((md->ctypes[eptr[-1]] & ctype_word) != 0);
+        cur_is_word = (eptr < md->end_subject) &&
+          ((md->ctypes[*eptr] & ctype_word) != 0);
+        }
+
+      /* Now see if the situation is what we want */
+
+      if ((*ecode++ == OP_WORD_BOUNDARY)?
+           cur_is_word == prev_is_word : cur_is_word != prev_is_word)
+        RRETURN(MATCH_NOMATCH);
+      }
+    break;
+
+    /* Match a single character type; inline for speed */
+
+    case OP_ANY:
+    if ((ims & PCRE_DOTALL) == 0 && eptr < md->end_subject && *eptr == NEWLINE)
+      RRETURN(MATCH_NOMATCH);
+    if (eptr++ >= md->end_subject) RRETURN(MATCH_NOMATCH);
+#ifdef SUPPORT_UTF8
+    if (md->utf8)
+      while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
+#endif
+    ecode++;
+    break;
+
+    /* Match a single byte, even in UTF-8 mode. This opcode really does match
+    any byte, even newline, independent of the setting of PCRE_DOTALL. */
+
+    case OP_ANYBYTE:
+    if (eptr++ >= md->end_subject) RRETURN(MATCH_NOMATCH);
+    ecode++;
+    break;
+
+    case OP_NOT_DIGIT:
+    if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+    GETCHARINCTEST(c, eptr);
+    if (
+#ifdef SUPPORT_UTF8
+       c < 256 &&
+#endif
+       (md->ctypes[c] & ctype_digit) != 0
+       )
+      RRETURN(MATCH_NOMATCH);
+    ecode++;
+    break;
+
+    case OP_DIGIT:
+    if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+    GETCHARINCTEST(c, eptr);
+    if (
+#ifdef SUPPORT_UTF8
+       c >= 256 ||
+#endif
+       (md->ctypes[c] & ctype_digit) == 0
+       )
+      RRETURN(MATCH_NOMATCH);
+    ecode++;
+    break;
+
+    case OP_NOT_WHITESPACE:
+    if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+    GETCHARINCTEST(c, eptr);
+    if (
+#ifdef SUPPORT_UTF8
+       c < 256 &&
+#endif
+       (md->ctypes[c] & ctype_space) != 0
+       )
+      RRETURN(MATCH_NOMATCH);
+    ecode++;
+    break;
+
+    case OP_WHITESPACE:
+    if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+    GETCHARINCTEST(c, eptr);
+    if (
+#ifdef SUPPORT_UTF8
+       c >= 256 ||
+#endif
+       (md->ctypes[c] & ctype_space) == 0
+       )
+      RRETURN(MATCH_NOMATCH);
+    ecode++;
+    break;
+
+    case OP_NOT_WORDCHAR:
+    if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+    GETCHARINCTEST(c, eptr);
+    if (
+#ifdef SUPPORT_UTF8
+       c < 256 &&
+#endif
+       (md->ctypes[c] & ctype_word) != 0
+       )
+      RRETURN(MATCH_NOMATCH);
+    ecode++;
+    break;
+
+    case OP_WORDCHAR:
+    if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+    GETCHARINCTEST(c, eptr);
+    if (
+#ifdef SUPPORT_UTF8
+       c >= 256 ||
+#endif
+       (md->ctypes[c] & ctype_word) == 0
+       )
+      RRETURN(MATCH_NOMATCH);
+    ecode++;
+    break;
+
+#ifdef SUPPORT_UCP
+    /* Check the next character by Unicode property. We will get here only
+    if the support is in the binary; otherwise a compile-time error occurs. */
+
+    case OP_PROP:
+    case OP_NOTPROP:
+    if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+    GETCHARINCTEST(c, eptr);
+      {
+      int chartype, rqdtype;
+      int othercase;
+      int category = ucp_findchar(c, &chartype, &othercase);
+
+      rqdtype = *(++ecode);
+      ecode++;
+
+      if (rqdtype >= 128)
+        {
+        if ((rqdtype - 128 != category) == (op == OP_PROP))
+          RRETURN(MATCH_NOMATCH);
+        }
+      else
+        {
+        if ((rqdtype != chartype) == (op == OP_PROP))
+          RRETURN(MATCH_NOMATCH);
+        }
+      }
+    break;
+
+    /* Match an extended Unicode sequence. We will get here only if the support
+    is in the binary; otherwise a compile-time error occurs. */
+
+    case OP_EXTUNI:
+    if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+    GETCHARINCTEST(c, eptr);
+      {
+      int chartype;
+      int othercase;
+      int category = ucp_findchar(c, &chartype, &othercase);
+      if (category == ucp_M) RRETURN(MATCH_NOMATCH);
+      while (eptr < md->end_subject)
+        {
+        int len = 1;
+        if (!md->utf8) c = *eptr; else
+          {
+          GETCHARLEN(c, eptr, len);
+          }
+        category = ucp_findchar(c, &chartype, &othercase);
+        if (category != ucp_M) break;
+        eptr += len;
+        }
+      }
+    ecode++;
+    break;
+#endif
+
+
+    /* Match a back reference, possibly repeatedly. Look past the end of the
+    item to see if there is repeat information following. The code is similar
+    to that for character classes, but repeated for efficiency. Then obey
+    similar code to character type repeats - written out again for speed.
+    However, if the referenced string is the empty string, always treat
+    it as matched, any number of times (otherwise there could be infinite
+    loops). */
+
+    case OP_REF:
+      {
+      offset = GET2(ecode, 1) << 1;               /* Doubled ref number */
+      ecode += 3;                                 /* Advance past item */
+
+      /* If the reference is unset, set the length to be longer than the amount
+      of subject left; this ensures that every attempt at a match fails. We
+      can't just fail here, because of the possibility of quantifiers with zero
+      minima. */
+
+      length = (offset >= offset_top || md->offset_vector[offset] < 0)?
+        md->end_subject - eptr + 1 :
+        md->offset_vector[offset+1] - md->offset_vector[offset];
+
+      /* Set up for repetition, or handle the non-repeated case */
+
+      switch (*ecode)
+        {
+        case OP_CRSTAR:
+        case OP_CRMINSTAR:
+        case OP_CRPLUS:
+        case OP_CRMINPLUS:
+        case OP_CRQUERY:
+        case OP_CRMINQUERY:
+        c = *ecode++ - OP_CRSTAR;
+        minimize = (c & 1) != 0;
+        min = rep_min[c];                 /* Pick up values from tables; */
+        max = rep_max[c];                 /* zero for max => infinity */
+        if (max == 0) max = INT_MAX;
+        break;
+
+        case OP_CRRANGE:
+        case OP_CRMINRANGE:
+        minimize = (*ecode == OP_CRMINRANGE);
+        min = GET2(ecode, 1);
+        max = GET2(ecode, 3);
+        if (max == 0) max = INT_MAX;
+        ecode += 5;
+        break;
+
+        default:               /* No repeat follows */
+        if (!match_ref(offset, eptr, length, md, ims)) RRETURN(MATCH_NOMATCH);
+        eptr += length;
+        continue;              /* With the main loop */
+        }
+
+      /* If the length of the reference is zero, just continue with the
+      main loop. */
+
+      if (length == 0) continue;
+
+      /* First, ensure the minimum number of matches are present. We get back
+      the length of the reference string explicitly rather than passing the
+      address of eptr, so that eptr can be a register variable. */
+
+      for (i = 1; i <= min; i++)
+        {
+        if (!match_ref(offset, eptr, length, md, ims)) RRETURN(MATCH_NOMATCH);
+        eptr += length;
+        }
+
+      /* If min = max, continue at the same level without recursion.
+      They are not both allowed to be zero. */
+
+      if (min == max) continue;
+
+      /* If minimizing, keep trying and advancing the pointer */
+
+      if (minimize)
+        {
+        for (fi = min;; fi++)
+          {
+          RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+          if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+          if (fi >= max || !match_ref(offset, eptr, length, md, ims))
+            RRETURN(MATCH_NOMATCH);
+          eptr += length;
+          }
+        /* Control never gets here */
+        }
+
+      /* If maximizing, find the longest string and work backwards */
+
+      else
+        {
+        pp = eptr;
+        for (i = min; i < max; i++)
+          {
+          if (!match_ref(offset, eptr, length, md, ims)) break;
+          eptr += length;
+          }
+        while (eptr >= pp)
+          {
+          RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+          if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+          eptr -= length;
+          }
+        RRETURN(MATCH_NOMATCH);
+        }
+      }
+    /* Control never gets here */
+
+
+
+    /* Match a bit-mapped character class, possibly repeatedly. This op code is
+    used when all the characters in the class have values in the range 0-255,
+    and either the matching is caseful, or the characters are in the range
+    0-127 when UTF-8 processing is enabled. The only difference between
+    OP_CLASS and OP_NCLASS occurs when a data character outside the range is
+    encountered.
+
+    First, look past the end of the item to see if there is repeat information
+    following. Then obey similar code to character type repeats - written out
+    again for speed. */
+
+    case OP_NCLASS:
+    case OP_CLASS:
+      {
+      data = ecode + 1;                /* Save for matching */
+      ecode += 33;                     /* Advance past the item */
+
+      switch (*ecode)
+        {
+        case OP_CRSTAR:
+        case OP_CRMINSTAR:
+        case OP_CRPLUS:
+        case OP_CRMINPLUS:
+        case OP_CRQUERY:
+        case OP_CRMINQUERY:
+        c = *ecode++ - OP_CRSTAR;
+        minimize = (c & 1) != 0;
+        min = rep_min[c];                 /* Pick up values from tables; */
+        max = rep_max[c];                 /* zero for max => infinity */
+        if (max == 0) max = INT_MAX;
+        break;
+
+        case OP_CRRANGE:
+        case OP_CRMINRANGE:
+        minimize = (*ecode == OP_CRMINRANGE);
+        min = GET2(ecode, 1);
+        max = GET2(ecode, 3);
+        if (max == 0) max = INT_MAX;
+        ecode += 5;
+        break;
+
+        default:               /* No repeat follows */
+        min = max = 1;
+        break;
+        }
+
+      /* First, ensure the minimum number of matches are present. */
+
+#ifdef SUPPORT_UTF8
+      /* UTF-8 mode */
+      if (md->utf8)
+        {
+        for (i = 1; i <= min; i++)
+          {
+          if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+          GETCHARINC(c, eptr);
+          if (c > 255)
+            {
+            if (op == OP_CLASS) RRETURN(MATCH_NOMATCH);
+            }
+          else
+            {
+            if ((data[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH);
+            }
+          }
+        }
+      else
+#endif
+      /* Not UTF-8 mode */
+        {
+        for (i = 1; i <= min; i++)
+          {
+          if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+          c = *eptr++;
+          if ((data[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH);
+          }
+        }
+
+      /* If max == min we can continue with the main loop without the
+      need to recurse. */
+
+      if (min == max) continue;
+
+      /* If minimizing, keep testing the rest of the expression and advancing
+      the pointer while it matches the class. */
+
+      if (minimize)
+        {
+#ifdef SUPPORT_UTF8
+        /* UTF-8 mode */
+        if (md->utf8)
+          {
+          for (fi = min;; fi++)
+            {
+            RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+            if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+            if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+            GETCHARINC(c, eptr);
+            if (c > 255)
+              {
+              if (op == OP_CLASS) RRETURN(MATCH_NOMATCH);
+              }
+            else
+              {
+              if ((data[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH);
+              }
+            }
+          }
+        else
+#endif
+        /* Not UTF-8 mode */
+          {
+          for (fi = min;; fi++)
+            {
+            RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+            if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+            if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+            c = *eptr++;
+            if ((data[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH);
+            }
+          }
+        /* Control never gets here */
+        }
+
+      /* If maximizing, find the longest possible run, then work backwards. */
+
+      else
+        {
+        pp = eptr;
+
+#ifdef SUPPORT_UTF8
+        /* UTF-8 mode */
+        if (md->utf8)
+          {
+          for (i = min; i < max; i++)
+            {
+            int len = 1;
+            if (eptr >= md->end_subject) break;
+            GETCHARLEN(c, eptr, len);
+            if (c > 255)
+              {
+              if (op == OP_CLASS) break;
+              }
+            else
+              {
+              if ((data[c/8] & (1 << (c&7))) == 0) break;
+              }
+            eptr += len;
+            }
+          for (;;)
+            {
+            RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+            if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+            if (eptr-- == pp) break;        /* Stop if tried at original pos */
+            BACKCHAR(eptr);
+            }
+          }
+        else
+#endif
+          /* Not UTF-8 mode */
+          {
+          for (i = min; i < max; i++)
+            {
+            if (eptr >= md->end_subject) break;
+            c = *eptr;
+            if ((data[c/8] & (1 << (c&7))) == 0) break;
+            eptr++;
+            }
+          while (eptr >= pp)
+            {
+            RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+            eptr--;
+            if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+            }
+          }
+
+        RRETURN(MATCH_NOMATCH);
+        }
+      }
+    /* Control never gets here */
+
+
+    /* Match an extended character class. This opcode is encountered only
+    in UTF-8 mode, because that's the only time it is compiled. */
+
+#ifdef SUPPORT_UTF8
+    case OP_XCLASS:
+      {
+      data = ecode + 1 + LINK_SIZE;                /* Save for matching */
+      ecode += GET(ecode, 1);                      /* Advance past the item */
+
+      switch (*ecode)
+        {
+        case OP_CRSTAR:
+        case OP_CRMINSTAR:
+        case OP_CRPLUS:
+        case OP_CRMINPLUS:
+        case OP_CRQUERY:
+        case OP_CRMINQUERY:
+        c = *ecode++ - OP_CRSTAR;
+        minimize = (c & 1) != 0;
+        min = rep_min[c];                 /* Pick up values from tables; */
+        max = rep_max[c];                 /* zero for max => infinity */
+        if (max == 0) max = INT_MAX;
+        break;
+
+        case OP_CRRANGE:
+        case OP_CRMINRANGE:
+        minimize = (*ecode == OP_CRMINRANGE);
+        min = GET2(ecode, 1);
+        max = GET2(ecode, 3);
+        if (max == 0) max = INT_MAX;
+        ecode += 5;
+        break;
+
+        default:               /* No repeat follows */
+        min = max = 1;
+        break;
+        }
+
+      /* First, ensure the minimum number of matches are present. */
+
+      for (i = 1; i <= min; i++)
+        {
+        if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+        GETCHARINC(c, eptr);
+        if (!match_xclass(c, data)) RRETURN(MATCH_NOMATCH);
+        }
+
+      /* If max == min we can continue with the main loop without the
+      need to recurse. */
+
+      if (min == max) continue;
+
+      /* If minimizing, keep testing the rest of the expression and advancing
+      the pointer while it matches the class. */
+
+      if (minimize)
+        {
+        for (fi = min;; fi++)
+          {
+          RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+          if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+          if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+          GETCHARINC(c, eptr);
+          if (!match_xclass(c, data)) RRETURN(MATCH_NOMATCH);
+          }
+        /* Control never gets here */
+        }
+
+      /* If maximizing, find the longest possible run, then work backwards. */
+
+      else
+        {
+        pp = eptr;
+        for (i = min; i < max; i++)
+          {
+          int len = 1;
+          if (eptr >= md->end_subject) break;
+          GETCHARLEN(c, eptr, len);
+          if (!match_xclass(c, data)) break;
+          eptr += len;
+          }
+        for(;;)
+          {
+          RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+          if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+          if (eptr-- == pp) break;        /* Stop if tried at original pos */
+          BACKCHAR(eptr)
+          }
+        RRETURN(MATCH_NOMATCH);
+        }
+
+      /* Control never gets here */
+      }
+#endif    /* End of XCLASS */
+
+    /* Match a single character, casefully */
+
+    case OP_CHAR:
+#ifdef SUPPORT_UTF8
+    if (md->utf8)
+      {
+      length = 1;
+      ecode++;
+      GETCHARLEN(fc, ecode, length);
+      if (length > md->end_subject - eptr) RRETURN(MATCH_NOMATCH);
+      while (length-- > 0) if (*ecode++ != *eptr++) RRETURN(MATCH_NOMATCH);
+      }
+    else
+#endif
+
+    /* Non-UTF-8 mode */
+      {
+      if (md->end_subject - eptr < 1) RRETURN(MATCH_NOMATCH);
+      if (ecode[1] != *eptr++) RRETURN(MATCH_NOMATCH);
+      ecode += 2;
+      }
+    break;
+
+    /* Match a single character, caselessly */
+
+    case OP_CHARNC:
+#ifdef SUPPORT_UTF8
+    if (md->utf8)
+      {
+      length = 1;
+      ecode++;
+      GETCHARLEN(fc, ecode, length);
+
+      if (length > md->end_subject - eptr) RRETURN(MATCH_NOMATCH);
+
+      /* If the pattern character's value is < 128, we have only one byte, and
+      can use the fast lookup table. */
+
+      if (fc < 128)
+        {
+        if (md->lcc[*ecode++] != md->lcc[*eptr++]) RRETURN(MATCH_NOMATCH);
+        }
+
+      /* Otherwise we must pick up the subject character */
+
+      else
+        {
+        int dc;
+        GETCHARINC(dc, eptr);
+        ecode += length;
+
+        /* If we have Unicode property support, we can use it to test the other
+        case of the character, if there is one. The result of ucp_findchar() is
+        < 0 if the char isn't found, and othercase is returned as zero if there
+        isn't one. */
+
+        if (fc != dc)
+          {
+#ifdef SUPPORT_UCP
+          int chartype;
+          int othercase;
+          if (ucp_findchar(fc, &chartype, &othercase) < 0 || dc != othercase)
+#endif
+            RRETURN(MATCH_NOMATCH);
+          }
+        }
+      }
+    else
+#endif   /* SUPPORT_UTF8 */
+
+    /* Non-UTF-8 mode */
+      {
+      if (md->end_subject - eptr < 1) RRETURN(MATCH_NOMATCH);
+      if (md->lcc[ecode[1]] != md->lcc[*eptr++]) RRETURN(MATCH_NOMATCH);
+      ecode += 2;
+      }
+    break;
+
+    /* Match a single character repeatedly; different opcodes share code. */
+
+    case OP_EXACT:
+    min = max = GET2(ecode, 1);
+    ecode += 3;
+    goto REPEATCHAR;
+
+    case OP_UPTO:
+    case OP_MINUPTO:
+    min = 0;
+    max = GET2(ecode, 1);
+    minimize = *ecode == OP_MINUPTO;
+    ecode += 3;
+    goto REPEATCHAR;
+
+    case OP_STAR:
+    case OP_MINSTAR:
+    case OP_PLUS:
+    case OP_MINPLUS:
+    case OP_QUERY:
+    case OP_MINQUERY:
+    c = *ecode++ - OP_STAR;
+    minimize = (c & 1) != 0;
+    min = rep_min[c];                 /* Pick up values from tables; */
+    max = rep_max[c];                 /* zero for max => infinity */
+    if (max == 0) max = INT_MAX;
+
+    /* Common code for all repeated single-character matches. We can give
+    up quickly if there are fewer than the minimum number of characters left in
+    the subject. */
+
+    REPEATCHAR:
+#ifdef SUPPORT_UTF8
+    if (md->utf8)
+      {
+      length = 1;
+      charptr = ecode;
+      GETCHARLEN(fc, ecode, length);
+      if (min * length > md->end_subject - eptr) RRETURN(MATCH_NOMATCH);
+      ecode += length;
+
+      /* Handle multibyte character matching specially here. There is
+      support for caseless matching if UCP support is present. */
+
+      if (length > 1)
+        {
+        int oclength = 0;
+        uschar occhars[8];
+
+#ifdef SUPPORT_UCP
+        int othercase;
+        int chartype;
+        if ((ims & PCRE_CASELESS) != 0 &&
+             ucp_findchar(fc, &chartype, &othercase) >= 0 &&
+             othercase > 0)
+          oclength = ord2utf8(othercase, occhars);
+#endif  /* SUPPORT_UCP */
+
+        for (i = 1; i <= min; i++)
+          {
+          if (memcmp(eptr, charptr, length) == 0) eptr += length;
+          /* Need braces because of following else */
+          else if (oclength == 0) { RRETURN(MATCH_NOMATCH); }
+          else
+            {
+            if (memcmp(eptr, occhars, oclength) != 0) RRETURN(MATCH_NOMATCH);
+            eptr += oclength;
+            }
+          }
+
+        if (min == max) continue;
+
+        if (minimize)
+          {
+          for (fi = min;; fi++)
+            {
+            RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+            if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+            if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+            if (memcmp(eptr, charptr, length) == 0) eptr += length;
+            /* Need braces because of following else */
+            else if (oclength == 0) { RRETURN(MATCH_NOMATCH); }
+            else
+              {
+              if (memcmp(eptr, occhars, oclength) != 0) RRETURN(MATCH_NOMATCH);
+              eptr += oclength;
+              }
+            }
+          /* Control never gets here */
+          }
+        else
+          {
+          pp = eptr;
+          for (i = min; i < max; i++)
+            {
+            if (eptr > md->end_subject - length) break;
+            if (memcmp(eptr, charptr, length) == 0) eptr += length;
+            else if (oclength == 0) break;
+            else
+              {
+              if (memcmp(eptr, occhars, oclength) != 0) break;
+              eptr += oclength;
+              }
+            }
+          while (eptr >= pp)
+           {
+           RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+           if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+           eptr -= length;
+           }
+          RRETURN(MATCH_NOMATCH);
+          }
+        /* Control never gets here */
+        }
+
+      /* If the length of a UTF-8 character is 1, we fall through here, and
+      obey the code as for non-UTF-8 characters below, though in this case the
+      value of fc will always be < 128. */
+      }
+    else
+#endif  /* SUPPORT_UTF8 */
+
+    /* When not in UTF-8 mode, load a single-byte character. */
+      {
+      if (min > md->end_subject - eptr) RRETURN(MATCH_NOMATCH);
+      fc = *ecode++;
+      }
+
+    /* The value of fc at this point is always less than 256, though we may or
+    may not be in UTF-8 mode. The code is duplicated for the caseless and
+    caseful cases, for speed, since matching characters is likely to be quite
+    common. First, ensure the minimum number of matches are present. If min =
+    max, continue at the same level without recursing. Otherwise, if
+    minimizing, keep trying the rest of the expression and advancing one
+    matching character if failing, up to the maximum. Alternatively, if
+    maximizing, find the maximum number of characters and work backwards. */
+
+    DPRINTF(("matching %c{%d,%d} against subject %.*s\n", fc, min, max,
+      max, eptr));
+
+    if ((ims & PCRE_CASELESS) != 0)
+      {
+      fc = md->lcc[fc];
+      for (i = 1; i <= min; i++)
+        if (fc != md->lcc[*eptr++]) RRETURN(MATCH_NOMATCH);
+      if (min == max) continue;
+      if (minimize)
+        {
+        for (fi = min;; fi++)
+          {
+          RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+          if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+          if (fi >= max || eptr >= md->end_subject ||
+              fc != md->lcc[*eptr++])
+            RRETURN(MATCH_NOMATCH);
+          }
+        /* Control never gets here */
+        }
+      else
+        {
+        pp = eptr;
+        for (i = min; i < max; i++)
+          {
+          if (eptr >= md->end_subject || fc != md->lcc[*eptr]) break;
+          eptr++;
+          }
+        while (eptr >= pp)
+          {
+          RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+          eptr--;
+          if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+          }
+        RRETURN(MATCH_NOMATCH);
+        }
+      /* Control never gets here */
+      }
+
+    /* Caseful comparisons (includes all multi-byte characters) */
+
+    else
+      {
+      for (i = 1; i <= min; i++) if (fc != *eptr++) RRETURN(MATCH_NOMATCH);
+      if (min == max) continue;
+      if (minimize)
+        {
+        for (fi = min;; fi++)
+          {
+          RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+          if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+          if (fi >= max || eptr >= md->end_subject || fc != *eptr++)
+            RRETURN(MATCH_NOMATCH);
+          }
+        /* Control never gets here */
+        }
+      else
+        {
+        pp = eptr;
+        for (i = min; i < max; i++)
+          {
+          if (eptr >= md->end_subject || fc != *eptr) break;
+          eptr++;
+          }
+        while (eptr >= pp)
+          {
+          RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+          eptr--;
+          if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+          }
+        RRETURN(MATCH_NOMATCH);
+        }
+      }
+    /* Control never gets here */
+
+    /* Match a negated single one-byte character. The character we are
+    checking can be multibyte. */
+
+    case OP_NOT:
+    if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+    ecode++;
+    GETCHARINCTEST(c, eptr);
+    if ((ims & PCRE_CASELESS) != 0)
+      {
+#ifdef SUPPORT_UTF8
+      if (c < 256)
+#endif
+      c = md->lcc[c];
+      if (md->lcc[*ecode++] == c) RRETURN(MATCH_NOMATCH);
+      }
+    else
+      {
+      if (*ecode++ == c) RRETURN(MATCH_NOMATCH);
+      }
+    break;
+
+    /* Match a negated single one-byte character repeatedly. This is almost a
+    repeat of the code for a repeated single character, but I haven't found a
+    nice way of commoning these up that doesn't require a test of the
+    positive/negative option for each character match. Maybe that wouldn't add
+    very much to the time taken, but character matching *is* what this is all
+    about... */
+
+    case OP_NOTEXACT:
+    min = max = GET2(ecode, 1);
+    ecode += 3;
+    goto REPEATNOTCHAR;
+
+    case OP_NOTUPTO:
+    case OP_NOTMINUPTO:
+    min = 0;
+    max = GET2(ecode, 1);
+    minimize = *ecode == OP_NOTMINUPTO;
+    ecode += 3;
+    goto REPEATNOTCHAR;
+
+    case OP_NOTSTAR:
+    case OP_NOTMINSTAR:
+    case OP_NOTPLUS:
+    case OP_NOTMINPLUS:
+    case OP_NOTQUERY:
+    case OP_NOTMINQUERY:
+    c = *ecode++ - OP_NOTSTAR;
+    minimize = (c & 1) != 0;
+    min = rep_min[c];                 /* Pick up values from tables; */
+    max = rep_max[c];                 /* zero for max => infinity */
+    if (max == 0) max = INT_MAX;
+
+    /* Common code for all repeated single-byte matches. We can give up quickly
+    if there are fewer than the minimum number of bytes left in the
+    subject. */
+
+    REPEATNOTCHAR:
+    if (min > md->end_subject - eptr) RRETURN(MATCH_NOMATCH);
+    fc = *ecode++;
+
+    /* The code is duplicated for the caseless and caseful cases, for speed,
+    since matching characters is likely to be quite common. First, ensure the
+    minimum number of matches are present. If min = max, continue at the same
+    level without recursing. Otherwise, if minimizing, keep trying the rest of
+    the expression and advancing one matching character if failing, up to the
+    maximum. Alternatively, if maximizing, find the maximum number of
+    characters and work backwards. */
+
+    DPRINTF(("negative matching %c{%d,%d} against subject %.*s\n", fc, min, max,
+      max, eptr));
+
+    if ((ims & PCRE_CASELESS) != 0)
+      {
+      fc = md->lcc[fc];
+
+#ifdef SUPPORT_UTF8
+      /* UTF-8 mode */
+      if (md->utf8)
+        {
+        register int d;
+        for (i = 1; i <= min; i++)
+          {
+          GETCHARINC(d, eptr);
+          if (d < 256) d = md->lcc[d];
+          if (fc == d) RRETURN(MATCH_NOMATCH);
+          }
+        }
+      else
+#endif
+
+      /* Not UTF-8 mode */
+        {
+        for (i = 1; i <= min; i++)
+          if (fc == md->lcc[*eptr++]) RRETURN(MATCH_NOMATCH);
+        }
+
+      if (min == max) continue;
+
+      if (minimize)
+        {
+#ifdef SUPPORT_UTF8
+        /* UTF-8 mode */
+        if (md->utf8)
+          {
+          register int d;
+          for (fi = min;; fi++)
+            {
+            RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+            if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+            GETCHARINC(d, eptr);
+            if (d < 256) d = md->lcc[d];
+            if (fi >= max || eptr >= md->end_subject || fc == d)
+              RRETURN(MATCH_NOMATCH);
+            }
+          }
+        else
+#endif
+        /* Not UTF-8 mode */
+          {
+          for (fi = min;; fi++)
+            {
+            RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+            if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+            if (fi >= max || eptr >= md->end_subject || fc == md->lcc[*eptr++])
+              RRETURN(MATCH_NOMATCH);
+            }
+          }
+        /* Control never gets here */
+        }
+
+      /* Maximize case */
+
+      else
+        {
+        pp = eptr;
+
+#ifdef SUPPORT_UTF8
+        /* UTF-8 mode */
+        if (md->utf8)
+          {
+          register int d;
+          for (i = min; i < max; i++)
+            {
+            int len = 1;
+            if (eptr >= md->end_subject) break;
+            GETCHARLEN(d, eptr, len);
+            if (d < 256) d = md->lcc[d];
+            if (fc == d) break;
+            eptr += len;
+            }
+          for(;;)
+            {
+            RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+            if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+            if (eptr-- == pp) break;        /* Stop if tried at original pos */
+            BACKCHAR(eptr);
+            }
+          }
+        else
+#endif
+        /* Not UTF-8 mode */
+          {
+          for (i = min; i < max; i++)
+            {
+            if (eptr >= md->end_subject || fc == md->lcc[*eptr]) break;
+            eptr++;
+            }
+          while (eptr >= pp)
+            {
+            RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+            if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+            eptr--;
+            }
+          }
+
+        RRETURN(MATCH_NOMATCH);
+        }
+      /* Control never gets here */
+      }
+
+    /* Caseful comparisons */
+
+    else
+      {
+#ifdef SUPPORT_UTF8
+      /* UTF-8 mode */
+      if (md->utf8)
+        {
+        register int d;
+        for (i = 1; i <= min; i++)
+          {
+          GETCHARINC(d, eptr);
+          if (fc == d) RRETURN(MATCH_NOMATCH);
+          }
+        }
+      else
+#endif
+      /* Not UTF-8 mode */
+        {
+        for (i = 1; i <= min; i++)
+          if (fc == *eptr++) RRETURN(MATCH_NOMATCH);
+        }
+
+      if (min == max) continue;
+
+      if (minimize)
+        {
+#ifdef SUPPORT_UTF8
+        /* UTF-8 mode */
+        if (md->utf8)
+          {
+          register int d;
+          for (fi = min;; fi++)
+            {
+            RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+            if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+            GETCHARINC(d, eptr);
+            if (fi >= max || eptr >= md->end_subject || fc == d)
+              RRETURN(MATCH_NOMATCH);
+            }
+          }
+        else
+#endif
+        /* Not UTF-8 mode */
+          {
+          for (fi = min;; fi++)
+            {
+            RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+            if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+            if (fi >= max || eptr >= md->end_subject || fc == *eptr++)
+              RRETURN(MATCH_NOMATCH);
+            }
+          }
+        /* Control never gets here */
+        }
+
+      /* Maximize case */
+
+      else
+        {
+        pp = eptr;
+
+#ifdef SUPPORT_UTF8
+        /* UTF-8 mode */
+        if (md->utf8)
+          {
+          register int d;
+          for (i = min; i < max; i++)
+            {
+            int len = 1;
+            if (eptr >= md->end_subject) break;
+            GETCHARLEN(d, eptr, len);
+            if (fc == d) break;
+            eptr += len;
+            }
+          for(;;)
+            {
+            RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+            if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+            if (eptr-- == pp) break;        /* Stop if tried at original pos */
+            BACKCHAR(eptr);
+            }
+          }
+        else
+#endif
+        /* Not UTF-8 mode */
+          {
+          for (i = min; i < max; i++)
+            {
+            if (eptr >= md->end_subject || fc == *eptr) break;
+            eptr++;
+            }
+          while (eptr >= pp)
+            {
+            RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+            if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+            eptr--;
+            }
+          }
+
+        RRETURN(MATCH_NOMATCH);
+        }
+      }
+    /* Control never gets here */
+
+    /* Match a single character type repeatedly; several different opcodes
+    share code. This is very similar to the code for single characters, but we
+    repeat it in the interests of efficiency. */
+
+    case OP_TYPEEXACT:
+    min = max = GET2(ecode, 1);
+    minimize = TRUE;
+    ecode += 3;
+    goto REPEATTYPE;
+
+    case OP_TYPEUPTO:
+    case OP_TYPEMINUPTO:
+    min = 0;
+    max = GET2(ecode, 1);
+    minimize = *ecode == OP_TYPEMINUPTO;
+    ecode += 3;
+    goto REPEATTYPE;
+
+    case OP_TYPESTAR:
+    case OP_TYPEMINSTAR:
+    case OP_TYPEPLUS:
+    case OP_TYPEMINPLUS:
+    case OP_TYPEQUERY:
+    case OP_TYPEMINQUERY:
+    c = *ecode++ - OP_TYPESTAR;
+    minimize = (c & 1) != 0;
+    min = rep_min[c];                 /* Pick up values from tables; */
+    max = rep_max[c];                 /* zero for max => infinity */
+    if (max == 0) max = INT_MAX;
+
+    /* Common code for all repeated single character type matches. Note that
+    in UTF-8 mode, '.' matches a character of any length, but for the other
+    character types, the valid characters are all one-byte long. */
+
+    REPEATTYPE:
+    ctype = *ecode++;      /* Code for the character type */
+
+#ifdef SUPPORT_UCP
+    if (ctype == OP_PROP || ctype == OP_NOTPROP)
+      {
+      prop_fail_result = ctype == OP_NOTPROP;
+      prop_type = *ecode++;
+      if (prop_type >= 128)
+        {
+        prop_test_against = prop_type - 128;
+        prop_test_variable = &prop_category;
+        }
+      else
+        {
+        prop_test_against = prop_type;
+        prop_test_variable = &prop_chartype;
+        }
+      }
+    else prop_type = -1;
+#endif
+
+    /* First, ensure the minimum number of matches are present. Use inline
+    code for maximizing the speed, and do the type test once at the start
+    (i.e. keep it out of the loop). Also we can test that there are at least
+    the minimum number of bytes before we start. This isn't as effective in
+    UTF-8 mode, but it does no harm. Separate the UTF-8 code completely as that
+    is tidier. Also separate the UCP code, which can be the same for both UTF-8
+    and single-bytes. */
+
+    if (min > md->end_subject - eptr) RRETURN(MATCH_NOMATCH);
+    if (min > 0)
+      {
+#ifdef SUPPORT_UCP
+      if (prop_type > 0)
+        {
+        for (i = 1; i <= min; i++)
+          {
+          GETCHARINC(c, eptr);
+          prop_category = ucp_findchar(c, &prop_chartype, &prop_othercase);
+          if ((*prop_test_variable == prop_test_against) == prop_fail_result)
+            RRETURN(MATCH_NOMATCH);
+          }
+        }
+
+      /* Match extended Unicode sequences. We will get here only if the
+      support is in the binary; otherwise a compile-time error occurs. */
+
+      else if (ctype == OP_EXTUNI)
+        {
+        for (i = 1; i <= min; i++)
+          {
+          GETCHARINCTEST(c, eptr);
+          prop_category = ucp_findchar(c, &prop_chartype, &prop_othercase);
+          if (prop_category == ucp_M) RRETURN(MATCH_NOMATCH);
+          while (eptr < md->end_subject)
+            {
+            int len = 1;
+            if (!md->utf8) c = *eptr; else
+              {
+              GETCHARLEN(c, eptr, len);
+              }
+            prop_category = ucp_findchar(c, &prop_chartype, &prop_othercase);
+            if (prop_category != ucp_M) break;
+            eptr += len;
+            }
+          }
+        }
+
+      else
+#endif     /* SUPPORT_UCP */
+
+/* Handle all other cases when the coding is UTF-8 */
+
+#ifdef SUPPORT_UTF8
+      if (md->utf8) switch(ctype)
+        {
+        case OP_ANY:
+        for (i = 1; i <= min; i++)
+          {
+          if (eptr >= md->end_subject ||
+             (*eptr++ == NEWLINE && (ims & PCRE_DOTALL) == 0))
+            RRETURN(MATCH_NOMATCH);
+          while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
+          }
+        break;
+
+        case OP_ANYBYTE:
+        eptr += min;
+        break;
+
+        case OP_NOT_DIGIT:
+        for (i = 1; i <= min; i++)
+          {
+          if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+          GETCHARINC(c, eptr);
+          if (c < 128 && (md->ctypes[c] & ctype_digit) != 0)
+            RRETURN(MATCH_NOMATCH);
+          }
+        break;
+
+        case OP_DIGIT:
+        for (i = 1; i <= min; i++)
+          {
+          if (eptr >= md->end_subject ||
+             *eptr >= 128 || (md->ctypes[*eptr++] & ctype_digit) == 0)
+            RRETURN(MATCH_NOMATCH);
+          /* No need to skip more bytes - we know it's a 1-byte character */
+          }
+        break;
+
+        case OP_NOT_WHITESPACE:
+        for (i = 1; i <= min; i++)
+          {
+          if (eptr >= md->end_subject ||
+             (*eptr < 128 && (md->ctypes[*eptr++] & ctype_space) != 0))
+            RRETURN(MATCH_NOMATCH);
+          while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
+          }
+        break;
+
+        case OP_WHITESPACE:
+        for (i = 1; i <= min; i++)
+          {
+          if (eptr >= md->end_subject ||
+             *eptr >= 128 || (md->ctypes[*eptr++] & ctype_space) == 0)
+            RRETURN(MATCH_NOMATCH);
+          /* No need to skip more bytes - we know it's a 1-byte character */
+          }
+        break;
+
+        case OP_NOT_WORDCHAR:
+        for (i = 1; i <= min; i++)
+          {
+          if (eptr >= md->end_subject ||
+             (*eptr < 128 && (md->ctypes[*eptr++] & ctype_word) != 0))
+            RRETURN(MATCH_NOMATCH);
+          while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
+          }
+        break;
+
+        case OP_WORDCHAR:
+        for (i = 1; i <= min; i++)
+          {
+          if (eptr >= md->end_subject ||
+             *eptr >= 128 || (md->ctypes[*eptr++] & ctype_word) == 0)
+            RRETURN(MATCH_NOMATCH);
+          /* No need to skip more bytes - we know it's a 1-byte character */
+          }
+        break;
+
+        default:
+        RRETURN(PCRE_ERROR_INTERNAL);
+        }  /* End switch(ctype) */
+
+      else
+#endif     /* SUPPORT_UTF8 */
+
+      /* Code for the non-UTF-8 case for minimum matching of operators other
+      than OP_PROP and OP_NOTPROP. */
+
+      switch(ctype)
+        {
+        case OP_ANY:
+        if ((ims & PCRE_DOTALL) == 0)
+          {
+          for (i = 1; i <= min; i++)
+            if (*eptr++ == NEWLINE) RRETURN(MATCH_NOMATCH);
+          }
+        else eptr += min;
+        break;
+
+        case OP_ANYBYTE:
+        eptr += min;
+        break;
+
+        case OP_NOT_DIGIT:
+        for (i = 1; i <= min; i++)
+          if ((md->ctypes[*eptr++] & ctype_digit) != 0) RRETURN(MATCH_NOMATCH);
+        break;
+
+        case OP_DIGIT:
+        for (i = 1; i <= min; i++)
+          if ((md->ctypes[*eptr++] & ctype_digit) == 0) RRETURN(MATCH_NOMATCH);
+        break;
+
+        case OP_NOT_WHITESPACE:
+        for (i = 1; i <= min; i++)
+          if ((md->ctypes[*eptr++] & ctype_space) != 0) RRETURN(MATCH_NOMATCH);
+        break;
+
+        case OP_WHITESPACE:
+        for (i = 1; i <= min; i++)
+          if ((md->ctypes[*eptr++] & ctype_space) == 0) RRETURN(MATCH_NOMATCH);
+        break;
+
+        case OP_NOT_WORDCHAR:
+        for (i = 1; i <= min; i++)
+          if ((md->ctypes[*eptr++] & ctype_word) != 0)
+            RRETURN(MATCH_NOMATCH);
+        break;
+
+        case OP_WORDCHAR:
+        for (i = 1; i <= min; i++)
+          if ((md->ctypes[*eptr++] & ctype_word) == 0)
+            RRETURN(MATCH_NOMATCH);
+        break;
+
+        default:
+        RRETURN(PCRE_ERROR_INTERNAL);
+        }
+      }
+
+    /* If min = max, continue at the same level without recursing */
+
+    if (min == max) continue;
+
+    /* If minimizing, we have to test the rest of the pattern before each
+    subsequent match. Again, separate the UTF-8 case for speed, and also
+    separate the UCP cases. */
+
+    if (minimize)
+      {
+#ifdef SUPPORT_UCP
+      if (prop_type > 0)
+        {
+        for (fi = min;; fi++)
+          {
+          RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+          if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+          if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+          GETCHARINC(c, eptr);
+          prop_category = ucp_findchar(c, &prop_chartype, &prop_othercase);
+          if ((*prop_test_variable == prop_test_against) == prop_fail_result)
+            RRETURN(MATCH_NOMATCH);
+          }
+        }
+
+      /* Match extended Unicode sequences. We will get here only if the
+      support is in the binary; otherwise a compile-time error occurs. */
+
+      else if (ctype == OP_EXTUNI)
+        {
+        for (fi = min;; fi++)
+          {
+          RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+          if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+          if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+          GETCHARINCTEST(c, eptr);
+          prop_category = ucp_findchar(c, &prop_chartype, &prop_othercase);
+          if (prop_category == ucp_M) RRETURN(MATCH_NOMATCH);
+          while (eptr < md->end_subject)
+            {
+            int len = 1;
+            if (!md->utf8) c = *eptr; else
+              {
+              GETCHARLEN(c, eptr, len);
+              }
+            prop_category = ucp_findchar(c, &prop_chartype, &prop_othercase);
+            if (prop_category != ucp_M) break;
+            eptr += len;
+            }
+          }
+        }
+
+      else
+#endif     /* SUPPORT_UCP */
+
+#ifdef SUPPORT_UTF8
+      /* UTF-8 mode */
+      if (md->utf8)
+        {
+        for (fi = min;; fi++)
+          {
+          RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+          if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+          if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+
+          GETCHARINC(c, eptr);
+          switch(ctype)
+            {
+            case OP_ANY:
+            if ((ims & PCRE_DOTALL) == 0 && c == NEWLINE) RRETURN(MATCH_NOMATCH);
+            break;
+
+            case OP_ANYBYTE:
+            break;
+
+            case OP_NOT_DIGIT:
+            if (c < 256 && (md->ctypes[c] & ctype_digit) != 0)
+              RRETURN(MATCH_NOMATCH);
+            break;
+
+            case OP_DIGIT:
+            if (c >= 256 || (md->ctypes[c] & ctype_digit) == 0)
+              RRETURN(MATCH_NOMATCH);
+            break;
+
+            case OP_NOT_WHITESPACE:
+            if (c < 256 && (md->ctypes[c] & ctype_space) != 0)
+              RRETURN(MATCH_NOMATCH);
+            break;
+
+            case OP_WHITESPACE:
+            if  (c >= 256 || (md->ctypes[c] & ctype_space) == 0)
+              RRETURN(MATCH_NOMATCH);
+            break;
+
+            case OP_NOT_WORDCHAR:
+            if (c < 256 && (md->ctypes[c] & ctype_word) != 0)
+              RRETURN(MATCH_NOMATCH);
+            break;
+
+            case OP_WORDCHAR:
+            if (c >= 256 && (md->ctypes[c] & ctype_word) == 0)
+              RRETURN(MATCH_NOMATCH);
+            break;
+
+            default:
+            RRETURN(PCRE_ERROR_INTERNAL);
+            }
+          }
+        }
+      else
+#endif
+      /* Not UTF-8 mode */
+        {
+        for (fi = min;; fi++)
+          {
+          RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+          if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+          if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+          c = *eptr++;
+          switch(ctype)
+            {
+            case OP_ANY:
+            if ((ims & PCRE_DOTALL) == 0 && c == NEWLINE) RRETURN(MATCH_NOMATCH);
+            break;
+
+            case OP_ANYBYTE:
+            break;
+
+            case OP_NOT_DIGIT:
+            if ((md->ctypes[c] & ctype_digit) != 0) RRETURN(MATCH_NOMATCH);
+            break;
+
+            case OP_DIGIT:
+            if ((md->ctypes[c] & ctype_digit) == 0) RRETURN(MATCH_NOMATCH);
+            break;
+
+            case OP_NOT_WHITESPACE:
+            if ((md->ctypes[c] & ctype_space) != 0) RRETURN(MATCH_NOMATCH);
+            break;
+
+            case OP_WHITESPACE:
+            if  ((md->ctypes[c] & ctype_space) == 0) RRETURN(MATCH_NOMATCH);
+            break;
+
+            case OP_NOT_WORDCHAR:
+            if ((md->ctypes[c] & ctype_word) != 0) RRETURN(MATCH_NOMATCH);
+            break;
+
+            case OP_WORDCHAR:
+            if ((md->ctypes[c] & ctype_word) == 0) RRETURN(MATCH_NOMATCH);
+            break;
+
+            default:
+            RRETURN(PCRE_ERROR_INTERNAL);
+            }
+          }
+        }
+      /* Control never gets here */
+      }
+
+    /* If maximizing it is worth using inline code for speed, doing the type
+    test once at the start (i.e. keep it out of the loop). Again, keep the
+    UTF-8 and UCP stuff separate. */
+
+    else
+      {
+      pp = eptr;  /* Remember where we started */
+
+#ifdef SUPPORT_UCP
+      if (prop_type > 0)
+        {
+        for (i = min; i < max; i++)
+          {
+          int len = 1;
+          if (eptr >= md->end_subject) break;
+          GETCHARLEN(c, eptr, len);
+          prop_category = ucp_findchar(c, &prop_chartype, &prop_othercase);
+          if ((*prop_test_variable == prop_test_against) == prop_fail_result)
+            break;
+          eptr+= len;
+          }
+
+        /* eptr is now past the end of the maximum run */
+
+        for(;;)
+          {
+          RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+          if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+          if (eptr-- == pp) break;        /* Stop if tried at original pos */
+          BACKCHAR(eptr);
+          }
+        }
+
+      /* Match extended Unicode sequences. We will get here only if the
+      support is in the binary; otherwise a compile-time error occurs. */
+
+      else if (ctype == OP_EXTUNI)
+        {
+        for (i = min; i < max; i++)
+          {
+          if (eptr >= md->end_subject) break;
+          GETCHARINCTEST(c, eptr);
+          prop_category = ucp_findchar(c, &prop_chartype, &prop_othercase);
+          if (prop_category == ucp_M) break;
+          while (eptr < md->end_subject)
+            {
+            int len = 1;
+            if (!md->utf8) c = *eptr; else
+              {
+              GETCHARLEN(c, eptr, len);
+              }
+            prop_category = ucp_findchar(c, &prop_chartype, &prop_othercase);
+            if (prop_category != ucp_M) break;
+            eptr += len;
+            }
+          }
+
+        /* eptr is now past the end of the maximum run */
+
+        for(;;)
+          {
+          RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+          if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+          if (eptr-- == pp) break;        /* Stop if tried at original pos */
+          for (;;)                        /* Move back over one extended */
+            {
+            int len = 1;
+            BACKCHAR(eptr);
+            if (!md->utf8) c = *eptr; else
+              {
+              GETCHARLEN(c, eptr, len);
+              }
+            prop_category = ucp_findchar(c, &prop_chartype, &prop_othercase);
+            if (prop_category != ucp_M) break;
+            eptr--;
+            }
+          }
+        }
+
+      else
+#endif   /* SUPPORT_UCP */
+
+#ifdef SUPPORT_UTF8
+      /* UTF-8 mode */
+
+      if (md->utf8)
+        {
+        switch(ctype)
+          {
+          case OP_ANY:
+
+          /* Special code is required for UTF8, but when the maximum is unlimited
+          we don't need it, so we repeat the non-UTF8 code. This is probably
+          worth it, because .* is quite a common idiom. */
+
+          if (max < INT_MAX)
+            {
+            if ((ims & PCRE_DOTALL) == 0)
+              {
+              for (i = min; i < max; i++)
+                {
+                if (eptr >= md->end_subject || *eptr == NEWLINE) break;
+                eptr++;
+                while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
+                }
+              }
+            else
+              {
+              for (i = min; i < max; i++)
+                {
+                eptr++;
+                while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
+                }
+              }
+            }
+
+          /* Handle unlimited UTF-8 repeat */
+
+          else
+            {
+            if ((ims & PCRE_DOTALL) == 0)
+              {
+              for (i = min; i < max; i++)
+                {
+                if (eptr >= md->end_subject || *eptr == NEWLINE) break;
+                eptr++;
+                }
+              break;
+              }
+            else
+              {
+              c = max - min;
+              if (c > md->end_subject - eptr) c = md->end_subject - eptr;
+              eptr += c;
+              }
+            }
+          break;
+
+          /* The byte case is the same as non-UTF8 */
+
+          case OP_ANYBYTE:
+          c = max - min;
+          if (c > md->end_subject - eptr) c = md->end_subject - eptr;
+          eptr += c;
+          break;
+
+          case OP_NOT_DIGIT:
+          for (i = min; i < max; i++)
+            {
+            int len = 1;
+            if (eptr >= md->end_subject) break;
+            GETCHARLEN(c, eptr, len);
+            if (c < 256 && (md->ctypes[c] & ctype_digit) != 0) break;
+            eptr+= len;
+            }
+          break;
+
+          case OP_DIGIT:
+          for (i = min; i < max; i++)
+            {
+            int len = 1;
+            if (eptr >= md->end_subject) break;
+            GETCHARLEN(c, eptr, len);
+            if (c >= 256 ||(md->ctypes[c] & ctype_digit) == 0) break;
+            eptr+= len;
+            }
+          break;
+
+          case OP_NOT_WHITESPACE:
+          for (i = min; i < max; i++)
+            {
+            int len = 1;
+            if (eptr >= md->end_subject) break;
+            GETCHARLEN(c, eptr, len);
+            if (c < 256 && (md->ctypes[c] & ctype_space) != 0) break;
+            eptr+= len;
+            }
+          break;
+
+          case OP_WHITESPACE:
+          for (i = min; i < max; i++)
+            {
+            int len = 1;
+            if (eptr >= md->end_subject) break;
+            GETCHARLEN(c, eptr, len);
+            if (c >= 256 ||(md->ctypes[c] & ctype_space) == 0) break;
+            eptr+= len;
+            }
+          break;
+
+          case OP_NOT_WORDCHAR:
+          for (i = min; i < max; i++)
+            {
+            int len = 1;
+            if (eptr >= md->end_subject) break;
+            GETCHARLEN(c, eptr, len);
+            if (c < 256 && (md->ctypes[c] & ctype_word) != 0) break;
+            eptr+= len;
+            }
+          break;
+
+          case OP_WORDCHAR:
+          for (i = min; i < max; i++)
+            {
+            int len = 1;
+            if (eptr >= md->end_subject) break;
+            GETCHARLEN(c, eptr, len);
+            if (c >= 256 || (md->ctypes[c] & ctype_word) == 0) break;
+            eptr+= len;
+            }
+          break;
+
+          default:
+          RRETURN(PCRE_ERROR_INTERNAL);
+          }
+
+        /* eptr is now past the end of the maximum run */
+
+        for(;;)
+          {
+          RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+          if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+          if (eptr-- == pp) break;        /* Stop if tried at original pos */
+          BACKCHAR(eptr);
+          }
+        }
+      else
+#endif
+
+      /* Not UTF-8 mode */
+        {
+        switch(ctype)
+          {
+          case OP_ANY:
+          if ((ims & PCRE_DOTALL) == 0)
+            {
+            for (i = min; i < max; i++)
+              {
+              if (eptr >= md->end_subject || *eptr == NEWLINE) break;
+              eptr++;
+              }
+            break;
+            }
+          /* For DOTALL case, fall through and treat as \C */
+
+          case OP_ANYBYTE:
+          c = max - min;
+          if (c > md->end_subject - eptr) c = md->end_subject - eptr;
+          eptr += c;
+          break;
+
+          case OP_NOT_DIGIT:
+          for (i = min; i < max; i++)
+            {
+            if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_digit) != 0)
+              break;
+            eptr++;
+            }
+          break;
+
+          case OP_DIGIT:
+          for (i = min; i < max; i++)
+            {
+            if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_digit) == 0)
+              break;
+            eptr++;
+            }
+          break;
+
+          case OP_NOT_WHITESPACE:
+          for (i = min; i < max; i++)
+            {
+            if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_space) != 0)
+              break;
+            eptr++;
+            }
+          break;
+
+          case OP_WHITESPACE:
+          for (i = min; i < max; i++)
+            {
+            if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_space) == 0)
+              break;
+            eptr++;
+            }
+          break;
+
+          case OP_NOT_WORDCHAR:
+          for (i = min; i < max; i++)
+            {
+            if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_word) != 0)
+              break;
+            eptr++;
+            }
+          break;
+
+          case OP_WORDCHAR:
+          for (i = min; i < max; i++)
+            {
+            if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_word) == 0)
+              break;
+            eptr++;
+            }
+          break;
+
+          default:
+          RRETURN(PCRE_ERROR_INTERNAL);
+          }
+
+        /* eptr is now past the end of the maximum run */
+
+        while (eptr >= pp)
+          {
+          RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+          eptr--;
+          if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+          }
+        }
+
+      /* Get here if we can't make it match with any permitted repetitions */
+
+      RRETURN(MATCH_NOMATCH);
+      }
+    /* Control never gets here */
+
+    /* There's been some horrible disaster. Since all codes > OP_BRA are
+    for capturing brackets, and there shouldn't be any gaps between 0 and
+    OP_BRA, arrival here can only mean there is something seriously wrong
+    in the code above or the OP_xxx definitions. */
+
+    default:
+    DPRINTF(("Unknown opcode %d\n", *ecode));
+    RRETURN(PCRE_ERROR_UNKNOWN_NODE);
+    }
+
+  /* Do not stick any code in here without much thought; it is assumed
+  that "continue" in the code above comes out to here to repeat the main
+  loop. */
+
+  }             /* End of main loop */
+/* Control never reaches here */
+}
+
+
+/***************************************************************************
+****************************************************************************
+                   RECURSION IN THE match() FUNCTION
+
+Undefine all the macros that were defined above to handle this. */
+
+#ifdef NO_RECURSE
+#undef eptr
+#undef ecode
+#undef offset_top
+#undef ims
+#undef eptrb
+#undef flags
+
+#undef callpat
+#undef charptr
+#undef data
+#undef next
+#undef pp
+#undef prev
+#undef saved_eptr
+
+#undef new_recursive
+
+#undef cur_is_word
+#undef condition
+#undef minimize
+#undef prev_is_word
+
+#undef original_ims
+
+#undef ctype
+#undef length
+#undef max
+#undef min
+#undef number
+#undef offset
+#undef op
+#undef save_capture_last
+#undef save_offset1
+#undef save_offset2
+#undef save_offset3
+#undef stacksave
+
+#undef newptrb
+
+#endif
+
+/* These two are defined as macros in both cases */
+
+#undef fc
+#undef fi
+
+/***************************************************************************
+***************************************************************************/
+
+
+
+/*************************************************
+*         Execute a Regular Expression           *
+*************************************************/
+
+/* This function applies a compiled re to a subject string and picks out
+portions of the string if it matches. Two elements in the vector are set for
+each substring: the offsets to the start and end of the substring.
+
+Arguments:
+  argument_re     points to the compiled expression
+  extra_data      points to extra data or is NULL
+  subject         points to the subject string
+  length          length of subject string (may contain binary zeros)
+  start_offset    where to start in the subject string
+  options         option bits
+  offsets         points to a vector of ints to be filled in with offsets
+  offsetcount     the number of elements in the vector
+
+Returns:          > 0 => success; value is the number of elements filled in
+                  = 0 => success, but offsets is not big enough
+                   -1 => failed to match
+                 < -1 => some kind of unexpected problem
+*/
+
+EXPORT int
+pcre_exec(const pcre *argument_re, const pcre_extra *extra_data,
+  const char *subject, int length, int start_offset, int options, int *offsets,
+  int offsetcount)
+{
+int rc, resetcount, ocount;
+int first_byte = -1;
+int req_byte = -1;
+int req_byte2 = -1;
+unsigned long int ims = 0;
+BOOL using_temporary_offsets = FALSE;
+BOOL anchored;
+BOOL startline;
+BOOL first_byte_caseless = FALSE;
+BOOL req_byte_caseless = FALSE;
+match_data match_block;
+const uschar *tables;
+const uschar *start_bits = NULL;
+const uschar *start_match = (const uschar *)subject + start_offset;
+const uschar *end_subject;
+const uschar *req_byte_ptr = start_match - 1;
+
+pcre_study_data internal_study;
+const pcre_study_data *study;
+
+real_pcre internal_re;
+const real_pcre *external_re = (const real_pcre *)argument_re;
+const real_pcre *re = external_re;
+
+/* Plausibility checks */
+
+if ((options & ~PUBLIC_EXEC_OPTIONS) != 0) return PCRE_ERROR_BADOPTION;
+if (re == NULL || subject == NULL ||
+   (offsets == NULL && offsetcount > 0)) return PCRE_ERROR_NULL;
+if (offsetcount < 0) return PCRE_ERROR_BADCOUNT;
+
+/* Fish out the optional data from the extra_data structure, first setting
+the default values. */
+
+study = NULL;
+match_block.match_limit = MATCH_LIMIT;
+match_block.callout_data = NULL;
+
+/* The table pointer is always in native byte order. */
+
+tables = external_re->tables;
+
+if (extra_data != NULL)
+  {
+  register unsigned int flags = extra_data->flags;
+  if ((flags & PCRE_EXTRA_STUDY_DATA) != 0)
+    study = (const pcre_study_data *)extra_data->study_data;
+  if ((flags & PCRE_EXTRA_MATCH_LIMIT) != 0)
+    match_block.match_limit = extra_data->match_limit;
+  if ((flags & PCRE_EXTRA_CALLOUT_DATA) != 0)
+    match_block.callout_data = extra_data->callout_data;
+  if ((flags & PCRE_EXTRA_TABLES) != 0) tables = extra_data->tables;
+  }
+
+/* If the exec call supplied NULL for tables, use the inbuilt ones. This
+is a feature that makes it possible to save compiled regex and re-use them
+in other programs later. */
+
+if (tables == NULL) tables = pcre_default_tables;
+
+/* Check that the first field in the block is the magic number. If it is not,
+test for a regex that was compiled on a host of opposite endianness. If this is
+the case, flipped values are put in internal_re and internal_study if there was
+study data too. */
+
+if (re->magic_number != MAGIC_NUMBER)
+  {
+  re = try_flipped(re, &internal_re, study, &internal_study);
+  if (re == NULL) return PCRE_ERROR_BADMAGIC;
+  if (study != NULL) study = &internal_study;
+  }
+
+/* Set up other data */
+
+anchored = ((re->options | options) & PCRE_ANCHORED) != 0;
+startline = (re->options & PCRE_STARTLINE) != 0;
+
+/* The code starts after the real_pcre block and the capture name table. */
+
+match_block.start_code = (const uschar *)external_re + re->name_table_offset +
+  re->name_count * re->name_entry_size;
+
+match_block.start_subject = (const uschar *)subject;
+match_block.start_offset = start_offset;
+match_block.end_subject = match_block.start_subject + length;
+end_subject = match_block.end_subject;
+
+match_block.endonly = (re->options & PCRE_DOLLAR_ENDONLY) != 0;
+match_block.utf8 = (re->options & PCRE_UTF8) != 0;
+
+match_block.notbol = (options & PCRE_NOTBOL) != 0;
+match_block.noteol = (options & PCRE_NOTEOL) != 0;
+match_block.notempty = (options & PCRE_NOTEMPTY) != 0;
+match_block.partial = (options & PCRE_PARTIAL) != 0;
+match_block.hitend = FALSE;
+
+match_block.recursive = NULL;                   /* No recursion at top level */
+
+match_block.lcc = tables + lcc_offset;
+match_block.ctypes = tables + ctypes_offset;
+
+/* Partial matching is supported only for a restricted set of regexes at the
+moment. */
+
+if (match_block.partial && (re->options & PCRE_NOPARTIAL) != 0)
+  return PCRE_ERROR_BADPARTIAL;
+
+/* Check a UTF-8 string if required. Unfortunately there's no way of passing
+back the character offset. */
+
+#ifdef SUPPORT_UTF8
+if (match_block.utf8 && (options & PCRE_NO_UTF8_CHECK) == 0)
+  {
+  if (valid_utf8((uschar *)subject, length) >= 0)
+    return PCRE_ERROR_BADUTF8;
+  if (start_offset > 0 && start_offset < length)
+    {
+    int tb = ((uschar *)subject)[start_offset];
+    if (tb > 127)
+      {
+      tb &= 0xc0;
+      if (tb != 0 && tb != 0xc0) return PCRE_ERROR_BADUTF8_OFFSET;
+      }
+    }
+  }
+#endif
+
+/* The ims options can vary during the matching as a result of the presence
+of (?ims) items in the pattern. They are kept in a local variable so that
+restoring at the exit of a group is easy. */
+
+ims = re->options & (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL);
+
+/* If the expression has got more back references than the offsets supplied can
+hold, we get a temporary chunk of working store to use during the matching.
+Otherwise, we can use the vector supplied, rounding down its size to a multiple
+of 3. */
+
+ocount = offsetcount - (offsetcount % 3);
+
+if (re->top_backref > 0 && re->top_backref >= ocount/3)
+  {
+  ocount = re->top_backref * 3 + 3;
+  match_block.offset_vector = (int *)(pcre_malloc)(ocount * sizeof(int));
+  if (match_block.offset_vector == NULL) return PCRE_ERROR_NOMEMORY;
+  using_temporary_offsets = TRUE;
+  DPRINTF(("Got memory to hold back references\n"));
+  }
+else match_block.offset_vector = offsets;
+
+match_block.offset_end = ocount;
+match_block.offset_max = (2*ocount)/3;
+match_block.offset_overflow = FALSE;
+match_block.capture_last = -1;
+
+/* Compute the minimum number of offsets that we need to reset each time. Doing
+this makes a huge difference to execution time when there aren't many brackets
+in the pattern. */
+
+resetcount = 2 + re->top_bracket * 2;
+if (resetcount > offsetcount) resetcount = ocount;
+
+/* Reset the working variable associated with each extraction. These should
+never be used unless previously set, but they get saved and restored, and so we
+initialize them to avoid reading uninitialized locations. */
+
+if (match_block.offset_vector != NULL)
+  {
+  register int *iptr = match_block.offset_vector + ocount;
+  register int *iend = iptr - resetcount/2 + 1;
+  while (--iptr >= iend) *iptr = -1;
+  }
+
+/* Set up the first character to match, if available. The first_byte value is
+never set for an anchored regular expression, but the anchoring may be forced
+at run time, so we have to test for anchoring. The first char may be unset for
+an unanchored pattern, of course. If there's no first char and the pattern was
+studied, there may be a bitmap of possible first characters. */
+
+if (!anchored)
+  {
+  if ((re->options & PCRE_FIRSTSET) != 0)
+    {
+    first_byte = re->first_byte & 255;
+    if ((first_byte_caseless = ((re->first_byte & REQ_CASELESS) != 0)) == TRUE)
+      first_byte = match_block.lcc[first_byte];
+    }
+  else
+    if (!startline && study != NULL &&
+      (study->options & PCRE_STUDY_MAPPED) != 0)
+        start_bits = study->start_bits;
+  }
+
+/* For anchored or unanchored matches, there may be a "last known required
+character" set. */
+
+if ((re->options & PCRE_REQCHSET) != 0)
+  {
+  req_byte = re->req_byte & 255;
+  req_byte_caseless = (re->req_byte & REQ_CASELESS) != 0;
+  req_byte2 = (tables + fcc_offset)[req_byte];  /* case flipped */
+  }
+
+/* Loop for handling unanchored repeated matching attempts; for anchored regexs
+the loop runs just once. */
+
+do
+  {
+  /* Reset the maximum number of extractions we might see. */
+
+  if (match_block.offset_vector != NULL)
+    {
+    register int *iptr = match_block.offset_vector;
+    register int *iend = iptr + resetcount;
+    while (iptr < iend) *iptr++ = -1;
+    }
+
+  /* Advance to a unique first char if possible */
+
+  if (first_byte >= 0)
+    {
+    if (first_byte_caseless)
+      while (start_match < end_subject &&
+             match_block.lcc[*start_match] != first_byte)
+        start_match++;
+    else
+      while (start_match < end_subject && *start_match != first_byte)
+        start_match++;
+    }
+
+  /* Or to just after \n for a multiline match if possible */
+
+  else if (startline)
+    {
+    if (start_match > match_block.start_subject + start_offset)
+      {
+      while (start_match < end_subject && start_match[-1] != NEWLINE)
+        start_match++;
+      }
+    }
+
+  /* Or to a non-unique first char after study */
+
+  else if (start_bits != NULL)
+    {
+    while (start_match < end_subject)
+      {
+      register unsigned int c = *start_match;
+      if ((start_bits[c/8] & (1 << (c&7))) == 0) start_match++; else break;
+      }
+    }
+
+#ifdef DEBUG  /* Sigh. Some compilers never learn. */
+  printf(">>>> Match against: ");
+  pchars(start_match, end_subject - start_match, TRUE, &match_block);
+  printf("\n");
+#endif
+
+  /* If req_byte is set, we know that that character must appear in the subject
+  for the match to succeed. If the first character is set, req_byte must be
+  later in the subject; otherwise the test starts at the match point. This
+  optimization can save a huge amount of backtracking in patterns with nested
+  unlimited repeats that aren't going to match. Writing separate code for
+  cased/caseless versions makes it go faster, as does using an autoincrement
+  and backing off on a match.
+
+  HOWEVER: when the subject string is very, very long, searching to its end can
+  take a long time, and give bad performance on quite ordinary patterns. This
+  showed up when somebody was matching /^C/ on a 32-megabyte string... so we
+  don't do this when the string is sufficiently long.
+
+  ALSO: this processing is disabled when partial matching is requested.
+  */
+
+  if (req_byte >= 0 &&
+      end_subject - start_match < REQ_BYTE_MAX &&
+      !match_block.partial)
+    {
+    register const uschar *p = start_match + ((first_byte >= 0)? 1 : 0);
+
+    /* We don't need to repeat the search if we haven't yet reached the
+    place we found it at last time. */
+
+    if (p > req_byte_ptr)
+      {
+      if (req_byte_caseless)
+        {
+        while (p < end_subject)
+          {
+          register int pp = *p++;
+          if (pp == req_byte || pp == req_byte2) { p--; break; }
+          }
+        }
+      else
+        {
+        while (p < end_subject)
+          {
+          if (*p++ == req_byte) { p--; break; }
+          }
+        }
+
+      /* If we can't find the required character, break the matching loop */
+
+      if (p >= end_subject) break;
+
+      /* If we have found the required character, save the point where we
+      found it, so that we don't search again next time round the loop if
+      the start hasn't passed this character yet. */
+
+      req_byte_ptr = p;
+      }
+    }
+
+  /* When a match occurs, substrings will be set for all internal extractions;
+  we just need to set up the whole thing as substring 0 before returning. If
+  there were too many extractions, set the return code to zero. In the case
+  where we had to get some local store to hold offsets for backreferences, copy
+  those back references that we can. In this case there need not be overflow
+  if certain parts of the pattern were not used. */
+
+  match_block.start_match = start_match;
+  match_block.match_call_count = 0;
+
+  rc = match(start_match, match_block.start_code, 2, &match_block, ims, NULL,
+    match_isgroup);
+
+  if (rc == MATCH_NOMATCH)
+    {
+    start_match++;
+#ifdef SUPPORT_UTF8
+    if (match_block.utf8)
+      while(start_match < end_subject && (*start_match & 0xc0) == 0x80)
+        start_match++;
+#endif
+    continue;
+    }
+
+  if (rc != MATCH_MATCH)
+    {
+    DPRINTF((">>>> error: returning %d\n", rc));
+    return rc;
+    }
+
+  /* We have a match! Copy the offset information from temporary store if
+  necessary */
+
+  if (using_temporary_offsets)
+    {
+    if (offsetcount >= 4)
+      {
+      memcpy(offsets + 2, match_block.offset_vector + 2,
+        (offsetcount - 2) * sizeof(int));
+      DPRINTF(("Copied offsets from temporary memory\n"));
+      }
+    if (match_block.end_offset_top > offsetcount)
+      match_block.offset_overflow = TRUE;
+
+    DPRINTF(("Freeing temporary memory\n"));
+    (pcre_free)(match_block.offset_vector);
+    }
+
+  rc = match_block.offset_overflow? 0 : match_block.end_offset_top/2;
+
+  if (offsetcount < 2) rc = 0; else
+    {
+    offsets[0] = start_match - match_block.start_subject;
+    offsets[1] = match_block.end_match_ptr - match_block.start_subject;
+    }
+
+  DPRINTF((">>>> returning %d\n", rc));
+  return rc;
+  }
+
+/* This "while" is the end of the "do" above */
+
+while (!anchored && start_match <= end_subject);
+
+if (using_temporary_offsets)
+  {
+  DPRINTF(("Freeing temporary memory\n"));
+  (pcre_free)(match_block.offset_vector);
+  }
+
+if (match_block.partial && match_block.hitend)
+  {
+  DPRINTF((">>>> returning PCRE_ERROR_PARTIAL\n"));
+  return PCRE_ERROR_PARTIAL;
+  }
+else
+  {
+  DPRINTF((">>>> returning PCRE_ERROR_NOMATCH\n"));
+  return PCRE_ERROR_NOMATCH;
+  }
+}
+
+/* End of pcre.c */
diff --git a/connectors/jk/native/iis/pcre/pcre.def b/connectors/jk/native/iis/pcre/pcre.def
new file mode 100644
index 0000000..4f6c4bf
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/pcre.def
@@ -0,0 +1,22 @@
+EXPORTS
+
+pcre_malloc DATA
+pcre_free DATA
+
+pcre_compile
+pcre_copy_substring
+pcre_exec
+pcre_get_substring
+pcre_get_substring_list
+pcre_free_substring
+pcre_free_substring_list
+pcre_info
+pcre_fullinfo
+pcre_maketables
+pcre_study
+pcre_version
+
+regcomp
+regexec
+regerror
+regfree
diff --git a/connectors/jk/native/iis/pcre/pcre.dsp b/connectors/jk/native/iis/pcre/pcre.dsp
new file mode 100644
index 0000000..018f7fe
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/pcre.dsp
@@ -0,0 +1,193 @@
+# Microsoft Developer Studio Project File - Name="pcre" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=pcre - 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 "pcre.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 "pcre.mak" CFG="pcre - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "pcre - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "pcre - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "pcre - 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 "LibR"
+# PROP Intermediate_Dir "LibR"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "_WIN32" /D "NDEBUG" /D "_WINDOWS" /D "PCRE_STATIC" /FD /c
+# ADD CPP /nologo /MD /W3 /Zi /O2 /D "_WIN32" /D "NDEBUG" /D "_WINDOWS" /D "PCRE_STATIC" /Fd"LibR/pcre_src" /FD /c
+# ADD BASE RSC /l 0x409
+# ADD RSC /l 0x409
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF  "$(CFG)" == "pcre - 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 "LibD"
+# PROP Intermediate_Dir "LibD"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "_WIN32" /D "_DEBUG" /D "_WINDOWS" /D "PCRE_STATIC" /FD /c
+# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "_WIN32" /D "_DEBUG" /D "_WINDOWS" /D "PCRE_STATIC" /Fd"LibD/pcre_src" /FD /c
+# ADD BASE RSC /l 0x409
+# ADD RSC /l 0x409
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ENDIF 
+
+# Begin Target
+
+# Name "pcre - Win32 Release"
+# Name "pcre - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "*.c"
+# Begin Source File
+
+SOURCE=.\dftables.exe
+
+!IF  "$(CFG)" == "pcre - Win32 Release"
+
+# Begin Custom Build - Creating pcre chartables.c from dftables 
+InputPath=.\dftables.exe
+
+".\chartables.c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	.\dftables.exe chartables.c 
+	
+# End Custom Build
+
+!ELSEIF  "$(CFG)" == "pcre - Win32 Debug"
+
+# Begin Custom Build - Creating pcre chartables.c from dftables 
+InputPath=.\dftables.exe
+
+".\chartables.c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	.\dftables.exe chartables.c 
+	
+# End Custom Build
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\get.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\maketables.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\pcre.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\study.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "*.h"
+# Begin Source File
+
+SOURCE=.\config.hw
+
+!IF  "$(CFG)" == "pcre - Win32 Release"
+
+# Begin Custom Build - Creating pcre config.h from config.hw 
+InputPath=.\config.hw
+
+".\config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	type .\config.hw > .\config.h
+	
+# End Custom Build
+
+!ELSEIF  "$(CFG)" == "pcre - Win32 Debug"
+
+# Begin Custom Build - Creating pcre config.h from config.hw 
+InputPath=.\config.hw
+
+".\config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	type .\config.hw > .\config.h
+	
+# End Custom Build
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\internal.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\pcre.hw
+
+!IF  "$(CFG)" == "pcre - Win32 Release"
+
+# Begin Custom Build - Creating pcre.h from pcre.hw 
+InputPath=.\pcre.hw
+
+".\pcre.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	type .\pcre.hw > .\pcre.h
+	
+# End Custom Build
+
+!ELSEIF  "$(CFG)" == "pcre - Win32 Debug"
+
+# Begin Custom Build - Creating pcre.h from pcre.hw 
+InputPath=.\pcre.hw
+
+".\pcre.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	type .\pcre.hw > .\pcre.h
+	
+# End Custom Build
+
+!ENDIF 
+
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/connectors/jk/native/iis/pcre/pcre.hw b/connectors/jk/native/iis/pcre/pcre.hw
new file mode 100644
index 0000000..4534b63
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/pcre.hw
@@ -0,0 +1,239 @@
+/*************************************************
+*       Perl-Compatible Regular Expressions      *
+*************************************************/
+
+/* In its original form, this is the .in file that is transformed by
+"configure" into pcre.h.
+
+           Copyright (c) 1997-2004 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+    * Neither the name of the University of Cambridge nor the names of its
+      contributors may be used to endorse or promote products derived from
+      this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+#ifndef _PCRE_H
+#define _PCRE_H
+
+/* The file pcre.h is build by "configure" or copied from pcre.hw
+Do not edit it; instead make changes to pcre.in and/or pcre.hw */
+
+#define PCRE_MAJOR          5
+#define PCRE_MINOR          0
+#define PCRE_DATE           13-Sep-2004
+
+/* Win32 uses DLL by default */
+
+#ifdef _WIN32
+#  ifdef PCRE_DEFINITION
+#    ifdef DLL_EXPORT
+#      define PCRE_DATA_SCOPE __declspec(dllexport)
+#    endif
+#  else
+#    ifndef PCRE_STATIC
+#      define PCRE_DATA_SCOPE extern __declspec(dllimport)
+#    endif
+#  endif
+#endif
+#ifndef PCRE_DATA_SCOPE
+#  define PCRE_DATA_SCOPE     extern
+#endif
+
+/* Have to include stdlib.h in order to ensure that size_t is defined;
+it is needed here for malloc. */
+
+#include <stdlib.h>
+
+/* Allow for C++ users */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Options */
+
+#define PCRE_CASELESS           0x0001
+#define PCRE_MULTILINE          0x0002
+#define PCRE_DOTALL             0x0004
+#define PCRE_EXTENDED           0x0008
+#define PCRE_ANCHORED           0x0010
+#define PCRE_DOLLAR_ENDONLY     0x0020
+#define PCRE_EXTRA              0x0040
+#define PCRE_NOTBOL             0x0080
+#define PCRE_NOTEOL             0x0100
+#define PCRE_UNGREEDY           0x0200
+#define PCRE_NOTEMPTY           0x0400
+#define PCRE_UTF8               0x0800
+#define PCRE_NO_AUTO_CAPTURE    0x1000
+#define PCRE_NO_UTF8_CHECK      0x2000
+#define PCRE_AUTO_CALLOUT       0x4000
+#define PCRE_PARTIAL            0x8000
+
+/* Exec-time and get/set-time error codes */
+
+#define PCRE_ERROR_NOMATCH         (-1)
+#define PCRE_ERROR_NULL            (-2)
+#define PCRE_ERROR_BADOPTION       (-3)
+#define PCRE_ERROR_BADMAGIC        (-4)
+#define PCRE_ERROR_UNKNOWN_NODE    (-5)
+#define PCRE_ERROR_NOMEMORY        (-6)
+#define PCRE_ERROR_NOSUBSTRING     (-7)
+#define PCRE_ERROR_MATCHLIMIT      (-8)
+#define PCRE_ERROR_CALLOUT         (-9)  /* Never used by PCRE itself */
+#define PCRE_ERROR_BADUTF8        (-10)
+#define PCRE_ERROR_BADUTF8_OFFSET (-11)
+#define PCRE_ERROR_PARTIAL        (-12)
+#define PCRE_ERROR_BADPARTIAL     (-13)
+#define PCRE_ERROR_INTERNAL       (-14)
+#define PCRE_ERROR_BADCOUNT       (-15)
+
+/* Request types for pcre_fullinfo() */
+
+#define PCRE_INFO_OPTIONS            0
+#define PCRE_INFO_SIZE               1
+#define PCRE_INFO_CAPTURECOUNT       2
+#define PCRE_INFO_BACKREFMAX         3
+#define PCRE_INFO_FIRSTBYTE          4
+#define PCRE_INFO_FIRSTCHAR          4  /* For backwards compatibility */
+#define PCRE_INFO_FIRSTTABLE         5
+#define PCRE_INFO_LASTLITERAL        6
+#define PCRE_INFO_NAMEENTRYSIZE      7
+#define PCRE_INFO_NAMECOUNT          8
+#define PCRE_INFO_NAMETABLE          9
+#define PCRE_INFO_STUDYSIZE         10
+#define PCRE_INFO_DEFAULT_TABLES    11
+
+/* Request types for pcre_config() */
+
+#define PCRE_CONFIG_UTF8                    0
+#define PCRE_CONFIG_NEWLINE                 1
+#define PCRE_CONFIG_LINK_SIZE               2
+#define PCRE_CONFIG_POSIX_MALLOC_THRESHOLD  3
+#define PCRE_CONFIG_MATCH_LIMIT             4
+#define PCRE_CONFIG_STACKRECURSE            5
+#define PCRE_CONFIG_UNICODE_PROPERTIES      6
+
+/* Bit flags for the pcre_extra structure */
+
+#define PCRE_EXTRA_STUDY_DATA          0x0001
+#define PCRE_EXTRA_MATCH_LIMIT         0x0002
+#define PCRE_EXTRA_CALLOUT_DATA        0x0004
+#define PCRE_EXTRA_TABLES              0x0008
+
+/* Types */
+
+struct real_pcre;                 /* declaration; the definition is private  */
+typedef struct real_pcre pcre;
+
+/* The structure for passing additional data to pcre_exec(). This is defined in
+such as way as to be extensible. Always add new fields at the end, in order to
+remain compatible. */
+
+typedef struct pcre_extra {
+  unsigned long int flags;        /* Bits for which fields are set */
+  void *study_data;               /* Opaque data from pcre_study() */
+  unsigned long int match_limit;  /* Maximum number of calls to match() */
+  void *callout_data;             /* Data passed back in callouts */
+  const unsigned char *tables;    /* Pointer to character tables */
+} pcre_extra;
+
+/* The structure for passing out data via the pcre_callout_function. We use a
+structure so that new fields can be added on the end in future versions,
+without changing the API of the function, thereby allowing old clients to work
+without modification. */
+
+typedef struct pcre_callout_block {
+  int          version;           /* Identifies version of block */
+  /* ------------------------ Version 0 ------------------------------- */
+  int          callout_number;    /* Number compiled into pattern */
+  int         *offset_vector;     /* The offset vector */
+  const char  *subject;           /* The subject being matched */
+  int          subject_length;    /* The length of the subject */
+  int          start_match;       /* Offset to start of this match attempt */
+  int          current_position;  /* Where we currently are in the subject */
+  int          capture_top;       /* Max current capture */
+  int          capture_last;      /* Most recently closed capture */
+  void        *callout_data;      /* Data passed in with the call */
+  /* ------------------- Added for Version 1 -------------------------- */
+  int          pattern_position;  /* Offset to next item in the pattern */
+  int          next_item_length;  /* Length of next item in the pattern */
+  /* ------------------------------------------------------------------ */
+} pcre_callout_block;
+
+/* Indirection for store get and free functions. These can be set to
+alternative malloc/free functions if required. Special ones are used in the
+non-recursive case for "frames". There is also an optional callout function
+that is triggered by the (?) regex item. Some magic is required for Win32 DLL;
+it is null on other OS. For Virtual Pascal, these have to be different again.
+*/
+
+#ifndef VPCOMPAT
+PCRE_DATA_SCOPE void *(*pcre_malloc)(size_t);
+PCRE_DATA_SCOPE void  (*pcre_free)(void *);
+PCRE_DATA_SCOPE void *(*pcre_stack_malloc)(size_t);
+PCRE_DATA_SCOPE void  (*pcre_stack_free)(void *);
+PCRE_DATA_SCOPE int   (*pcre_callout)(pcre_callout_block *);
+#else   /* VPCOMPAT */
+extern void *pcre_malloc(size_t);
+extern void  pcre_free(void *);
+extern void *pcre_stack_malloc(size_t);
+extern void  pcre_stack_free(void *);
+extern int   pcre_callout(pcre_callout_block *);
+#endif  /* VPCOMPAT */
+
+/* Exported PCRE functions */
+
+extern pcre *pcre_compile(const char *, int, const char **,
+              int *, const unsigned char *);
+extern int  pcre_config(int, void *);
+extern int  pcre_copy_named_substring(const pcre *, const char *,
+              int *, int, const char *, char *, int);
+extern int  pcre_copy_substring(const char *, int *, int, int,
+              char *, int);
+extern int  pcre_exec(const pcre *, const pcre_extra *,
+              const char *, int, int, int, int *, int);
+extern void pcre_free_substring(const char *);
+extern void pcre_free_substring_list(const char **);
+extern int  pcre_fullinfo(const pcre *, const pcre_extra *, int,
+              void *);
+extern int  pcre_get_named_substring(const pcre *, const char *,
+              int *, int,  const char *, const char **);
+extern int  pcre_get_stringnumber(const pcre *, const char *);
+extern int  pcre_get_substring(const char *, int *, int, int,
+              const char **);
+extern int  pcre_get_substring_list(const char *, int *, int,
+              const char ***);
+extern int  pcre_info(const pcre *, int *, int *);
+extern const unsigned char *pcre_maketables(void);
+extern pcre_extra *pcre_study(const pcre *, int, const char **);
+extern const char *pcre_version(void);
+
+#ifdef __cplusplus
+}  /* extern "C" */
+#endif
+
+#endif /* End of pcre.h */
diff --git a/connectors/jk/native/iis/pcre/pcre.ia64 b/connectors/jk/native/iis/pcre/pcre.ia64
new file mode 100644
index 0000000..d7f1ecc
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/pcre.ia64
@@ -0,0 +1,132 @@
+# Microsoft Developer Studio Generated NMAKE File, Based on pcre.dsp
+# Use Platform SDK:
+# SetEnv.cmd /SRV64 /RETAIL
+# nmake -f pcre.amd64
+#
+
+CPP=cl.exe
+RSC=rc.exe
+OUTDIR=.\Release_ia64
+INTDIR=.\Release_ia64
+# Begin Custom Macros
+OutDir=.\Release_ia64
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0" 
+
+ALL : "$(OUTDIR)\pcre.lib"
+
+!ELSE 
+
+ALL : "$(OUTDIR)\pcre.lib"
+
+!ENDIF 
+
+CLEAN :
+	-@erase "$(INTDIR)\get.obj"
+	-@erase "$(INTDIR)\maketables.obj"
+	-@erase "$(INTDIR)\pcre.obj"
+	-@erase "$(INTDIR)\pcre_src.idb"
+	-@erase "$(INTDIR)\pcre_src.pdb"
+	-@erase "$(INTDIR)\study.obj"
+	-@erase "$(OUTDIR)\pcre.lib"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\pcre.bsc" 
+BSC32_SBRS= \
+	
+LIB32=link.exe -lib
+LIB32_FLAGS=/nologo /out:"$(OUTDIR)\pcre.lib" 
+LIB32_OBJS= \
+	"$(INTDIR)\get.obj" \
+	"$(INTDIR)\maketables.obj" \
+	"$(INTDIR)\pcre.obj" \
+	"$(INTDIR)\study.obj"
+
+"$(OUTDIR)\pcre.lib" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS)
+    $(LIB32) @<<
+  $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS)
+<<
+
+CPP_PROJ=/nologo /MD /W3 /Zi /O2 /D "_WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_IA64_=1" -DWIN64 /D "_WIN64" /Wp64 /FIPRE64PRA.H /D "PCRE_STATIC" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\pcre_src" /FD /c 
+
+.c{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cpp{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cxx{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.c{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cpp{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cxx{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+SOURCE=.\dftables.exe
+InputPath=.\dftables.exe
+
+".\chartables.c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	<<tempfile.bat 
+	@echo off 
+	.\dftables.exe chartables.c 
+<< 
+	
+SOURCE=.\get.c
+
+"$(INTDIR)\get.obj" : $(SOURCE) "$(INTDIR)" ".\config.h" ".\pcre.h"
+
+
+SOURCE=.\maketables.c
+
+"$(INTDIR)\maketables.obj" : $(SOURCE) "$(INTDIR)" ".\config.h" ".\pcre.h"
+
+
+SOURCE=.\pcre.c
+
+"$(INTDIR)\pcre.obj" : $(SOURCE) "$(INTDIR)" ".\chartables.c" ".\config.h" ".\pcre.h"
+
+
+SOURCE=.\study.c
+
+"$(INTDIR)\study.obj" : $(SOURCE) "$(INTDIR)" ".\config.h" ".\pcre.h"
+
+
+SOURCE=.\config.hw
+InputPath=.\config.hw
+
+".\config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	<<tempfile.bat 
+	@echo off 
+	type .\config.hw > .\config.h
+<< 
+	
+SOURCE=.\pcre.hw
+InputPath=.\pcre.hw
+
+".\pcre.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	<<tempfile.bat 
+	@echo off 
+	type .\pcre.hw > .\pcre.h
+<< 
+	
diff --git a/connectors/jk/native/iis/pcre/pcre.in b/connectors/jk/native/iis/pcre/pcre.in
new file mode 100644
index 0000000..163cf94
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/pcre.in
@@ -0,0 +1,239 @@
+/*************************************************
+*       Perl-Compatible Regular Expressions      *
+*************************************************/
+
+/* In its original form, this is the .in file that is transformed by
+"configure" into pcre.h.
+
+           Copyright (c) 1997-2004 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+    * Neither the name of the University of Cambridge nor the names of its
+      contributors may be used to endorse or promote products derived from
+      this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+#ifndef _PCRE_H
+#define _PCRE_H
+
+/* The file pcre.h is build by "configure". Do not edit it; instead
+make changes to pcre.in. */
+
+#define PCRE_MAJOR          @PCRE_MAJOR@
+#define PCRE_MINOR          @PCRE_MINOR@
+#define PCRE_DATE           @PCRE_DATE@
+
+/* Win32 uses DLL by default */
+
+#ifdef _WIN32
+#  ifdef PCRE_DEFINITION
+#    ifdef DLL_EXPORT
+#      define PCRE_DATA_SCOPE __declspec(dllexport)
+#    endif
+#  else
+#    ifndef PCRE_STATIC
+#      define PCRE_DATA_SCOPE extern __declspec(dllimport)
+#    endif
+#  endif
+#endif
+#ifndef PCRE_DATA_SCOPE
+#  define PCRE_DATA_SCOPE     extern
+#endif
+
+/* Have to include stdlib.h in order to ensure that size_t is defined;
+it is needed here for malloc. */
+
+#include <stdlib.h>
+
+/* Allow for C++ users */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Options */
+
+#define PCRE_CASELESS           0x0001
+#define PCRE_MULTILINE          0x0002
+#define PCRE_DOTALL             0x0004
+#define PCRE_EXTENDED           0x0008
+#define PCRE_ANCHORED           0x0010
+#define PCRE_DOLLAR_ENDONLY     0x0020
+#define PCRE_EXTRA              0x0040
+#define PCRE_NOTBOL             0x0080
+#define PCRE_NOTEOL             0x0100
+#define PCRE_UNGREEDY           0x0200
+#define PCRE_NOTEMPTY           0x0400
+#define PCRE_UTF8               0x0800
+#define PCRE_NO_AUTO_CAPTURE    0x1000
+#define PCRE_NO_UTF8_CHECK      0x2000
+#define PCRE_AUTO_CALLOUT       0x4000
+#define PCRE_PARTIAL            0x8000
+
+/* Exec-time and get/set-time error codes */
+
+#define PCRE_ERROR_NOMATCH         (-1)
+#define PCRE_ERROR_NULL            (-2)
+#define PCRE_ERROR_BADOPTION       (-3)
+#define PCRE_ERROR_BADMAGIC        (-4)
+#define PCRE_ERROR_UNKNOWN_NODE    (-5)
+#define PCRE_ERROR_NOMEMORY        (-6)
+#define PCRE_ERROR_NOSUBSTRING     (-7)
+#define PCRE_ERROR_MATCHLIMIT      (-8)
+#define PCRE_ERROR_CALLOUT         (-9)  /* Never used by PCRE itself */
+#define PCRE_ERROR_BADUTF8        (-10)
+#define PCRE_ERROR_BADUTF8_OFFSET (-11)
+#define PCRE_ERROR_PARTIAL        (-12)
+#define PCRE_ERROR_BADPARTIAL     (-13)
+#define PCRE_ERROR_INTERNAL       (-14)
+#define PCRE_ERROR_BADCOUNT       (-15)
+
+/* Request types for pcre_fullinfo() */
+
+#define PCRE_INFO_OPTIONS            0
+#define PCRE_INFO_SIZE               1
+#define PCRE_INFO_CAPTURECOUNT       2
+#define PCRE_INFO_BACKREFMAX         3
+#define PCRE_INFO_FIRSTBYTE          4
+#define PCRE_INFO_FIRSTCHAR          4  /* For backwards compatibility */
+#define PCRE_INFO_FIRSTTABLE         5
+#define PCRE_INFO_LASTLITERAL        6
+#define PCRE_INFO_NAMEENTRYSIZE      7
+#define PCRE_INFO_NAMECOUNT          8
+#define PCRE_INFO_NAMETABLE          9
+#define PCRE_INFO_STUDYSIZE         10
+#define PCRE_INFO_DEFAULT_TABLES    11
+
+/* Request types for pcre_config() */
+
+#define PCRE_CONFIG_UTF8                    0
+#define PCRE_CONFIG_NEWLINE                 1
+#define PCRE_CONFIG_LINK_SIZE               2
+#define PCRE_CONFIG_POSIX_MALLOC_THRESHOLD  3
+#define PCRE_CONFIG_MATCH_LIMIT             4
+#define PCRE_CONFIG_STACKRECURSE            5
+#define PCRE_CONFIG_UNICODE_PROPERTIES      6
+
+/* Bit flags for the pcre_extra structure */
+
+#define PCRE_EXTRA_STUDY_DATA          0x0001
+#define PCRE_EXTRA_MATCH_LIMIT         0x0002
+#define PCRE_EXTRA_CALLOUT_DATA        0x0004
+#define PCRE_EXTRA_TABLES              0x0008
+
+/* Types */
+
+struct real_pcre;                 /* declaration; the definition is private  */
+typedef struct real_pcre pcre;
+
+/* The structure for passing additional data to pcre_exec(). This is defined in
+such as way as to be extensible. Always add new fields at the end, in order to
+remain compatible. */
+
+typedef struct pcre_extra {
+  unsigned long int flags;        /* Bits for which fields are set */
+  void *study_data;               /* Opaque data from pcre_study() */
+  unsigned long int match_limit;  /* Maximum number of calls to match() */
+  void *callout_data;             /* Data passed back in callouts */
+  const unsigned char *tables;    /* Pointer to character tables */
+} pcre_extra;
+
+/* The structure for passing out data via the pcre_callout_function. We use a
+structure so that new fields can be added on the end in future versions,
+without changing the API of the function, thereby allowing old clients to work
+without modification. */
+
+typedef struct pcre_callout_block {
+  int          version;           /* Identifies version of block */
+  /* ------------------------ Version 0 ------------------------------- */
+  int          callout_number;    /* Number compiled into pattern */
+  int         *offset_vector;     /* The offset vector */
+  const char  *subject;           /* The subject being matched */
+  int          subject_length;    /* The length of the subject */
+  int          start_match;       /* Offset to start of this match attempt */
+  int          current_position;  /* Where we currently are in the subject */
+  int          capture_top;       /* Max current capture */
+  int          capture_last;      /* Most recently closed capture */
+  void        *callout_data;      /* Data passed in with the call */
+  /* ------------------- Added for Version 1 -------------------------- */
+  int          pattern_position;  /* Offset to next item in the pattern */
+  int          next_item_length;  /* Length of next item in the pattern */
+  /* ------------------------------------------------------------------ */
+} pcre_callout_block;
+
+/* Indirection for store get and free functions. These can be set to
+alternative malloc/free functions if required. Special ones are used in the
+non-recursive case for "frames". There is also an optional callout function
+that is triggered by the (?) regex item. Some magic is required for Win32 DLL;
+it is null on other OS. For Virtual Pascal, these have to be different again.
+*/
+
+#ifndef VPCOMPAT
+PCRE_DATA_SCOPE void *(*pcre_malloc)(size_t);
+PCRE_DATA_SCOPE void  (*pcre_free)(void *);
+PCRE_DATA_SCOPE void *(*pcre_stack_malloc)(size_t);
+PCRE_DATA_SCOPE void  (*pcre_stack_free)(void *);
+PCRE_DATA_SCOPE int   (*pcre_callout)(pcre_callout_block *);
+#else   /* VPCOMPAT */
+extern void *pcre_malloc(size_t);
+extern void  pcre_free(void *);
+extern void *pcre_stack_malloc(size_t);
+extern void  pcre_stack_free(void *);
+extern int   pcre_callout(pcre_callout_block *);
+#endif  /* VPCOMPAT */
+
+/* Exported PCRE functions */
+
+extern pcre *pcre_compile(const char *, int, const char **,
+              int *, const unsigned char *);
+extern int  pcre_config(int, void *);
+extern int  pcre_copy_named_substring(const pcre *, const char *,
+              int *, int, const char *, char *, int);
+extern int  pcre_copy_substring(const char *, int *, int, int,
+              char *, int);
+extern int  pcre_exec(const pcre *, const pcre_extra *,
+              const char *, int, int, int, int *, int);
+extern void pcre_free_substring(const char *);
+extern void pcre_free_substring_list(const char **);
+extern int  pcre_fullinfo(const pcre *, const pcre_extra *, int,
+              void *);
+extern int  pcre_get_named_substring(const pcre *, const char *,
+              int *, int,  const char *, const char **);
+extern int  pcre_get_stringnumber(const pcre *, const char *);
+extern int  pcre_get_substring(const char *, int *, int, int,
+              const char **);
+extern int  pcre_get_substring_list(const char *, int *, int,
+              const char ***);
+extern int  pcre_info(const pcre *, int *, int *);
+extern const unsigned char *pcre_maketables(void);
+extern pcre_extra *pcre_study(const pcre *, int, const char **);
+extern const char *pcre_version(void);
+
+#ifdef __cplusplus
+}  /* extern "C" */
+#endif
+
+#endif /* End of pcre.h */
diff --git a/connectors/jk/native/iis/pcre/pcre.x86 b/connectors/jk/native/iis/pcre/pcre.x86
new file mode 100644
index 0000000..cc40329
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/pcre.x86
@@ -0,0 +1,139 @@
+CPP=cl.exe
+RSC=rc.exe
+OUTDIR=.\Release_x86
+INTDIR=.\Release_x86
+# Begin Custom Macros
+OutDir=.\Release_x86
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0" 
+
+ALL : "$(OUTDIR)\pcre.lib"
+
+!ELSE 
+
+ALL : "dftables_x86" "$(OUTDIR)\pcre.lib"
+
+!ENDIF 
+
+!IF "$(RECURSE)" == "1" 
+CLEAN :"dftables_x86CLEAN" 
+!ELSE 
+CLEAN :
+!ENDIF 
+	-@erase "$(INTDIR)\get.obj"
+	-@erase "$(INTDIR)\maketables.obj"
+	-@erase "$(INTDIR)\pcre.obj"
+	-@erase "$(INTDIR)\pcre_src.idb"
+	-@erase "$(INTDIR)\pcre_src.pdb"
+	-@erase "$(INTDIR)\study.obj"
+	-@erase "$(OUTDIR)\pcre.lib"
+
+"$(OUTDIR)" :
+    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\pcre.bsc" 
+BSC32_SBRS= \
+	
+LIB32=link.exe -lib
+LIB32_FLAGS=/nologo /out:"$(OUTDIR)\pcre.lib" 
+LIB32_OBJS= \
+	"$(INTDIR)\get.obj" \
+	"$(INTDIR)\maketables.obj" \
+	"$(INTDIR)\pcre.obj" \
+	"$(INTDIR)\study.obj"
+
+"$(OUTDIR)\pcre.lib" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS)
+    $(LIB32) @<<
+  $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS)
+<<
+
+CPP_PROJ=/nologo /MD /W3 /Zi /O2 /D "_WIN32" /D "NDEBUG" /D "_WINDOWS" /D "PCRE_STATIC" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\pcre_src" /FD /c 
+
+.c{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cpp{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cxx{$(INTDIR)}.obj::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.c{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cpp{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+.cxx{$(INTDIR)}.sbr::
+   $(CPP) @<<
+   $(CPP_PROJ) $< 
+<<
+
+SOURCE=.\dftables.exe
+InputPath=.\dftables.exe
+
+".\chartables.c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	<<tempfile.bat 
+	@echo off 
+	.\dftables.exe chartables.c 
+<< 
+	
+SOURCE=.\get.c
+
+"$(INTDIR)\get.obj" : $(SOURCE) "$(INTDIR)" ".\config.h" ".\pcre.h"
+
+
+SOURCE=.\maketables.c
+
+"$(INTDIR)\maketables.obj" : $(SOURCE) "$(INTDIR)" ".\config.h" ".\pcre.h"
+
+
+SOURCE=.\pcre.c
+
+"$(INTDIR)\pcre.obj" : $(SOURCE) "$(INTDIR)" ".\chartables.c" ".\config.h" ".\pcre.h"
+
+
+SOURCE=.\study.c
+
+"$(INTDIR)\study.obj" : $(SOURCE) "$(INTDIR)" ".\config.h" ".\pcre.h"
+
+
+SOURCE=.\config.hw
+InputPath=.\config.hw
+
+".\config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	<<tempfile.bat 
+	@echo off 
+	type .\config.hw > .\config.h
+<< 
+	
+SOURCE=.\pcre.hw
+InputPath=.\pcre.hw
+
+".\pcre.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	<<tempfile.bat 
+	@echo off 
+	type .\pcre.hw > .\pcre.h
+<< 
+
+"dftables_x86" : 
+   cd "."
+   $(MAKE) /$(MAKEFLAGS) /F ".\dftables.x86" CFG="dftables_x86" 
+   cd "."
+
+"dftables_x86CLEAN" : 
+   cd "."
+   $(MAKE) /$(MAKEFLAGS) /F ".\dftables.x86" CFG="dftables_x86" RECURSE=1 CLEAN 
+   cd "."
diff --git a/connectors/jk/native/iis/pcre/pcredemo.c b/connectors/jk/native/iis/pcre/pcredemo.c
new file mode 100644
index 0000000..3817203
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/pcredemo.c
@@ -0,0 +1,324 @@
+/*************************************************
+*           PCRE DEMONSTRATION PROGRAM           *
+*************************************************/
+
+/* This is a demonstration program to illustrate the most straightforward ways
+of calling the PCRE regular expression library from a C program. See the
+pcresample documentation for a short discussion.
+
+Compile thuswise:
+  gcc -Wall pcredemo.c -I/usr/local/include -L/usr/local/lib \
+    -R/usr/local/lib -lpcre
+
+Replace "/usr/local/include" and "/usr/local/lib" with wherever the include and
+library files for PCRE are installed on your system. Only some operating
+systems (e.g. Solaris) use the -R option.
+*/
+
+
+#include <stdio.h>
+#include <string.h>
+#include <pcre.h>
+
+#define OVECCOUNT 30    /* should be a multiple of 3 */
+
+
+int main(int argc, char **argv)
+{
+pcre *re;
+const char *error;
+char *pattern;
+char *subject;
+unsigned char *name_table;
+int erroffset;
+int find_all;
+int namecount;
+int name_entry_size;
+int ovector[OVECCOUNT];
+int subject_length;
+int rc, i;
+
+
+/**************************************************************************
+* First, sort out the command line. There is only one possible option at  *
+* the moment, "-g" to request repeated matching to find all occurrences,  *
+* like Perl's /g option. We set the variable find_all to a non-zero value *
+* if the -g option is present. Apart from that, there must be exactly two *
+* arguments.                                                              *
+**************************************************************************/
+
+find_all = 0;
+for (i = 1; i < argc; i++)
+  {
+  if (strcmp(argv[i], "-g") == 0) find_all = 1;
+    else break;
+  }
+
+/* After the options, we require exactly two arguments, which are the pattern,
+and the subject string. */
+
+if (argc - i != 2)
+  {
+  printf("Two arguments required: a regex and a subject string\n");
+  return 1;
+  }
+
+pattern = argv[i];
+subject = argv[i+1];
+subject_length = (int)strlen(subject);
+
+
+/*************************************************************************
+* Now we are going to compile the regular expression pattern, and handle *
+* and errors that are detected.                                          *
+*************************************************************************/
+
+re = pcre_compile(
+  pattern,              /* the pattern */
+  0,                    /* default options */
+  &error,               /* for error message */
+  &erroffset,           /* for error offset */
+  NULL);                /* use default character tables */
+
+/* Compilation failed: print the error message and exit */
+
+if (re == NULL)
+  {
+  printf("PCRE compilation failed at offset %d: %s\n", erroffset, error);
+  return 1;
+  }
+
+
+/*************************************************************************
+* If the compilation succeeded, we call PCRE again, in order to do a     *
+* pattern match against the subject string. This does just ONE match. If *
+* further matching is needed, it will be done below.                     *
+*************************************************************************/
+
+rc = pcre_exec(
+  re,                   /* the compiled pattern */
+  NULL,                 /* no extra data - we didn't study the pattern */
+  subject,              /* the subject string */
+  subject_length,       /* the length of the subject */
+  0,                    /* start at offset 0 in the subject */
+  0,                    /* default options */
+  ovector,              /* output vector for substring information */
+  OVECCOUNT);           /* number of elements in the output vector */
+
+/* Matching failed: handle error cases */
+
+if (rc < 0)
+  {
+  switch(rc)
+    {
+    case PCRE_ERROR_NOMATCH: printf("No match\n"); break;
+    /*
+    Handle other special cases if you like
+    */
+    default: printf("Matching error %d\n", rc); break;
+    }
+  free(re);     /* Release memory used for the compiled pattern */
+  return 1;
+  }
+
+/* Match succeded */
+
+printf("\nMatch succeeded at offset %d\n", ovector[0]);
+
+
+/*************************************************************************
+* We have found the first match within the subject string. If the output *
+* vector wasn't big enough, set its size to the maximum. Then output any *
+* substrings that were captured.                                         *
+*************************************************************************/
+
+/* The output vector wasn't big enough */
+
+if (rc == 0)
+  {
+  rc = OVECCOUNT/3;
+  printf("ovector only has room for %d captured substrings\n", rc - 1);
+  }
+
+/* Show substrings stored in the output vector by number. Obviously, in a real
+application you might want to do things other than print them. */
+
+for (i = 0; i < rc; i++)
+  {
+  char *substring_start = subject + ovector[2*i];
+  int substring_length = ovector[2*i+1] - ovector[2*i];
+  printf("%2d: %.*s\n", i, substring_length, substring_start);
+  }
+
+
+/**************************************************************************
+* That concludes the basic part of this demonstration program. We have    *
+* compiled a pattern, and performed a single match. The code that follows *
+* first shows how to access named substrings, and then how to code for    *
+* repeated matches on the same subject.                                   *
+**************************************************************************/
+
+/* See if there are any named substrings, and if so, show them by name. First
+we have to extract the count of named parentheses from the pattern. */
+
+(void)pcre_fullinfo(
+  re,                   /* the compiled pattern */
+  NULL,                 /* no extra data - we didn't study the pattern */
+  PCRE_INFO_NAMECOUNT,  /* number of named substrings */
+  &namecount);          /* where to put the answer */
+
+if (namecount <= 0) printf("No named substrings\n"); else
+  {
+  unsigned char *tabptr;
+  printf("Named substrings\n");
+
+  /* Before we can access the substrings, we must extract the table for
+  translating names to numbers, and the size of each entry in the table. */
+
+  (void)pcre_fullinfo(
+    re,                       /* the compiled pattern */
+    NULL,                     /* no extra data - we didn't study the pattern */
+    PCRE_INFO_NAMETABLE,      /* address of the table */
+    &name_table);             /* where to put the answer */
+
+  (void)pcre_fullinfo(
+    re,                       /* the compiled pattern */
+    NULL,                     /* no extra data - we didn't study the pattern */
+    PCRE_INFO_NAMEENTRYSIZE,  /* size of each entry in the table */
+    &name_entry_size);        /* where to put the answer */
+
+  /* Now we can scan the table and, for each entry, print the number, the name,
+  and the substring itself. */
+
+  tabptr = name_table;
+  for (i = 0; i < namecount; i++)
+    {
+    int n = (tabptr[0] << 8) | tabptr[1];
+    printf("(%d) %*s: %.*s\n", n, name_entry_size - 3, tabptr + 2,
+      ovector[2*n+1] - ovector[2*n], subject + ovector[2*n]);
+    tabptr += name_entry_size;
+    }
+  }
+
+
+/*************************************************************************
+* If the "-g" option was given on the command line, we want to continue  *
+* to search for additional matches in the subject string, in a similar   *
+* way to the /g option in Perl. This turns out to be trickier than you   *
+* might think because of the possibility of matching an empty string.    *
+* What happens is as follows:                                            *
+*                                                                        *
+* If the previous match was NOT for an empty string, we can just start   *
+* the next match at the end of the previous one.                         *
+*                                                                        *
+* If the previous match WAS for an empty string, we can't do that, as it *
+* would lead to an infinite loop. Instead, a special call of pcre_exec() *
+* is made with the PCRE_NOTEMPTY and PCRE_ANCHORED flags set. The first  *
+* of these tells PCRE that an empty string is not a valid match; other   *
+* possibilities must be tried. The second flag restricts PCRE to one     *
+* match attempt at the initial string position. If this match succeeds,  *
+* an alternative to the empty string match has been found, and we can    *
+* proceed round the loop.                                                *
+*************************************************************************/
+
+if (!find_all)
+  {
+  free(re);   /* Release the memory used for the compiled pattern */
+  return 0;   /* Finish unless -g was given */
+  }
+
+/* Loop for second and subsequent matches */
+
+for (;;)
+  {
+  int options = 0;                 /* Normally no options */
+  int start_offset = ovector[1];   /* Start at end of previous match */
+
+  /* If the previous match was for an empty string, we are finished if we are
+  at the end of the subject. Otherwise, arrange to run another match at the
+  same point to see if a non-empty match can be found. */
+
+  if (ovector[0] == ovector[1])
+    {
+    if (ovector[0] == subject_length) break;
+    options = PCRE_NOTEMPTY | PCRE_ANCHORED;
+    }
+
+  /* Run the next matching operation */
+
+  rc = pcre_exec(
+    re,                   /* the compiled pattern */
+    NULL,                 /* no extra data - we didn't study the pattern */
+    subject,              /* the subject string */
+    subject_length,       /* the length of the subject */
+    start_offset,         /* starting offset in the subject */
+    options,              /* options */
+    ovector,              /* output vector for substring information */
+    OVECCOUNT);           /* number of elements in the output vector */
+
+  /* This time, a result of NOMATCH isn't an error. If the value in "options"
+  is zero, it just means we have found all possible matches, so the loop ends.
+  Otherwise, it means we have failed to find a non-empty-string match at a
+  point where there was a previous empty-string match. In this case, we do what
+  Perl does: advance the matching position by one, and continue. We do this by
+  setting the "end of previous match" offset, because that is picked up at the
+  top of the loop as the point at which to start again. */
+
+  if (rc == PCRE_ERROR_NOMATCH)
+    {
+    if (options == 0) break;
+    ovector[1] = start_offset + 1;
+    continue;    /* Go round the loop again */
+    }
+
+  /* Other matching errors are not recoverable. */
+
+  if (rc < 0)
+    {
+    printf("Matching error %d\n", rc);
+    free(re);    /* Release memory used for the compiled pattern */
+    return 1;
+    }
+
+  /* Match succeded */
+
+  printf("\nMatch succeeded again at offset %d\n", ovector[0]);
+
+  /* The match succeeded, but the output vector wasn't big enough. */
+
+  if (rc == 0)
+    {
+    rc = OVECCOUNT/3;
+    printf("ovector only has room for %d captured substrings\n", rc - 1);
+    }
+
+  /* As before, show substrings stored in the output vector by number, and then
+  also any named substrings. */
+
+  for (i = 0; i < rc; i++)
+    {
+    char *substring_start = subject + ovector[2*i];
+    int substring_length = ovector[2*i+1] - ovector[2*i];
+    printf("%2d: %.*s\n", i, substring_length, substring_start);
+    }
+
+  if (namecount <= 0) printf("No named substrings\n"); else
+    {
+    unsigned char *tabptr = name_table;
+    printf("Named substrings\n");
+    for (i = 0; i < namecount; i++)
+      {
+      int n = (tabptr[0] << 8) | tabptr[1];
+      printf("(%d) %*s: %.*s\n", n, name_entry_size - 3, tabptr + 2,
+        ovector[2*n+1] - ovector[2*n], subject + ovector[2*n]);
+      tabptr += name_entry_size;
+      }
+    }
+  }      /* End of loop to find second and subsequent matches */
+
+printf("\n");
+free(re);       /* Release memory used for the compiled pattern */
+return 0;
+}
+
+/* End of pcredemo.c */
diff --git a/connectors/jk/native/iis/pcre/pcregrep.c b/connectors/jk/native/iis/pcre/pcregrep.c
new file mode 100644
index 0000000..21b2a9b
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/pcregrep.c
@@ -0,0 +1,673 @@
+/*************************************************
+*               pcregrep program                 *
+*************************************************/
+
+/* This is a grep program that uses the PCRE regular expression library to do
+its pattern matching. On a Unix or Win32 system it can recurse into
+directories.
+
+           Copyright (c) 1997-2004 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+    * Neither the name of the University of Cambridge nor the names of its
+      contributors may be used to endorse or promote products derived from
+      this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "config.h"
+#include "pcre.h"
+
+#define FALSE 0
+#define TRUE 1
+
+typedef int BOOL;
+
+#define VERSION "3.0 14-Jan-2003"
+#define MAX_PATTERN_COUNT 100
+
+
+/*************************************************
+*               Global variables                 *
+*************************************************/
+
+static char *pattern_filename = NULL;
+static int  pattern_count = 0;
+static pcre **pattern_list;
+static pcre_extra **hints_list;
+
+static BOOL count_only = FALSE;
+static BOOL filenames = TRUE;
+static BOOL filenames_only = FALSE;
+static BOOL invert = FALSE;
+static BOOL number = FALSE;
+static BOOL recurse = FALSE;
+static BOOL silent = FALSE;
+static BOOL whole_lines = FALSE;
+
+/* Structure for options and list of them */
+
+typedef struct option_item {
+  int one_char;
+  const char *long_name;
+  const char *help_text;
+} option_item;
+
+static option_item optionlist[] = {
+  { -1,  "help",         "display this help and exit" },
+  { 'c', "count",        "print only a count of matching lines per FILE" },
+  { 'h', "no-filename",  "suppress the prefixing filename on output" },
+  { 'i', "ignore-case",  "ignore case distinctions" },
+  { 'l', "files-with-matches", "print only FILE names containing matches" },
+  { 'n', "line-number",  "print line number with output lines" },
+  { 'r', "recursive",    "recursively scan sub-directories" },
+  { 's', "no-messages",  "suppress error messages" },
+  { 'u', "utf-8",        "use UTF-8 mode" },
+  { 'V', "version",      "print version information and exit" },
+  { 'v', "invert-match", "select non-matching lines" },
+  { 'x', "line-regex",   "force PATTERN to match only whole lines" },
+  { 'x', "line-regexp",  "force PATTERN to match only whole lines" },
+  { 0,    NULL,           NULL }
+};
+
+
+/*************************************************
+*       Functions for directory scanning         *
+*************************************************/
+
+/* These functions are defined so that they can be made system specific,
+although at present the only ones are for Unix, Win32, and for "no directory
+recursion support". */
+
+
+/************* Directory scanning in Unix ***********/
+
+#if IS_UNIX
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+
+typedef DIR directory_type;
+
+static int
+isdirectory(char *filename)
+{
+struct stat statbuf;
+if (stat(filename, &statbuf) < 0)
+  return 0;        /* In the expectation that opening as a file will fail */
+return ((statbuf.st_mode & S_IFMT) == S_IFDIR)? '/' : 0;
+}
+
+static directory_type *
+opendirectory(char *filename)
+{
+return opendir(filename);
+}
+
+static char *
+readdirectory(directory_type *dir)
+{
+for (;;)
+  {
+  struct dirent *dent = readdir(dir);
+  if (dent == NULL) return NULL;
+  if (strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0)
+    return dent->d_name;
+  }
+return NULL;   /* Keep compiler happy; never executed */
+}
+
+static void
+closedirectory(directory_type *dir)
+{
+closedir(dir);
+}
+
+
+/************* Directory scanning in Win32 ***********/
+
+/* I (Philip Hazel) have no means of testing this code. It was contributed by
+Lionel Fourquaux. */
+
+
+#elif HAVE_WIN32API
+
+#ifndef STRICT
+# define STRICT
+#endif
+#ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+
+typedef struct directory_type
+{
+HANDLE handle;
+BOOL first;
+WIN32_FIND_DATA data;
+} directory_type;
+
+int
+isdirectory(char *filename)
+{
+DWORD attr = GetFileAttributes(filename);
+if (attr == INVALID_FILE_ATTRIBUTES)
+  return 0;
+return ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) ? '/' : 0;
+}
+
+directory_type *
+opendirectory(char *filename)
+{
+size_t len;
+char *pattern;
+directory_type *dir;
+DWORD err;
+len = strlen(filename);
+pattern = (char *) malloc(len + 3);
+dir = (directory_type *) malloc(sizeof(*dir));
+if ((pattern == NULL) || (dir == NULL))
+  {
+  fprintf(stderr, "pcregrep: malloc failed\n");
+  exit(2);
+  }
+memcpy(pattern, filename, len);
+memcpy(&(pattern[len]), "\\*", 3);
+dir->handle = FindFirstFile(pattern, &(dir->data));
+if (dir->handle != INVALID_HANDLE_VALUE)
+  {
+  free(pattern);
+  dir->first = TRUE;
+  return dir;
+  }
+err = GetLastError();
+free(pattern);
+free(dir);
+errno = (err == ERROR_ACCESS_DENIED) ? EACCES : ENOENT;
+return NULL;
+}
+
+char *
+readdirectory(directory_type *dir)
+{
+for (;;)
+  {
+  if (!dir->first)
+    {
+    if (!FindNextFile(dir->handle, &(dir->data)))
+      return NULL;
+    }
+  else
+    {
+    dir->first = FALSE;
+    }
+  if (strcmp(dir->data.cFileName, ".") != 0 && strcmp(dir->data.cFileName, "..") != 0)
+    return dir->data.cFileName;
+  }
+#ifndef _MSC_VER
+return NULL;   /* Keep compiler happy; never executed */
+#endif
+}
+
+void
+closedirectory(directory_type *dir)
+{
+FindClose(dir->handle);
+free(dir);
+}
+
+
+/************* Directory scanning when we can't do it ***********/
+
+/* The type is void, and apart from isdirectory(), the functions do nothing. */
+
+#else
+
+typedef void directory_type;
+
+int isdirectory(char *filename) { return FALSE; }
+directory_type * opendirectory(char *filename) {}
+char *readdirectory(directory_type *dir) {}
+void closedirectory(directory_type *dir) {}
+
+#endif
+
+
+
+#if ! HAVE_STRERROR
+/*************************************************
+*     Provide strerror() for non-ANSI libraries  *
+*************************************************/
+
+/* Some old-fashioned systems still around (e.g. SunOS4) don't have strerror()
+in their libraries, but can provide the same facility by this simple
+alternative function. */
+
+extern int   sys_nerr;
+extern char *sys_errlist[];
+
+char *
+strerror(int n)
+{
+if (n < 0 || n >= sys_nerr) return "unknown error number";
+return sys_errlist[n];
+}
+#endif /* HAVE_STRERROR */
+
+
+
+/*************************************************
+*              Grep an individual file           *
+*************************************************/
+
+static int
+pcregrep(FILE *in, char *name)
+{
+int rc = 1;
+int linenumber = 0;
+int count = 0;
+int offsets[99];
+char buffer[BUFSIZ];
+
+while (fgets(buffer, sizeof(buffer), in) != NULL)
+  {
+  BOOL match = FALSE;
+  int i;
+  int length = (int)strlen(buffer);
+  if (length > 0 && buffer[length-1] == '\n') buffer[--length] = 0;
+  linenumber++;
+
+  for (i = 0; !match && i < pattern_count; i++)
+    {
+    match = pcre_exec(pattern_list[i], hints_list[i], buffer, length, 0, 0,
+      offsets, 99) >= 0;
+    if (match && whole_lines && offsets[1] != length) match = FALSE;
+    }
+
+  if (match != invert)
+    {
+    if (count_only) count++;
+
+    else if (filenames_only)
+      {
+      fprintf(stdout, "%s\n", (name == NULL)? "<stdin>" : name);
+      return 0;
+      }
+
+    else if (silent) return 0;
+
+    else
+      {
+      if (name != NULL) fprintf(stdout, "%s:", name);
+      if (number) fprintf(stdout, "%d:", linenumber);
+      fprintf(stdout, "%s\n", buffer);
+      }
+
+    rc = 0;
+    }
+  }
+
+if (count_only)
+  {
+  if (name != NULL) fprintf(stdout, "%s:", name);
+  fprintf(stdout, "%d\n", count);
+  }
+
+return rc;
+}
+
+
+
+
+/*************************************************
+*     Grep a file or recurse into a directory    *
+*************************************************/
+
+static int
+grep_or_recurse(char *filename, BOOL dir_recurse, BOOL show_filenames,
+  BOOL only_one_at_top)
+{
+int rc = 1;
+int sep;
+FILE *in;
+
+/* If the file is a directory and we are recursing, scan each file within it.
+The scanning code is localized so it can be made system-specific. */
+
+if ((sep = isdirectory(filename)) != 0 && dir_recurse)
+  {
+  char buffer[1024];
+  char *nextfile;
+  directory_type *dir = opendirectory(filename);
+
+  if (dir == NULL)
+    {
+    fprintf(stderr, "pcregrep: Failed to open directory %s: %s\n", filename,
+      strerror(errno));
+    return 2;
+    }
+
+  while ((nextfile = readdirectory(dir)) != NULL)
+    {
+    int frc;
+    sprintf(buffer, "%.512s%c%.128s", filename, sep, nextfile);
+    frc = grep_or_recurse(buffer, dir_recurse, TRUE, FALSE);
+    if (frc == 0 && rc == 1) rc = 0;
+    }
+
+  closedirectory(dir);
+  return rc;
+  }
+
+/* If the file is not a directory, or we are not recursing, scan it. If this is
+the first and only argument at top level, we don't show the file name (unless
+we are only showing the file name). Otherwise, control is via the
+show_filenames variable. */
+
+in = fopen(filename, "r");
+if (in == NULL)
+  {
+  fprintf(stderr, "pcregrep: Failed to open %s: %s\n", filename, strerror(errno));
+  return 2;
+  }
+
+rc = pcregrep(in, (filenames_only || (show_filenames && !only_one_at_top))?
+  filename : NULL);
+fclose(in);
+return rc;
+}
+
+
+
+
+/*************************************************
+*                Usage function                  *
+*************************************************/
+
+static int
+usage(int rc)
+{
+fprintf(stderr, "Usage: pcregrep [-Vcfhilnrsvx] [long-options] [pattern] [file1 file2 ...]\n");
+fprintf(stderr, "Type `pcregrep --help' for more information.\n");
+return rc;
+}
+
+
+
+
+/*************************************************
+*                Help function                   *
+*************************************************/
+
+static void
+help(void)
+{
+option_item *op;
+
+printf("Usage: pcregrep [OPTION]... [PATTERN] [FILE1 FILE2 ...]\n");
+printf("Search for PATTERN in each FILE or standard input.\n");
+printf("PATTERN must be present if -f is not used.\n");
+printf("Example: pcregrep -i 'hello.*world' menu.h main.c\n\n");
+
+printf("Options:\n");
+
+for (op = optionlist; op->one_char != 0; op++)
+  {
+  int n;
+  char s[4];
+  if (op->one_char > 0) sprintf(s, "-%c,", op->one_char); else strcpy(s, "   ");
+  printf("  %s --%s%n", s, op->long_name, &n);
+  n = 30 - n;
+  if (n < 1) n = 1;
+  printf("%.*s%s\n", n, "                    ", op->help_text);
+  }
+
+printf("\n  -f<filename>  or  --file=<filename>\n");
+printf("    Read patterns from <filename> instead of using a command line option.\n");
+printf("    Trailing white space is removed; blanks lines are ignored.\n");
+printf("    There is a maximum of %d patterns.\n", MAX_PATTERN_COUNT);
+
+printf("\nWith no FILE, read standard input. If fewer than two FILEs given, assume -h.\n");
+printf("Exit status is 0 if any matches, 1 if no matches, and 2 if trouble.\n");
+}
+
+
+
+
+/*************************************************
+*                Handle an option                *
+*************************************************/
+
+static int
+handle_option(int letter, int options)
+{
+switch(letter)
+  {
+  case -1:  help(); exit(0);
+  case 'c': count_only = TRUE; break;
+  case 'h': filenames = FALSE; break;
+  case 'i': options |= PCRE_CASELESS; break;
+  case 'l': filenames_only = TRUE;
+  case 'n': number = TRUE; break;
+  case 'r': recurse = TRUE; break;
+  case 's': silent = TRUE; break;
+  case 'u': options |= PCRE_UTF8; break;
+  case 'v': invert = TRUE; break;
+  case 'x': whole_lines = TRUE; options |= PCRE_ANCHORED; break;
+
+  case 'V':
+  fprintf(stderr, "pcregrep version %s using ", VERSION);
+  fprintf(stderr, "PCRE version %s\n", pcre_version());
+  exit(0);
+  break;
+
+  default:
+  fprintf(stderr, "pcregrep: Unknown option -%c\n", letter);
+  exit(usage(2));
+  }
+
+return options;
+}
+
+
+
+
+/*************************************************
+*                Main program                    *
+*************************************************/
+
+int
+main(int argc, char **argv)
+{
+int i, j;
+int rc = 1;
+int options = 0;
+int errptr;
+const char *error;
+BOOL only_one_at_top;
+
+/* Process the options */
+
+for (i = 1; i < argc; i++)
+  {
+  if (argv[i][0] != '-') break;
+
+  /* Missing options */
+
+  if (argv[i][1] == 0) exit(usage(2));
+
+  /* Long name options */
+
+  if (argv[i][1] == '-')
+    {
+    option_item *op;
+
+    if (strncmp(argv[i]+2, "file=", 5) == 0)
+      {
+      pattern_filename = argv[i] + 7;
+      continue;
+      }
+
+    for (op = optionlist; op->one_char != 0; op++)
+      {
+      if (strcmp(argv[i]+2, op->long_name) == 0)
+        {
+        options = handle_option(op->one_char, options);
+        break;
+        }
+      }
+    if (op->one_char == 0)
+      {
+      fprintf(stderr, "pcregrep: Unknown option %s\n", argv[i]);
+      exit(usage(2));
+      }
+    }
+
+  /* One-char options */
+
+  else
+    {
+    char *s = argv[i] + 1;
+    while (*s != 0)
+      {
+      if (*s == 'f')
+        {
+        pattern_filename = s + 1;
+        if (pattern_filename[0] == 0)
+          {
+          if (i >= argc - 1)
+            {
+            fprintf(stderr, "pcregrep: File name missing after -f\n");
+            exit(usage(2));
+            }
+          pattern_filename = argv[++i];
+          }
+        break;
+        }
+      else options = handle_option(*s++, options);
+      }
+    }
+  }
+
+pattern_list = (pcre **)malloc(MAX_PATTERN_COUNT * sizeof(pcre *));
+hints_list = (pcre_extra **)malloc(MAX_PATTERN_COUNT * sizeof(pcre_extra *));
+
+if (pattern_list == NULL || hints_list == NULL)
+  {
+  fprintf(stderr, "pcregrep: malloc failed\n");
+  return 2;
+  }
+
+/* Compile the regular expression(s). */
+
+if (pattern_filename != NULL)
+  {
+  FILE *f = fopen(pattern_filename, "r");
+  char buffer[BUFSIZ];
+  if (f == NULL)
+    {
+    fprintf(stderr, "pcregrep: Failed to open %s: %s\n", pattern_filename,
+      strerror(errno));
+    return 2;
+    }
+  while (fgets(buffer, sizeof(buffer), f) != NULL)
+    {
+    char *s = buffer + (int)strlen(buffer);
+    if (pattern_count >= MAX_PATTERN_COUNT)
+      {
+      fprintf(stderr, "pcregrep: Too many patterns in file (max %d)\n",
+        MAX_PATTERN_COUNT);
+      return 2;
+      }
+    while (s > buffer && isspace((unsigned char)(s[-1]))) s--;
+    if (s == buffer) continue;
+    *s = 0;
+    pattern_list[pattern_count] = pcre_compile(buffer, options, &error,
+      &errptr, NULL);
+    if (pattern_list[pattern_count++] == NULL)
+      {
+      fprintf(stderr, "pcregrep: Error in regex number %d at offset %d: %s\n",
+        pattern_count, errptr, error);
+      return 2;
+      }
+    }
+  fclose(f);
+  }
+
+/* If no file name, a single regex must be given inline */
+
+else
+  {
+  if (i >= argc) return usage(2);
+  pattern_list[0] = pcre_compile(argv[i++], options, &error, &errptr, NULL);
+  if (pattern_list[0] == NULL)
+    {
+    fprintf(stderr, "pcregrep: Error in regex at offset %d: %s\n", errptr,
+      error);
+    return 2;
+    }
+  pattern_count++;
+  }
+
+/* Study the regular expressions, as we will be running them may times */
+
+for (j = 0; j < pattern_count; j++)
+  {
+  hints_list[j] = pcre_study(pattern_list[j], 0, &error);
+  if (error != NULL)
+    {
+    char s[16];
+    if (pattern_count == 1) s[0] = 0; else sprintf(s, " number %d", j);
+    fprintf(stderr, "pcregrep: Error while studying regex%s: %s\n", s, error);
+    return 2;
+    }
+  }
+
+/* If there are no further arguments, do the business on stdin and exit */
+
+if (i >= argc) return pcregrep(stdin, NULL);
+
+/* Otherwise, work through the remaining arguments as files or directories.
+Pass in the fact that there is only one argument at top level - this suppresses
+the file name if the argument is not a directory. */
+
+only_one_at_top = (i == argc - 1);
+if (filenames_only) filenames = TRUE;
+
+for (; i < argc; i++)
+  {
+  int frc = grep_or_recurse(argv[i], recurse, filenames, only_one_at_top);
+  if (frc == 0 && rc == 1) rc = 0;
+  }
+
+return rc;
+}
+
+/* End */
diff --git a/connectors/jk/native/iis/pcre/pcreposix.c b/connectors/jk/native/iis/pcre/pcreposix.c
new file mode 100644
index 0000000..1e8b6a7
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/pcreposix.c
@@ -0,0 +1,316 @@
+/*************************************************
+*      Perl-Compatible Regular Expressions       *
+*************************************************/
+
+/*
+This is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language. See
+the file Tech.Notes for some information on the internals.
+
+This module is a wrapper that provides a POSIX API to the underlying PCRE
+functions.
+
+Written by: Philip Hazel <ph10@cam.ac.uk>
+
+           Copyright (c) 1997-2004 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+    * Neither the name of the University of Cambridge nor the names of its
+      contributors may be used to endorse or promote products derived from
+      this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+#include "internal.h"
+#include "pcreposix.h"
+#include "stdlib.h"
+
+
+
+/* Corresponding tables of PCRE error messages and POSIX error codes. */
+
+static const char *const estring[] = {
+  ERR1,  ERR2,  ERR3,  ERR4,  ERR5,  ERR6,  ERR7,  ERR8,  ERR9,  ERR10,
+  ERR11, ERR12, ERR13, ERR14, ERR15, ERR16, ERR17, ERR18, ERR19, ERR20,
+  ERR21, ERR22, ERR23, ERR24, ERR25, ERR26, ERR27, ERR29, ERR29, ERR30,
+  ERR31, ERR32, ERR33, ERR34, ERR35, ERR36, ERR37, ERR38, ERR39, ERR40,
+  ERR41, ERR42, ERR43, ERR44, ERR45, ERR46, ERR47 };
+
+static const int eint[] = {
+  REG_EESCAPE, /* "\\ at end of pattern" */
+  REG_EESCAPE, /* "\\c at end of pattern" */
+  REG_EESCAPE, /* "unrecognized character follows \\" */
+  REG_BADBR,   /* "numbers out of order in {} quantifier" */
+  REG_BADBR,   /* "number too big in {} quantifier" */
+  REG_EBRACK,  /* "missing terminating ] for character class" */
+  REG_ECTYPE,  /* "invalid escape sequence in character class" */
+  REG_ERANGE,  /* "range out of order in character class" */
+  REG_BADRPT,  /* "nothing to repeat" */
+  REG_BADRPT,  /* "operand of unlimited repeat could match the empty string" */
+  REG_ASSERT,  /* "internal error: unexpected repeat" */
+  REG_BADPAT,  /* "unrecognized character after (?" */
+  REG_BADPAT,  /* "POSIX named classes are supported only within a class" */
+  REG_EPAREN,  /* "missing )" */
+  REG_ESUBREG, /* "reference to non-existent subpattern" */
+  REG_INVARG,  /* "erroffset passed as NULL" */
+  REG_INVARG,  /* "unknown option bit(s) set" */
+  REG_EPAREN,  /* "missing ) after comment" */
+  REG_ESIZE,   /* "parentheses nested too deeply" */
+  REG_ESIZE,   /* "regular expression too large" */
+  REG_ESPACE,  /* "failed to get memory" */
+  REG_EPAREN,  /* "unmatched brackets" */
+  REG_ASSERT,  /* "internal error: code overflow" */
+  REG_BADPAT,  /* "unrecognized character after (?<" */
+  REG_BADPAT,  /* "lookbehind assertion is not fixed length" */
+  REG_BADPAT,  /* "malformed number after (?(" */
+  REG_BADPAT,  /* "conditional group containe more than two branches" */
+  REG_BADPAT,  /* "assertion expected after (?(" */
+  REG_BADPAT,  /* "(?R or (?digits must be followed by )" */
+  REG_ECTYPE,  /* "unknown POSIX class name" */
+  REG_BADPAT,  /* "POSIX collating elements are not supported" */
+  REG_INVARG,  /* "this version of PCRE is not compiled with PCRE_UTF8 support" */
+  REG_BADPAT,  /* "spare error" */
+  REG_BADPAT,  /* "character value in \x{...} sequence is too large" */
+  REG_BADPAT,  /* "invalid condition (?(0)" */
+  REG_BADPAT,  /* "\\C not allowed in lookbehind assertion" */
+  REG_EESCAPE, /* "PCRE does not support \\L, \\l, \\N, \\U, or \\u" */
+  REG_BADPAT,  /* "number after (?C is > 255" */
+  REG_BADPAT,  /* "closing ) for (?C expected" */
+  REG_BADPAT,  /* "recursive call could loop indefinitely" */
+  REG_BADPAT,  /* "unrecognized character after (?P" */
+  REG_BADPAT,  /* "syntax error after (?P" */
+  REG_BADPAT,  /* "two named groups have the same name" */
+  REG_BADPAT,  /* "invalid UTF-8 string" */
+  REG_BADPAT,  /* "support for \\P, \\p, and \\X has not been compiled" */
+  REG_BADPAT,  /* "malformed \\P or \\p sequence" */
+  REG_BADPAT   /* "unknown property name after \\P or \\p" */
+};
+
+/* Table of texts corresponding to POSIX error codes */
+
+static const char *const pstring[] = {
+  "",                                /* Dummy for value 0 */
+  "internal error",                  /* REG_ASSERT */
+  "invalid repeat counts in {}",     /* BADBR      */
+  "pattern error",                   /* BADPAT     */
+  "? * + invalid",                   /* BADRPT     */
+  "unbalanced {}",                   /* EBRACE     */
+  "unbalanced []",                   /* EBRACK     */
+  "collation error - not relevant",  /* ECOLLATE   */
+  "bad class",                       /* ECTYPE     */
+  "bad escape sequence",             /* EESCAPE    */
+  "empty expression",                /* EMPTY      */
+  "unbalanced ()",                   /* EPAREN     */
+  "bad range inside []",             /* ERANGE     */
+  "expression too big",              /* ESIZE      */
+  "failed to get memory",            /* ESPACE     */
+  "bad back reference",              /* ESUBREG    */
+  "bad argument",                    /* INVARG     */
+  "match failed"                     /* NOMATCH    */
+};
+
+
+
+
+/*************************************************
+*          Translate PCRE text code to int       *
+*************************************************/
+
+/* PCRE compile-time errors are given as strings defined as macros. We can just
+look them up in a table to turn them into POSIX-style error codes. */
+
+static int
+pcre_posix_error_code(const char *s)
+{
+size_t i;
+for (i = 0; i < sizeof(estring)/sizeof(char *); i++)
+  if (strcmp(s, estring[i]) == 0) return eint[i];
+return REG_ASSERT;
+}
+
+
+
+/*************************************************
+*          Translate error code to string        *
+*************************************************/
+
+EXPORT size_t
+regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size)
+{
+const char *message, *addmessage;
+size_t length, addlength;
+
+message = (errcode >= (int)(sizeof(pstring)/sizeof(char *)))?
+  "unknown error code" : pstring[errcode];
+length = strlen(message) + 1;
+
+addmessage = " at offset ";
+addlength = (preg != NULL && (int)preg->re_erroffset != -1)?
+  strlen(addmessage) + 6 : 0;
+
+if (errbuf_size > 0)
+  {
+  if (addlength > 0 && errbuf_size >= length + addlength)
+    sprintf(errbuf, "%s%s%-6d", message, addmessage, (int)preg->re_erroffset);
+  else
+    {
+    strncpy(errbuf, message, errbuf_size - 1);
+    errbuf[errbuf_size-1] = 0;
+    }
+  }
+
+return length + addlength;
+}
+
+
+
+
+/*************************************************
+*           Free store held by a regex           *
+*************************************************/
+
+EXPORT void
+regfree(regex_t *preg)
+{
+(pcre_free)(preg->re_pcre);
+}
+
+
+
+
+/*************************************************
+*            Compile a regular expression        *
+*************************************************/
+
+/*
+Arguments:
+  preg        points to a structure for recording the compiled expression
+  pattern     the pattern to compile
+  cflags      compilation flags
+
+Returns:      0 on success
+              various non-zero codes on failure
+*/
+
+EXPORT int
+regcomp(regex_t *preg, const char *pattern, int cflags)
+{
+const char *errorptr;
+int erroffset;
+int options = 0;
+
+if ((cflags & REG_ICASE) != 0) options |= PCRE_CASELESS;
+if ((cflags & REG_NEWLINE) != 0) options |= PCRE_MULTILINE;
+
+preg->re_pcre = pcre_compile(pattern, options, &errorptr, &erroffset, NULL);
+preg->re_erroffset = erroffset;
+
+if (preg->re_pcre == NULL) return pcre_posix_error_code(errorptr);
+
+preg->re_nsub = pcre_info((const pcre *)preg->re_pcre, NULL, NULL);
+return 0;
+}
+
+
+
+
+/*************************************************
+*              Match a regular expression        *
+*************************************************/
+
+/* Unfortunately, PCRE requires 3 ints of working space for each captured
+substring, so we have to get and release working store instead of just using
+the POSIX structures as was done in earlier releases when PCRE needed only 2
+ints. However, if the number of possible capturing brackets is small, use a
+block of store on the stack, to reduce the use of malloc/free. The threshold is
+in a macro that can be changed at configure time. */
+
+EXPORT int
+regexec(const regex_t *preg, const char *string, size_t nmatch,
+  regmatch_t pmatch[], int eflags)
+{
+int rc;
+int options = 0;
+int *ovector = NULL;
+int small_ovector[POSIX_MALLOC_THRESHOLD * 3];
+BOOL allocated_ovector = FALSE;
+
+if ((eflags & REG_NOTBOL) != 0) options |= PCRE_NOTBOL;
+if ((eflags & REG_NOTEOL) != 0) options |= PCRE_NOTEOL;
+
+((regex_t *)preg)->re_erroffset = (size_t)(-1);  /* Only has meaning after compile */
+
+if (nmatch > 0)
+  {
+  if (nmatch <= POSIX_MALLOC_THRESHOLD)
+    {
+    ovector = &(small_ovector[0]);
+    }
+  else
+    {
+    ovector = (int *)malloc(sizeof(int) * nmatch * 3);
+    if (ovector == NULL) return REG_ESPACE;
+    allocated_ovector = TRUE;
+    }
+  }
+
+rc = pcre_exec((const pcre *)preg->re_pcre, NULL, string, (int)strlen(string),
+  0, options, ovector, nmatch * 3);
+
+if (rc == 0) rc = nmatch;    /* All captured slots were filled in */
+
+if (rc >= 0)
+  {
+  size_t i;
+  for (i = 0; i < (size_t)rc; i++)
+    {
+    pmatch[i].rm_so = ovector[i*2];
+    pmatch[i].rm_eo = ovector[i*2+1];
+    }
+  if (allocated_ovector) free(ovector);
+  for (; i < nmatch; i++) pmatch[i].rm_so = pmatch[i].rm_eo = -1;
+  return 0;
+  }
+
+else
+  {
+  if (allocated_ovector) free(ovector);
+  switch(rc)
+    {
+    case PCRE_ERROR_NOMATCH: return REG_NOMATCH;
+    case PCRE_ERROR_NULL: return REG_INVARG;
+    case PCRE_ERROR_BADOPTION: return REG_INVARG;
+    case PCRE_ERROR_BADMAGIC: return REG_INVARG;
+    case PCRE_ERROR_UNKNOWN_NODE: return REG_ASSERT;
+    case PCRE_ERROR_NOMEMORY: return REG_ESPACE;
+    case PCRE_ERROR_MATCHLIMIT: return REG_ESPACE;
+    case PCRE_ERROR_BADUTF8: return REG_INVARG;
+    case PCRE_ERROR_BADUTF8_OFFSET: return REG_INVARG;
+    default: return REG_ASSERT;
+    }
+  }
+}
+
+/* End of pcreposix.c */
diff --git a/connectors/jk/native/iis/pcre/pcreposix.dsp b/connectors/jk/native/iis/pcre/pcreposix.dsp
new file mode 100644
index 0000000..e3cdfc4
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/pcreposix.dsp
@@ -0,0 +1,154 @@
+# Microsoft Developer Studio Project File - Name="pcreposix" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=pcreposix - 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 "pcreposix.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 "pcreposix.mak" CFG="pcreposix - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "pcreposix - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "pcreposix - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "pcreposix - 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 "LibR"
+# PROP Intermediate_Dir "LibR"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "_WIN32" /D "NDEBUG" /D "_WINDOWS" /D "PCRE_STATIC" /FD /c
+# ADD CPP /nologo /MD /W3 /Zi /O2 /Oy- /D "_WIN32" /D "NDEBUG" /D "_WINDOWS" /D "PCRE_STATIC" /Fd"LibR/pcreposix_src" /FD /c
+# ADD BASE RSC /l 0x409
+# ADD RSC /l 0x409
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF  "$(CFG)" == "pcreposix - 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 "LibD"
+# PROP Intermediate_Dir "LibD"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /Zi /Od /D "_WIN32" /D "_DEBUG" /D "_WINDOWS" /D "PCRE_STATIC" /FD /EHsc /c
+# ADD CPP /nologo /MDd /W3 /Zi /Od /I "..\..\include" /D "_WIN32" /D "_DEBUG" /D "_WINDOWS" /D "PCRE_STATIC" /Fd"LibD/pcreposix_src" /FD /EHsc /c
+# ADD BASE RSC /l 0x409
+# ADD RSC /l 0x409
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ENDIF 
+
+# Begin Target
+
+# Name "pcreposix - Win32 Release"
+# Name "pcreposix - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "*.c"
+# Begin Source File
+
+SOURCE=.\pcreposix.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "*.h"
+# Begin Source File
+
+SOURCE=.\config.hw
+
+!IF  "$(CFG)" == "pcreposix - Win32 Release"
+
+# Begin Custom Build - Creating pcre config.h from config.hw
+InputPath=.\config.hw
+
+".\config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	type .\config.hw > .\config.h
+
+# End Custom Build
+
+!ELSEIF  "$(CFG)" == "pcreposix - Win32 Debug"
+
+# Begin Custom Build - Creating pcre config.h from config.hw
+InputPath=.\config.hw
+
+".\config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	type .\config.hw > .\config.h
+
+# End Custom Build
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\pcre.hw
+
+!IF  "$(CFG)" == "pcreposix - Win32 Release"
+
+# Begin Custom Build - Creating pcre.h from pcre.hw
+InputPath=.\pcre.hw
+
+".\pcre.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	type .\pcre.hw > .\pcre.h
+
+# End Custom Build
+
+!ELSEIF  "$(CFG)" == "pcreposix - Win32 Debug"
+
+# Begin Custom Build - Creating pcre.h from pcre.hw
+InputPath=.\pcre.hw
+
+".\pcre.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	type .\pcre.hw > .\pcre.h
+
+# End Custom Build
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\pcreposix.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/connectors/jk/native/iis/pcre/pcreposix.h b/connectors/jk/native/iis/pcre/pcreposix.h
new file mode 100644
index 0000000..13e2784
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/pcreposix.h
@@ -0,0 +1,99 @@
+/*************************************************
+*       Perl-Compatible Regular Expressions      *
+*************************************************/
+
+/* Copyright (c) 1997-2000 University of Cambridge */
+
+/**
+ * @file include/pcreposix.h
+ * @brief PCRE definitions
+ */
+
+#ifndef _PCREPOSIX_H
+#define _PCREPOSIX_H
+
+/* This is the header for the POSIX wrapper interface to the PCRE Perl-
+Compatible Regular Expression library. It defines the things POSIX says should
+be there. I hope. */
+
+/* Have to include stdlib.h in order to ensure that size_t is defined. */
+
+#include <stdlib.h>
+
+/* Allow for C++ users */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Options defined by POSIX. */
+
+  /** Ignore case */
+#define REG_ICASE     0x01
+  /** Don't match newlines with wildcards */
+#define REG_NEWLINE   0x02
+  /** Don't match BOL */
+#define REG_NOTBOL    0x04
+  /** Don't match EOL */
+#define REG_NOTEOL    0x08
+
+/* These are not used by PCRE, but by defining them we make it easier
+to slot PCRE into existing programs that make POSIX calls. */
+
+  /** UNUSED! */
+#define REG_EXTENDED  0
+  /** UNUSED! */
+#define REG_NOSUB     0
+
+/* Error values. Not all these are relevant or used by the wrapper. */
+
+enum {
+  REG_ASSERT = 1,  /* internal error ? */
+  REG_BADBR,       /* invalid repeat counts in {} */
+  REG_BADPAT,      /* pattern error */
+  REG_BADRPT,      /* ? * + invalid */
+  REG_EBRACE,      /* unbalanced {} */
+  REG_EBRACK,      /* unbalanced [] */
+  REG_ECOLLATE,    /* collation error - not relevant */
+  REG_ECTYPE,      /* bad class */
+  REG_EESCAPE,     /* bad escape sequence */
+  REG_EMPTY,       /* empty expression */
+  REG_EPAREN,      /* unbalanced () */
+  REG_ERANGE,      /* bad range inside [] */
+  REG_ESIZE,       /* expression too big */
+  REG_ESPACE,      /* failed to get memory */
+  REG_ESUBREG,     /* bad back reference */
+  REG_INVARG,      /* bad argument */
+  REG_NOMATCH      /* match failed */
+};
+
+
+/* The structure representing a compiled regular expression. */
+
+typedef struct {
+  void *re_pcre;
+  size_t re_nsub;
+  size_t re_erroffset;
+} regex_t;
+
+/* The structure in which a captured offset is returned. */
+
+typedef int regoff_t;
+
+typedef struct {
+  regoff_t rm_so;
+  regoff_t rm_eo;
+} regmatch_t;
+
+/* The functions */
+
+extern int regcomp(regex_t *, const char *, int);
+extern int regexec(const regex_t *, const char *, size_t, regmatch_t *, int);
+extern size_t regerror(int, const regex_t *, char *, size_t);
+extern void regfree(regex_t *);
+
+#ifdef __cplusplus
+}   /* extern "C" */
+#endif
+
+#endif /* End of pcreposix.h */
diff --git a/connectors/jk/native/iis/pcre/pcretest.c b/connectors/jk/native/iis/pcre/pcretest.c
new file mode 100644
index 0000000..e531cc1
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/pcretest.c
@@ -0,0 +1,1786 @@
+/*************************************************
+*             PCRE testing program               *
+*************************************************/
+
+/* This program was hacked up as a tester for PCRE. I really should have
+written it more tidily in the first place. Will I ever learn? It has grown and
+been extended and consequently is now rather untidy in places.
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+    * Neither the name of the University of Cambridge nor the names of its
+      contributors may be used to endorse or promote products derived from
+      this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <locale.h>
+#include <errno.h>
+
+/* We need the internal info for displaying the results of pcre_study(). Also
+for getting the opcodes for showing compiled code. */
+
+#define PCRE_SPY        /* For Win32 build, import data, not export */
+#include "internal.h"
+
+/* It is possible to compile this test program without including support for
+testing the POSIX interface, though this is not available via the standard
+Makefile. */
+
+#if !defined NOPOSIX
+#include "pcreposix.h"
+#endif
+
+#ifndef CLOCKS_PER_SEC
+#ifdef CLK_TCK
+#define CLOCKS_PER_SEC CLK_TCK
+#else
+#define CLOCKS_PER_SEC 100
+#endif
+#endif
+
+#define LOOPREPEAT 500000
+
+#define BUFFER_SIZE 30000
+#define PBUFFER_SIZE BUFFER_SIZE
+#define DBUFFER_SIZE BUFFER_SIZE
+
+
+static FILE *outfile;
+static int log_store = 0;
+static int callout_count;
+static int callout_extra;
+static int callout_fail_count;
+static int callout_fail_id;
+static int first_callout;
+static int show_malloc;
+static int use_utf8;
+static size_t gotten_store;
+
+static uschar *pbuffer = NULL;
+
+
+static const int utf8_table1[] = {
+  0x0000007f, 0x000007ff, 0x0000ffff, 0x001fffff, 0x03ffffff, 0x7fffffff};
+
+static const int utf8_table2[] = {
+  0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc};
+
+static const int utf8_table3[] = {
+  0xff, 0x1f, 0x0f, 0x07, 0x03, 0x01};
+
+
+
+/*************************************************
+*         Print compiled regex                   *
+*************************************************/
+
+/* The code for doing this is held in a separate file that is also included in
+pcre.c when it is compiled with the debug switch. It defines a function called
+print_internals(), which uses a table of opcode lengths defined by the macro
+OP_LENGTHS, whose name must be OP_lengths. It also uses a table that translates
+Unicode property names to numbers; this is kept in a separate file. */
+
+static uschar OP_lengths[] = { OP_LENGTHS };
+
+#include "ucp.h"
+#include "ucptypetable.c"
+#include "printint.c"
+
+
+
+/*************************************************
+*          Read number from string               *
+*************************************************/
+
+/* We don't use strtoul() because SunOS4 doesn't have it. Rather than mess
+around with conditional compilation, just do the job by hand. It is only used
+for unpicking the -o argument, so just keep it simple.
+
+Arguments:
+  str           string to be converted
+  endptr        where to put the end pointer
+
+Returns:        the unsigned long
+*/
+
+static int
+get_value(unsigned char *str, unsigned char **endptr)
+{
+int result = 0;
+while(*str != 0 && isspace(*str)) str++;
+while (isdigit(*str)) result = result * 10 + (int)(*str++ - '0');
+*endptr = str;
+return(result);
+}
+
+
+
+/*************************************************
+*       Convert character value to UTF-8         *
+*************************************************/
+
+/* This function takes an integer value in the range 0 - 0x7fffffff
+and encodes it as a UTF-8 character in 0 to 6 bytes.
+
+Arguments:
+  cvalue     the character value
+  buffer     pointer to buffer for result - at least 6 bytes long
+
+Returns:     number of characters placed in the buffer
+             -1 if input character is negative
+             0 if input character is positive but too big (only when
+             int is longer than 32 bits)
+*/
+
+static int
+ord2utf8(int cvalue, unsigned char *buffer)
+{
+register int i, j;
+for (i = 0; i < sizeof(utf8_table1)/sizeof(int); i++)
+  if (cvalue <= utf8_table1[i]) break;
+if (i >= sizeof(utf8_table1)/sizeof(int)) return 0;
+if (cvalue < 0) return -1;
+
+buffer += i;
+for (j = i; j > 0; j--)
+ {
+ *buffer-- = 0x80 | (cvalue & 0x3f);
+ cvalue >>= 6;
+ }
+*buffer = utf8_table2[i] | cvalue;
+return i + 1;
+}
+
+
+/*************************************************
+*            Convert UTF-8 string to value       *
+*************************************************/
+
+/* This function takes one or more bytes that represents a UTF-8 character,
+and returns the value of the character.
+
+Argument:
+  buffer   a pointer to the byte vector
+  vptr     a pointer to an int to receive the value
+
+Returns:   >  0 => the number of bytes consumed
+           -6 to 0 => malformed UTF-8 character at offset = (-return)
+*/
+
+static int
+utf82ord(unsigned char *buffer, int *vptr)
+{
+int c = *buffer++;
+int d = c;
+int i, j, s;
+
+for (i = -1; i < 6; i++)               /* i is number of additional bytes */
+  {
+  if ((d & 0x80) == 0) break;
+  d <<= 1;
+  }
+
+if (i == -1) { *vptr = c; return 1; }  /* ascii character */
+if (i == 0 || i == 6) return 0;        /* invalid UTF-8 */
+
+/* i now has a value in the range 1-5 */
+
+s = 6*i;
+d = (c & utf8_table3[i]) << s;
+
+for (j = 0; j < i; j++)
+  {
+  c = *buffer++;
+  if ((c & 0xc0) != 0x80) return -(j+1);
+  s -= 6;
+  d |= (c & 0x3f) << s;
+  }
+
+/* Check that encoding was the correct unique one */
+
+for (j = 0; j < sizeof(utf8_table1)/sizeof(int); j++)
+  if (d <= utf8_table1[j]) break;
+if (j != i) return -(i+1);
+
+/* Valid value */
+
+*vptr = d;
+return i+1;
+}
+
+
+
+/*************************************************
+*             Print character string             *
+*************************************************/
+
+/* Character string printing function. Must handle UTF-8 strings in utf8
+mode. Yields number of characters printed. If handed a NULL file, just counts
+chars without printing. */
+
+static int pchars(unsigned char *p, int length, FILE *f)
+{
+int c;
+int yield = 0;
+
+while (length-- > 0)
+  {
+  if (use_utf8)
+    {
+    int rc = utf82ord(p, &c);
+
+    if (rc > 0 && rc <= length + 1)   /* Mustn't run over the end */
+      {
+      length -= rc - 1;
+      p += rc;
+      if (c < 256 && isprint(c))
+        {
+        if (f != NULL) fprintf(f, "%c", c);
+        yield++;
+        }
+      else
+        {
+        int n;
+        if (f != NULL) fprintf(f, "\\x{%02x}%n", c, &n);
+        yield += n;
+        }
+      continue;
+      }
+    }
+
+   /* Not UTF-8, or malformed UTF-8  */
+
+  if (isprint(c = *(p++)))
+    {
+    if (f != NULL) fprintf(f, "%c", c);
+    yield++;
+    }
+  else
+    {
+    if (f != NULL) fprintf(f, "\\x%02x", c);
+    yield += 4;
+    }
+  }
+
+return yield;
+}
+
+
+
+/*************************************************
+*              Callout function                  *
+*************************************************/
+
+/* Called from PCRE as a result of the (?C) item. We print out where we are in
+the match. Yield zero unless more callouts than the fail count, or the callout
+data is not zero. */
+
+static int callout(pcre_callout_block *cb)
+{
+FILE *f = (first_callout | callout_extra)? outfile : NULL;
+int i, pre_start, post_start, subject_length;
+
+if (callout_extra)
+  {
+  fprintf(f, "Callout %d: last capture = %d\n",
+    cb->callout_number, cb->capture_last);
+
+  for (i = 0; i < cb->capture_top * 2; i += 2)
+    {
+    if (cb->offset_vector[i] < 0)
+      fprintf(f, "%2d: <unset>\n", i/2);
+    else
+      {
+      fprintf(f, "%2d: ", i/2);
+      (void)pchars((unsigned char *)cb->subject + cb->offset_vector[i],
+        cb->offset_vector[i+1] - cb->offset_vector[i], f);
+      fprintf(f, "\n");
+      }
+    }
+  }
+
+/* Re-print the subject in canonical form, the first time or if giving full
+datails. On subsequent calls in the same match, we use pchars just to find the
+printed lengths of the substrings. */
+
+if (f != NULL) fprintf(f, "--->");
+
+pre_start = pchars((unsigned char *)cb->subject, cb->start_match, f);
+post_start = pchars((unsigned char *)(cb->subject + cb->start_match),
+  cb->current_position - cb->start_match, f);
+
+subject_length = pchars((unsigned char *)cb->subject, cb->subject_length, NULL);
+
+(void)pchars((unsigned char *)(cb->subject + cb->current_position),
+  cb->subject_length - cb->current_position, f);
+
+if (f != NULL) fprintf(f, "\n");
+
+/* Always print appropriate indicators, with callout number if not already
+shown. For automatic callouts, show the pattern offset. */
+
+if (cb->callout_number == 255)
+  {
+  fprintf(outfile, "%+3d ", cb->pattern_position);
+  if (cb->pattern_position > 99) fprintf(outfile, "\n    ");
+  }
+else
+  {
+  if (callout_extra) fprintf(outfile, "    ");
+    else fprintf(outfile, "%3d ", cb->callout_number);
+  }
+
+for (i = 0; i < pre_start; i++) fprintf(outfile, " ");
+fprintf(outfile, "^");
+
+if (post_start > 0)
+  {
+  for (i = 0; i < post_start - 1; i++) fprintf(outfile, " ");
+  fprintf(outfile, "^");
+  }
+
+for (i = 0; i < subject_length - pre_start - post_start + 4; i++)
+  fprintf(outfile, " ");
+
+fprintf(outfile, "%.*s", (cb->next_item_length == 0)? 1 : cb->next_item_length,
+  pbuffer + cb->pattern_position);
+
+fprintf(outfile, "\n");
+first_callout = 0;
+
+if (cb->callout_data != NULL)
+  {
+  int callout_data = *((int *)(cb->callout_data));
+  if (callout_data != 0)
+    {
+    fprintf(outfile, "Callout data = %d\n", callout_data);
+    return callout_data;
+    }
+  }
+
+return (cb->callout_number != callout_fail_id)? 0 :
+       (++callout_count >= callout_fail_count)? 1 : 0;
+}
+
+
+/*************************************************
+*            Local malloc functions              *
+*************************************************/
+
+/* Alternative malloc function, to test functionality and show the size of the
+compiled re. */
+
+static void *new_malloc(size_t size)
+{
+void *block = malloc(size);
+gotten_store = size;
+if (show_malloc)
+  fprintf(outfile, "malloc       %3d %p\n", size, block);
+return block;
+}
+
+static void new_free(void *block)
+{
+if (show_malloc)
+  fprintf(outfile, "free             %p\n", block);
+free(block);
+}
+
+
+/* For recursion malloc/free, to test stacking calls */
+
+static void *stack_malloc(size_t size)
+{
+void *block = malloc(size);
+if (show_malloc)
+  fprintf(outfile, "stack_malloc %3d %p\n", size, block);
+return block;
+}
+
+static void stack_free(void *block)
+{
+if (show_malloc)
+  fprintf(outfile, "stack_free       %p\n", block);
+free(block);
+}
+
+
+/*************************************************
+*          Call pcre_fullinfo()                  *
+*************************************************/
+
+/* Get one piece of information from the pcre_fullinfo() function */
+
+static void new_info(pcre *re, pcre_extra *study, int option, void *ptr)
+{
+int rc;
+if ((rc = pcre_fullinfo(re, study, option, ptr)) < 0)
+  fprintf(outfile, "Error %d from pcre_fullinfo(%d)\n", rc, option);
+}
+
+
+
+/*************************************************
+*         Byte flipping function                 *
+*************************************************/
+
+static long int
+byteflip(long int value, int n)
+{
+if (n == 2) return ((value & 0x00ff) << 8) | ((value & 0xff00) >> 8);
+return ((value & 0x000000ff) << 24) |
+       ((value & 0x0000ff00) <<  8) |
+       ((value & 0x00ff0000) >>  8) |
+       ((value & 0xff000000) >> 24);
+}
+
+
+
+
+/*************************************************
+*                Main Program                    *
+*************************************************/
+
+/* Read lines from named file or stdin and write to named file or stdout; lines
+consist of a regular expression, in delimiters and optionally followed by
+options, followed by a set of test data, terminated by an empty line. */
+
+int main(int argc, char **argv)
+{
+FILE *infile = stdin;
+int options = 0;
+int study_options = 0;
+int op = 1;
+int timeit = 0;
+int showinfo = 0;
+int showstore = 0;
+int size_offsets = 45;
+int size_offsets_max;
+int *offsets;
+#if !defined NOPOSIX
+int posix = 0;
+#endif
+int debug = 0;
+int done = 0;
+
+unsigned char *buffer;
+unsigned char *dbuffer;
+
+/* Get buffers from malloc() so that Electric Fence will check their misuse
+when I am debugging. */
+
+buffer = (unsigned char *)malloc(BUFFER_SIZE);
+dbuffer = (unsigned char *)malloc(DBUFFER_SIZE);
+pbuffer = (unsigned char *)malloc(PBUFFER_SIZE);
+
+/* The outfile variable is static so that new_malloc can use it. The _setmode()
+stuff is some magic that I don't understand, but which apparently does good
+things in Windows. It's related to line terminations.  */
+
+#if defined(_WIN32) || defined(WIN32)
+_setmode( _fileno( stdout ), 0x8000 );
+#endif  /* defined(_WIN32) || defined(WIN32) */
+
+outfile = stdout;
+
+/* Scan options */
+
+while (argc > 1 && argv[op][0] == '-')
+  {
+  unsigned char *endptr;
+
+  if (strcmp(argv[op], "-s") == 0 || strcmp(argv[op], "-m") == 0)
+    showstore = 1;
+  else if (strcmp(argv[op], "-t") == 0) timeit = 1;
+  else if (strcmp(argv[op], "-i") == 0) showinfo = 1;
+  else if (strcmp(argv[op], "-d") == 0) showinfo = debug = 1;
+  else if (strcmp(argv[op], "-o") == 0 && argc > 2 &&
+      ((size_offsets = get_value((unsigned char *)argv[op+1], &endptr)),
+        *endptr == 0))
+    {
+    op++;
+    argc--;
+    }
+#if !defined NOPOSIX
+  else if (strcmp(argv[op], "-p") == 0) posix = 1;
+#endif
+  else if (strcmp(argv[op], "-C") == 0)
+    {
+    int rc;
+    printf("PCRE version %s\n", pcre_version());
+    printf("Compiled with\n");
+    (void)pcre_config(PCRE_CONFIG_UTF8, &rc);
+    printf("  %sUTF-8 support\n", rc? "" : "No ");
+    (void)pcre_config(PCRE_CONFIG_UNICODE_PROPERTIES, &rc);
+    printf("  %sUnicode properties support\n", rc? "" : "No ");
+    (void)pcre_config(PCRE_CONFIG_NEWLINE, &rc);
+    printf("  Newline character is %s\n", (rc == '\r')? "CR" : "LF");
+    (void)pcre_config(PCRE_CONFIG_LINK_SIZE, &rc);
+    printf("  Internal link size = %d\n", rc);
+    (void)pcre_config(PCRE_CONFIG_POSIX_MALLOC_THRESHOLD, &rc);
+    printf("  POSIX malloc threshold = %d\n", rc);
+    (void)pcre_config(PCRE_CONFIG_MATCH_LIMIT, &rc);
+    printf("  Default match limit = %d\n", rc);
+    (void)pcre_config(PCRE_CONFIG_STACKRECURSE, &rc);
+    printf("  Match recursion uses %s\n", rc? "stack" : "heap");
+    exit(0);
+    }
+  else
+    {
+    printf("** Unknown or malformed option %s\n", argv[op]);
+    printf("Usage:   pcretest [-d] [-i] [-o <n>] [-p] [-s] [-t] [<input> [<output>]]\n");
+    printf("  -C     show PCRE compile-time options and exit\n");
+    printf("  -d     debug: show compiled code; implies -i\n"
+           "  -i     show information about compiled pattern\n"
+           "  -m     output memory used information\n"
+           "  -o <n> set size of offsets vector to <n>\n");
+#if !defined NOPOSIX
+    printf("  -p     use POSIX interface\n");
+#endif
+    printf("  -s     output store (memory) used information\n"
+           "  -t     time compilation and execution\n");
+    return 1;
+    }
+  op++;
+  argc--;
+  }
+
+/* Get the store for the offsets vector, and remember what it was */
+
+size_offsets_max = size_offsets;
+offsets = (int *)malloc(size_offsets_max * sizeof(int));
+if (offsets == NULL)
+  {
+  printf("** Failed to get %d bytes of memory for offsets vector\n",
+    size_offsets_max * sizeof(int));
+  return 1;
+  }
+
+/* Sort out the input and output files */
+
+if (argc > 1)
+  {
+  infile = fopen(argv[op], "rb");
+  if (infile == NULL)
+    {
+    printf("** Failed to open %s\n", argv[op]);
+    return 1;
+    }
+  }
+
+if (argc > 2)
+  {
+  outfile = fopen(argv[op+1], "wb");
+  if (outfile == NULL)
+    {
+    printf("** Failed to open %s\n", argv[op+1]);
+    return 1;
+    }
+  }
+
+/* Set alternative malloc function */
+
+pcre_malloc = new_malloc;
+pcre_free = new_free;
+pcre_stack_malloc = stack_malloc;
+pcre_stack_free = stack_free;
+
+/* Heading line, then prompt for first regex if stdin */
+
+fprintf(outfile, "PCRE version %s\n\n", pcre_version());
+
+/* Main loop */
+
+while (!done)
+  {
+  pcre *re = NULL;
+  pcre_extra *extra = NULL;
+
+#if !defined NOPOSIX  /* There are still compilers that require no indent */
+  regex_t preg;
+  int do_posix = 0;
+#endif
+
+  const char *error;
+  unsigned char *p, *pp, *ppp;
+  unsigned char *to_file = NULL;
+  const unsigned char *tables = NULL;
+  unsigned long int true_size, true_study_size = 0;
+  size_t size, regex_gotten_store;
+  int do_study = 0;
+  int do_debug = debug;
+  int do_G = 0;
+  int do_g = 0;
+  int do_showinfo = showinfo;
+  int do_showrest = 0;
+  int do_flip = 0;
+  int erroroffset, len, delimiter;
+
+  use_utf8 = 0;
+
+  if (infile == stdin) printf("  re> ");
+  if (fgets((char *)buffer, BUFFER_SIZE, infile) == NULL) break;
+  if (infile != stdin) fprintf(outfile, "%s", (char *)buffer);
+  fflush(outfile);
+
+  p = buffer;
+  while (isspace(*p)) p++;
+  if (*p == 0) continue;
+
+  /* See if the pattern is to be loaded pre-compiled from a file. */
+
+  if (*p == '<' && strchr((char *)(p+1), '<') == NULL)
+    {
+    unsigned long int magic;
+    uschar sbuf[8];
+    FILE *f;
+
+    p++;
+    pp = p + (int)strlen((char *)p);
+    while (isspace(pp[-1])) pp--;
+    *pp = 0;
+
+    f = fopen((char *)p, "rb");
+    if (f == NULL)
+      {
+      fprintf(outfile, "Failed to open %s: %s\n", p, strerror(errno));
+      continue;
+      }
+
+    if (fread(sbuf, 1, 8, f) != 8) goto FAIL_READ;
+
+    true_size =
+      (sbuf[0] << 24) | (sbuf[1] << 16) | (sbuf[2] << 8) | sbuf[3];
+    true_study_size =
+      (sbuf[4] << 24) | (sbuf[5] << 16) | (sbuf[6] << 8) | sbuf[7];
+
+    re = (real_pcre *)new_malloc(true_size);
+    regex_gotten_store = gotten_store;
+
+    if (fread(re, 1, true_size, f) != true_size) goto FAIL_READ;
+
+    magic = ((real_pcre *)re)->magic_number;
+    if (magic != MAGIC_NUMBER)
+      {
+      if (byteflip(magic, sizeof(magic)) == MAGIC_NUMBER)
+        {
+        do_flip = 1;
+        }
+      else
+        {
+        fprintf(outfile, "Data in %s is not a compiled PCRE regex\n", p);
+        fclose(f);
+        continue;
+        }
+      }
+
+    fprintf(outfile, "Compiled regex%s loaded from %s\n",
+      do_flip? " (byte-inverted)" : "", p);
+
+    /* Need to know if UTF-8 for printing data strings */
+
+    new_info(re, NULL, PCRE_INFO_OPTIONS, &options);
+    use_utf8 = (options & PCRE_UTF8) != 0;
+
+    /* Now see if there is any following study data */
+
+    if (true_study_size != 0)
+      {
+      pcre_study_data *psd;
+
+      extra = (pcre_extra *)new_malloc(sizeof(pcre_extra) + true_study_size);
+      extra->flags = PCRE_EXTRA_STUDY_DATA;
+
+      psd = (pcre_study_data *)(((char *)extra) + sizeof(pcre_extra));
+      extra->study_data = psd;
+
+      if (fread(psd, 1, true_study_size, f) != true_study_size)
+        {
+        FAIL_READ:
+        fprintf(outfile, "Failed to read data from %s\n", p);
+        if (extra != NULL) new_free(extra);
+        if (re != NULL) new_free(re);
+        fclose(f);
+        continue;
+        }
+      fprintf(outfile, "Study data loaded from %s\n", p);
+      do_study = 1;     /* To get the data output if requested */
+      }
+    else fprintf(outfile, "No study data\n");
+
+    fclose(f);
+    goto SHOW_INFO;
+    }
+
+  /* In-line pattern (the usual case). Get the delimiter and seek the end of
+  the pattern; if is isn't complete, read more. */
+
+  delimiter = *p++;
+
+  if (isalnum(delimiter) || delimiter == '\\')
+    {
+    fprintf(outfile, "** Delimiter must not be alphameric or \\\n");
+    goto SKIP_DATA;
+    }
+
+  pp = p;
+
+  for(;;)
+    {
+    while (*pp != 0)
+      {
+      if (*pp == '\\' && pp[1] != 0) pp++;
+        else if (*pp == delimiter) break;
+      pp++;
+      }
+    if (*pp != 0) break;
+
+    len = BUFFER_SIZE - (pp - buffer);
+    if (len < 256)
+      {
+      fprintf(outfile, "** Expression too long - missing delimiter?\n");
+      goto SKIP_DATA;
+      }
+
+    if (infile == stdin) printf("    > ");
+    if (fgets((char *)pp, len, infile) == NULL)
+      {
+      fprintf(outfile, "** Unexpected EOF\n");
+      done = 1;
+      goto CONTINUE;
+      }
+    if (infile != stdin) fprintf(outfile, "%s", (char *)pp);
+    }
+
+  /* If the first character after the delimiter is backslash, make
+  the pattern end with backslash. This is purely to provide a way
+  of testing for the error message when a pattern ends with backslash. */
+
+  if (pp[1] == '\\') *pp++ = '\\';
+
+  /* Terminate the pattern at the delimiter, and save a copy of the pattern
+  for callouts. */
+
+  *pp++ = 0;
+  strcpy((char *)pbuffer, (char *)p);
+
+  /* Look for options after final delimiter */
+
+  options = 0;
+  study_options = 0;
+  log_store = showstore;  /* default from command line */
+
+  while (*pp != 0)
+    {
+    switch (*pp++)
+      {
+      case 'g': do_g = 1; break;
+      case 'i': options |= PCRE_CASELESS; break;
+      case 'm': options |= PCRE_MULTILINE; break;
+      case 's': options |= PCRE_DOTALL; break;
+      case 'x': options |= PCRE_EXTENDED; break;
+
+      case '+': do_showrest = 1; break;
+      case 'A': options |= PCRE_ANCHORED; break;
+      case 'C': options |= PCRE_AUTO_CALLOUT; break;
+      case 'D': do_debug = do_showinfo = 1; break;
+      case 'E': options |= PCRE_DOLLAR_ENDONLY; break;
+      case 'F': do_flip = 1; break;
+      case 'G': do_G = 1; break;
+      case 'I': do_showinfo = 1; break;
+      case 'M': log_store = 1; break;
+      case 'N': options |= PCRE_NO_AUTO_CAPTURE; break;
+
+#if !defined NOPOSIX
+      case 'P': do_posix = 1; break;
+#endif
+
+      case 'S': do_study = 1; break;
+      case 'U': options |= PCRE_UNGREEDY; break;
+      case 'X': options |= PCRE_EXTRA; break;
+      case '8': options |= PCRE_UTF8; use_utf8 = 1; break;
+      case '?': options |= PCRE_NO_UTF8_CHECK; break;
+
+      case 'L':
+      ppp = pp;
+      while (*ppp != '\n' && *ppp != ' ') ppp++;
+      *ppp = 0;
+      if (setlocale(LC_CTYPE, (const char *)pp) == NULL)
+        {
+        fprintf(outfile, "** Failed to set locale \"%s\"\n", pp);
+        goto SKIP_DATA;
+        }
+      tables = pcre_maketables();
+      pp = ppp;
+      break;
+
+      case '>':
+      to_file = pp;
+      while (*pp != 0) pp++;
+      while (isspace(pp[-1])) pp--;
+      *pp = 0;
+      break;
+
+      case '\n': case ' ': break;
+
+      default:
+      fprintf(outfile, "** Unknown option '%c'\n", pp[-1]);
+      goto SKIP_DATA;
+      }
+    }
+
+  /* Handle compiling via the POSIX interface, which doesn't support the
+  timing, showing, or debugging options, nor the ability to pass over
+  local character tables. */
+
+#if !defined NOPOSIX
+  if (posix || do_posix)
+    {
+    int rc;
+    int cflags = 0;
+
+    if ((options & PCRE_CASELESS) != 0) cflags |= REG_ICASE;
+    if ((options & PCRE_MULTILINE) != 0) cflags |= REG_NEWLINE;
+    rc = regcomp(&preg, (char *)p, cflags);
+
+    /* Compilation failed; go back for another re, skipping to blank line
+    if non-interactive. */
+
+    if (rc != 0)
+      {
+      (void)regerror(rc, &preg, (char *)buffer, BUFFER_SIZE);
+      fprintf(outfile, "Failed: POSIX code %d: %s\n", rc, buffer);
+      goto SKIP_DATA;
+      }
+    }
+
+  /* Handle compiling via the native interface */
+
+  else
+#endif  /* !defined NOPOSIX */
+
+    {
+    if (timeit)
+      {
+      register int i;
+      clock_t time_taken;
+      clock_t start_time = clock();
+      for (i = 0; i < LOOPREPEAT; i++)
+        {
+        re = pcre_compile((char *)p, options, &error, &erroroffset, tables);
+        if (re != NULL) free(re);
+        }
+      time_taken = clock() - start_time;
+      fprintf(outfile, "Compile time %.3f milliseconds\n",
+        (((double)time_taken * 1000.0) / (double)LOOPREPEAT) /
+          (double)CLOCKS_PER_SEC);
+      }
+
+    re = pcre_compile((char *)p, options, &error, &erroroffset, tables);
+
+    /* Compilation failed; go back for another re, skipping to blank line
+    if non-interactive. */
+
+    if (re == NULL)
+      {
+      fprintf(outfile, "Failed: %s at offset %d\n", error, erroroffset);
+      SKIP_DATA:
+      if (infile != stdin)
+        {
+        for (;;)
+          {
+          if (fgets((char *)buffer, BUFFER_SIZE, infile) == NULL)
+            {
+            done = 1;
+            goto CONTINUE;
+            }
+          len = (int)strlen((char *)buffer);
+          while (len > 0 && isspace(buffer[len-1])) len--;
+          if (len == 0) break;
+          }
+        fprintf(outfile, "\n");
+        }
+      goto CONTINUE;
+      }
+
+    /* Compilation succeeded; print data if required. There are now two
+    info-returning functions. The old one has a limited interface and
+    returns only limited data. Check that it agrees with the newer one. */
+
+    if (log_store)
+      fprintf(outfile, "Memory allocation (code space): %d\n",
+        (int)(gotten_store -
+              sizeof(real_pcre) -
+              ((real_pcre *)re)->name_count * ((real_pcre *)re)->name_entry_size));
+
+    /* Extract the size for possible writing before possibly flipping it,
+    and remember the store that was got. */
+
+    true_size = ((real_pcre *)re)->size;
+    regex_gotten_store = gotten_store;
+
+    /* If /S was present, study the regexp to generate additional info to
+    help with the matching. */
+
+    if (do_study)
+      {
+      if (timeit)
+        {
+        register int i;
+        clock_t time_taken;
+        clock_t start_time = clock();
+        for (i = 0; i < LOOPREPEAT; i++)
+          extra = pcre_study(re, study_options, &error);
+        time_taken = clock() - start_time;
+        if (extra != NULL) free(extra);
+        fprintf(outfile, "  Study time %.3f milliseconds\n",
+          (((double)time_taken * 1000.0) / (double)LOOPREPEAT) /
+            (double)CLOCKS_PER_SEC);
+        }
+      extra = pcre_study(re, study_options, &error);
+      if (error != NULL)
+        fprintf(outfile, "Failed to study: %s\n", error);
+      else if (extra != NULL)
+        true_study_size = ((pcre_study_data *)(extra->study_data))->size;
+      }
+
+    /* If the 'F' option was present, we flip the bytes of all the integer
+    fields in the regex data block and the study block. This is to make it
+    possible to test PCRE's handling of byte-flipped patterns, e.g. those
+    compiled on a different architecture. */
+
+    if (do_flip)
+      {
+      real_pcre *rre = (real_pcre *)re;
+      rre->magic_number = byteflip(rre->magic_number, sizeof(rre->magic_number));
+      rre->size = byteflip(rre->size, sizeof(rre->size));
+      rre->options = byteflip(rre->options, sizeof(rre->options));
+      rre->top_bracket = byteflip(rre->top_bracket, sizeof(rre->top_bracket));
+      rre->top_backref = byteflip(rre->top_backref, sizeof(rre->top_backref));
+      rre->first_byte = byteflip(rre->first_byte, sizeof(rre->first_byte));
+      rre->req_byte = byteflip(rre->req_byte, sizeof(rre->req_byte));
+      rre->name_table_offset = byteflip(rre->name_table_offset,
+        sizeof(rre->name_table_offset));
+      rre->name_entry_size = byteflip(rre->name_entry_size,
+        sizeof(rre->name_entry_size));
+      rre->name_count = byteflip(rre->name_count, sizeof(rre->name_count));
+
+      if (extra != NULL)
+        {
+        pcre_study_data *rsd = (pcre_study_data *)(extra->study_data);
+        rsd->size = byteflip(rsd->size, sizeof(rsd->size));
+        rsd->options = byteflip(rsd->options, sizeof(rsd->options));
+        }
+      }
+
+    /* Extract information from the compiled data if required */
+
+    SHOW_INFO:
+
+    if (do_showinfo)
+      {
+      unsigned long int get_options, all_options;
+      int old_first_char, old_options, old_count;
+      int count, backrefmax, first_char, need_char;
+      int nameentrysize, namecount;
+      const uschar *nametable;
+
+      if (do_debug)
+        {
+        fprintf(outfile, "------------------------------------------------------------------\n");
+        print_internals(re, outfile);
+        }
+
+      new_info(re, NULL, PCRE_INFO_OPTIONS, &get_options);
+      new_info(re, NULL, PCRE_INFO_SIZE, &size);
+      new_info(re, NULL, PCRE_INFO_CAPTURECOUNT, &count);
+      new_info(re, NULL, PCRE_INFO_BACKREFMAX, &backrefmax);
+      new_info(re, NULL, PCRE_INFO_FIRSTBYTE, &first_char);
+      new_info(re, NULL, PCRE_INFO_LASTLITERAL, &need_char);
+      new_info(re, NULL, PCRE_INFO_NAMEENTRYSIZE, &nameentrysize);
+      new_info(re, NULL, PCRE_INFO_NAMECOUNT, &namecount);
+      new_info(re, NULL, PCRE_INFO_NAMETABLE, (void *)&nametable);
+
+      old_count = pcre_info(re, &old_options, &old_first_char);
+      if (count < 0) fprintf(outfile,
+        "Error %d from pcre_info()\n", count);
+      else
+        {
+        if (old_count != count) fprintf(outfile,
+          "Count disagreement: pcre_fullinfo=%d pcre_info=%d\n", count,
+            old_count);
+
+        if (old_first_char != first_char) fprintf(outfile,
+          "First char disagreement: pcre_fullinfo=%d pcre_info=%d\n",
+            first_char, old_first_char);
+
+        if (old_options != (int)get_options) fprintf(outfile,
+          "Options disagreement: pcre_fullinfo=%ld pcre_info=%d\n",
+            get_options, old_options);
+        }
+
+      if (size != regex_gotten_store) fprintf(outfile,
+        "Size disagreement: pcre_fullinfo=%d call to malloc for %d\n",
+        size, regex_gotten_store);
+
+      fprintf(outfile, "Capturing subpattern count = %d\n", count);
+      if (backrefmax > 0)
+        fprintf(outfile, "Max back reference = %d\n", backrefmax);
+
+      if (namecount > 0)
+        {
+        fprintf(outfile, "Named capturing subpatterns:\n");
+        while (namecount-- > 0)
+          {
+          fprintf(outfile, "  %s %*s%3d\n", nametable + 2,
+            nameentrysize - 3 - (int)strlen((char *)nametable + 2), "",
+            GET2(nametable, 0));
+          nametable += nameentrysize;
+          }
+        }
+
+      /* The NOPARTIAL bit is a private bit in the options, so we have
+      to fish it out via out back door */
+
+      all_options = ((real_pcre *)re)->options;
+      if (do_flip)
+        {
+        all_options = byteflip(all_options, sizeof(all_options));
+        }
+
+      if ((all_options & PCRE_NOPARTIAL) != 0)
+        fprintf(outfile, "Partial matching not supported\n");
+
+      if (get_options == 0) fprintf(outfile, "No options\n");
+        else fprintf(outfile, "Options:%s%s%s%s%s%s%s%s%s%s\n",
+          ((get_options & PCRE_ANCHORED) != 0)? " anchored" : "",
+          ((get_options & PCRE_CASELESS) != 0)? " caseless" : "",
+          ((get_options & PCRE_EXTENDED) != 0)? " extended" : "",
+          ((get_options & PCRE_MULTILINE) != 0)? " multiline" : "",
+          ((get_options & PCRE_DOTALL) != 0)? " dotall" : "",
+          ((get_options & PCRE_DOLLAR_ENDONLY) != 0)? " dollar_endonly" : "",
+          ((get_options & PCRE_EXTRA) != 0)? " extra" : "",
+          ((get_options & PCRE_UNGREEDY) != 0)? " ungreedy" : "",
+          ((get_options & PCRE_UTF8) != 0)? " utf8" : "",
+          ((get_options & PCRE_NO_UTF8_CHECK) != 0)? " no_utf8_check" : "");
+
+      if (((((real_pcre *)re)->options) & PCRE_ICHANGED) != 0)
+        fprintf(outfile, "Case state changes\n");
+
+      if (first_char == -1)
+        {
+        fprintf(outfile, "First char at start or follows \\n\n");
+        }
+      else if (first_char < 0)
+        {
+        fprintf(outfile, "No first char\n");
+        }
+      else
+        {
+        int ch = first_char & 255;
+        const char *caseless = ((first_char & REQ_CASELESS) == 0)?
+          "" : " (caseless)";
+        if (isprint(ch))
+          fprintf(outfile, "First char = \'%c\'%s\n", ch, caseless);
+        else
+          fprintf(outfile, "First char = %d%s\n", ch, caseless);
+        }
+
+      if (need_char < 0)
+        {
+        fprintf(outfile, "No need char\n");
+        }
+      else
+        {
+        int ch = need_char & 255;
+        const char *caseless = ((need_char & REQ_CASELESS) == 0)?
+          "" : " (caseless)";
+        if (isprint(ch))
+          fprintf(outfile, "Need char = \'%c\'%s\n", ch, caseless);
+        else
+          fprintf(outfile, "Need char = %d%s\n", ch, caseless);
+        }
+
+      /* Don't output study size; at present it is in any case a fixed
+      value, but it varies, depending on the computer architecture, and
+      so messes up the test suite. (And with the /F option, it might be
+      flipped.) */
+
+      if (do_study)
+        {
+        if (extra == NULL)
+          fprintf(outfile, "Study returned NULL\n");
+        else
+          {
+          uschar *start_bits = NULL;
+          new_info(re, extra, PCRE_INFO_FIRSTTABLE, &start_bits);
+
+          if (start_bits == NULL)
+            fprintf(outfile, "No starting byte set\n");
+          else
+            {
+            int i;
+            int c = 24;
+            fprintf(outfile, "Starting byte set: ");
+            for (i = 0; i < 256; i++)
+              {
+              if ((start_bits[i/8] & (1<<(i&7))) != 0)
+                {
+                if (c > 75)
+                  {
+                  fprintf(outfile, "\n  ");
+                  c = 2;
+                  }
+                if (isprint(i) && i != ' ')
+                  {
+                  fprintf(outfile, "%c ", i);
+                  c += 2;
+                  }
+                else
+                  {
+                  fprintf(outfile, "\\x%02x ", i);
+                  c += 5;
+                  }
+                }
+              }
+            fprintf(outfile, "\n");
+            }
+          }
+        }
+      }
+
+    /* If the '>' option was present, we write out the regex to a file, and
+    that is all. The first 8 bytes of the file are the regex length and then
+    the study length, in big-endian order. */
+
+    if (to_file != NULL)
+      {
+      FILE *f = fopen((char *)to_file, "wb");
+      if (f == NULL)
+        {
+        fprintf(outfile, "Unable to open %s: %s\n", to_file, strerror(errno));
+        }
+      else
+        {
+        uschar sbuf[8];
+        sbuf[0] = (true_size >> 24)  & 255;
+        sbuf[1] = (true_size >> 16)  & 255;
+        sbuf[2] = (true_size >>  8)  & 255;
+        sbuf[3] = (true_size)  & 255;
+
+        sbuf[4] = (true_study_size >> 24)  & 255;
+        sbuf[5] = (true_study_size >> 16)  & 255;
+        sbuf[6] = (true_study_size >>  8)  & 255;
+        sbuf[7] = (true_study_size)  & 255;
+
+        if (fwrite(sbuf, 1, 8, f) < 8 ||
+            fwrite(re, 1, true_size, f) < true_size)
+          {
+          fprintf(outfile, "Write error on %s: %s\n", to_file, strerror(errno));
+          }
+        else
+          {
+          fprintf(outfile, "Compiled regex written to %s\n", to_file);
+          if (extra != NULL)
+            {
+            if (fwrite(extra->study_data, 1, true_study_size, f) <
+                true_study_size)
+              {
+              fprintf(outfile, "Write error on %s: %s\n", to_file,
+                strerror(errno));
+              }
+            else fprintf(outfile, "Study data written to %s\n", to_file);
+            }
+          }
+        fclose(f);
+        }
+      continue;  /* With next regex */
+      }
+    }        /* End of non-POSIX compile */
+
+  /* Read data lines and test them */
+
+  for (;;)
+    {
+    unsigned char *q;
+    unsigned char *bptr = dbuffer;
+    int *use_offsets = offsets;
+    int use_size_offsets = size_offsets;
+    int callout_data = 0;
+    int callout_data_set = 0;
+    int count, c;
+    int copystrings = 0;
+    int find_match_limit = 0;
+    int getstrings = 0;
+    int getlist = 0;
+    int gmatched = 0;
+    int start_offset = 0;
+    int g_notempty = 0;
+
+    options = 0;
+
+    pcre_callout = callout;
+    first_callout = 1;
+    callout_extra = 0;
+    callout_count = 0;
+    callout_fail_count = 999999;
+    callout_fail_id = -1;
+    show_malloc = 0;
+
+    if (infile == stdin) printf("data> ");
+    if (fgets((char *)buffer, BUFFER_SIZE, infile) == NULL)
+      {
+      done = 1;
+      goto CONTINUE;
+      }
+    if (infile != stdin) fprintf(outfile, "%s", (char *)buffer);
+
+    len = (int)strlen((char *)buffer);
+    while (len > 0 && isspace(buffer[len-1])) len--;
+    buffer[len] = 0;
+    if (len == 0) break;
+
+    p = buffer;
+    while (isspace(*p)) p++;
+
+    q = dbuffer;
+    while ((c = *p++) != 0)
+      {
+      int i = 0;
+      int n = 0;
+
+      if (c == '\\') switch ((c = *p++))
+        {
+        case 'a': c =    7; break;
+        case 'b': c = '\b'; break;
+        case 'e': c =   27; break;
+        case 'f': c = '\f'; break;
+        case 'n': c = '\n'; break;
+        case 'r': c = '\r'; break;
+        case 't': c = '\t'; break;
+        case 'v': c = '\v'; break;
+
+        case '0': case '1': case '2': case '3':
+        case '4': case '5': case '6': case '7':
+        c -= '0';
+        while (i++ < 2 && isdigit(*p) && *p != '8' && *p != '9')
+          c = c * 8 + *p++ - '0';
+        break;
+
+        case 'x':
+
+        /* Handle \x{..} specially - new Perl thing for utf8 */
+
+        if (*p == '{')
+          {
+          unsigned char *pt = p;
+          c = 0;
+          while (isxdigit(*(++pt)))
+            c = c * 16 + tolower(*pt) - ((isdigit(*pt))? '0' : 'W');
+          if (*pt == '}')
+            {
+            unsigned char buff8[8];
+            int ii, utn;
+            utn = ord2utf8(c, buff8);
+            for (ii = 0; ii < utn - 1; ii++) *q++ = buff8[ii];
+            c = buff8[ii];   /* Last byte */
+            p = pt + 1;
+            break;
+            }
+          /* Not correct form; fall through */
+          }
+
+        /* Ordinary \x */
+
+        c = 0;
+        while (i++ < 2 && isxdigit(*p))
+          {
+          c = c * 16 + tolower(*p) - ((isdigit(*p))? '0' : 'W');
+          p++;
+          }
+        break;
+
+        case 0:   /* \ followed by EOF allows for an empty line */
+        p--;
+        continue;
+
+        case '>':
+        while(isdigit(*p)) start_offset = start_offset * 10 + *p++ - '0';
+        continue;
+
+        case 'A':  /* Option setting */
+        options |= PCRE_ANCHORED;
+        continue;
+
+        case 'B':
+        options |= PCRE_NOTBOL;
+        continue;
+
+        case 'C':
+        if (isdigit(*p))    /* Set copy string */
+          {
+          while(isdigit(*p)) n = n * 10 + *p++ - '0';
+          copystrings |= 1 << n;
+          }
+        else if (isalnum(*p))
+          {
+          uschar name[256];
+          uschar *npp = name;
+          while (isalnum(*p)) *npp++ = *p++;
+          *npp = 0;
+          n = pcre_get_stringnumber(re, (char *)name);
+          if (n < 0)
+            fprintf(outfile, "no parentheses with name \"%s\"\n", name);
+          else copystrings |= 1 << n;
+          }
+        else if (*p == '+')
+          {
+          callout_extra = 1;
+          p++;
+          }
+        else if (*p == '-')
+          {
+          pcre_callout = NULL;
+          p++;
+          }
+        else if (*p == '!')
+          {
+          callout_fail_id = 0;
+          p++;
+          while(isdigit(*p))
+            callout_fail_id = callout_fail_id * 10 + *p++ - '0';
+          callout_fail_count = 0;
+          if (*p == '!')
+            {
+            p++;
+            while(isdigit(*p))
+              callout_fail_count = callout_fail_count * 10 + *p++ - '0';
+            }
+          }
+        else if (*p == '*')
+          {
+          int sign = 1;
+          callout_data = 0;
+          if (*(++p) == '-') { sign = -1; p++; }
+          while(isdigit(*p))
+            callout_data = callout_data * 10 + *p++ - '0';
+          callout_data *= sign;
+          callout_data_set = 1;
+          }
+        continue;
+
+        case 'G':
+        if (isdigit(*p))
+          {
+          while(isdigit(*p)) n = n * 10 + *p++ - '0';
+          getstrings |= 1 << n;
+          }
+        else if (isalnum(*p))
+          {
+          uschar name[256];
+          uschar *npp = name;
+          while (isalnum(*p)) *npp++ = *p++;
+          *npp = 0;
+          n = pcre_get_stringnumber(re, (char *)name);
+          if (n < 0)
+            fprintf(outfile, "no parentheses with name \"%s\"\n", name);
+          else getstrings |= 1 << n;
+          }
+        continue;
+
+        case 'L':
+        getlist = 1;
+        continue;
+
+        case 'M':
+        find_match_limit = 1;
+        continue;
+
+        case 'N':
+        options |= PCRE_NOTEMPTY;
+        continue;
+
+        case 'O':
+        while(isdigit(*p)) n = n * 10 + *p++ - '0';
+        if (n > size_offsets_max)
+          {
+          size_offsets_max = n;
+          free(offsets);
+          use_offsets = offsets = (int *)malloc(size_offsets_max * sizeof(int));
+          if (offsets == NULL)
+            {
+            printf("** Failed to get %d bytes of memory for offsets vector\n",
+              size_offsets_max * sizeof(int));
+            return 1;
+            }
+          }
+        use_size_offsets = n;
+        if (n == 0) use_offsets = NULL;   /* Ensures it can't write to it */
+        continue;
+
+        case 'P':
+        options |= PCRE_PARTIAL;
+        continue;
+
+        case 'S':
+        show_malloc = 1;
+        continue;
+
+        case 'Z':
+        options |= PCRE_NOTEOL;
+        continue;
+
+        case '?':
+        options |= PCRE_NO_UTF8_CHECK;
+        continue;
+        }
+      *q++ = c;
+      }
+    *q = 0;
+    len = q - dbuffer;
+
+    /* Handle matching via the POSIX interface, which does not
+    support timing or playing with the match limit or callout data. */
+
+#if !defined NOPOSIX
+    if (posix || do_posix)
+      {
+      int rc;
+      int eflags = 0;
+      regmatch_t *pmatch = NULL;
+      if (use_size_offsets > 0)
+        pmatch = (regmatch_t *)malloc(sizeof(regmatch_t) * use_size_offsets);
+      if ((options & PCRE_NOTBOL) != 0) eflags |= REG_NOTBOL;
+      if ((options & PCRE_NOTEOL) != 0) eflags |= REG_NOTEOL;
+
+      rc = regexec(&preg, (const char *)bptr, use_size_offsets, pmatch, eflags);
+
+      if (rc != 0)
+        {
+        (void)regerror(rc, &preg, (char *)buffer, BUFFER_SIZE);
+        fprintf(outfile, "No match: POSIX code %d: %s\n", rc, buffer);
+        }
+      else
+        {
+        size_t i;
+        for (i = 0; i < (size_t)use_size_offsets; i++)
+          {
+          if (pmatch[i].rm_so >= 0)
+            {
+            fprintf(outfile, "%2d: ", (int)i);
+            (void)pchars(dbuffer + pmatch[i].rm_so,
+              pmatch[i].rm_eo - pmatch[i].rm_so, outfile);
+            fprintf(outfile, "\n");
+            if (i == 0 && do_showrest)
+              {
+              fprintf(outfile, " 0+ ");
+              (void)pchars(dbuffer + pmatch[i].rm_eo, len - pmatch[i].rm_eo,
+                outfile);
+              fprintf(outfile, "\n");
+              }
+            }
+          }
+        }
+      free(pmatch);
+      }
+
+    /* Handle matching via the native interface - repeats for /g and /G */
+
+    else
+#endif  /* !defined NOPOSIX */
+
+    for (;; gmatched++)    /* Loop for /g or /G */
+      {
+      if (timeit)
+        {
+        register int i;
+        clock_t time_taken;
+        clock_t start_time = clock();
+        for (i = 0; i < LOOPREPEAT; i++)
+          count = pcre_exec(re, extra, (char *)bptr, len,
+            start_offset, options | g_notempty, use_offsets, use_size_offsets);
+        time_taken = clock() - start_time;
+        fprintf(outfile, "Execute time %.3f milliseconds\n",
+          (((double)time_taken * 1000.0) / (double)LOOPREPEAT) /
+            (double)CLOCKS_PER_SEC);
+        }
+
+      /* If find_match_limit is set, we want to do repeated matches with
+      varying limits in order to find the minimum value. */
+
+      if (find_match_limit)
+        {
+        int min = 0;
+        int mid = 64;
+        int max = -1;
+
+        if (extra == NULL)
+          {
+          extra = (pcre_extra *)malloc(sizeof(pcre_extra));
+          extra->flags = 0;
+          }
+        extra->flags |= PCRE_EXTRA_MATCH_LIMIT;
+
+        for (;;)
+          {
+          extra->match_limit = mid;
+          count = pcre_exec(re, extra, (char *)bptr, len, start_offset,
+            options | g_notempty, use_offsets, use_size_offsets);
+          if (count == PCRE_ERROR_MATCHLIMIT)
+            {
+            /* fprintf(outfile, "Testing match limit = %d\n", mid); */
+            min = mid;
+            mid = (mid == max - 1)? max : (max > 0)? (min + max)/2 : mid*2;
+            }
+          else if (count >= 0 || count == PCRE_ERROR_NOMATCH ||
+                                 count == PCRE_ERROR_PARTIAL)
+            {
+            if (mid == min + 1)
+              {
+              fprintf(outfile, "Minimum match limit = %d\n", mid);
+              break;
+              }
+            /* fprintf(outfile, "Testing match limit = %d\n", mid); */
+            max = mid;
+            mid = (min + mid)/2;
+            }
+          else break;    /* Some other error */
+          }
+
+        extra->flags &= ~PCRE_EXTRA_MATCH_LIMIT;
+        }
+
+      /* If callout_data is set, use the interface with additional data */
+
+      else if (callout_data_set)
+        {
+        if (extra == NULL)
+          {
+          extra = (pcre_extra *)malloc(sizeof(pcre_extra));
+          extra->flags = 0;
+          }
+        extra->flags |= PCRE_EXTRA_CALLOUT_DATA;
+        extra->callout_data = &callout_data;
+        count = pcre_exec(re, extra, (char *)bptr, len, start_offset,
+          options | g_notempty, use_offsets, use_size_offsets);
+        extra->flags &= ~PCRE_EXTRA_CALLOUT_DATA;
+        }
+
+      /* The normal case is just to do the match once, with the default
+      value of match_limit. */
+
+      else
+        {
+        count = pcre_exec(re, extra, (char *)bptr, len,
+          start_offset, options | g_notempty, use_offsets, use_size_offsets);
+        }
+
+      if (count == 0)
+        {
+        fprintf(outfile, "Matched, but too many substrings\n");
+        count = use_size_offsets/3;
+        }
+
+      /* Matched */
+
+      if (count >= 0)
+        {
+        int i;
+        for (i = 0; i < count * 2; i += 2)
+          {
+          if (use_offsets[i] < 0)
+            fprintf(outfile, "%2d: <unset>\n", i/2);
+          else
+            {
+            fprintf(outfile, "%2d: ", i/2);
+            (void)pchars(bptr + use_offsets[i],
+              use_offsets[i+1] - use_offsets[i], outfile);
+            fprintf(outfile, "\n");
+            if (i == 0)
+              {
+              if (do_showrest)
+                {
+                fprintf(outfile, " 0+ ");
+                (void)pchars(bptr + use_offsets[i+1], len - use_offsets[i+1],
+                  outfile);
+                fprintf(outfile, "\n");
+                }
+              }
+            }
+          }
+
+        for (i = 0; i < 32; i++)
+          {
+          if ((copystrings & (1 << i)) != 0)
+            {
+            char copybuffer[16];
+            int rc = pcre_copy_substring((char *)bptr, use_offsets, count,
+              i, copybuffer, sizeof(copybuffer));
+            if (rc < 0)
+              fprintf(outfile, "copy substring %d failed %d\n", i, rc);
+            else
+              fprintf(outfile, "%2dC %s (%d)\n", i, copybuffer, rc);
+            }
+          }
+
+        for (i = 0; i < 32; i++)
+          {
+          if ((getstrings & (1 << i)) != 0)
+            {
+            const char *substring;
+            int rc = pcre_get_substring((char *)bptr, use_offsets, count,
+              i, &substring);
+            if (rc < 0)
+              fprintf(outfile, "get substring %d failed %d\n", i, rc);
+            else
+              {
+              fprintf(outfile, "%2dG %s (%d)\n", i, substring, rc);
+              /* free((void *)substring); */
+              pcre_free_substring(substring);
+              }
+            }
+          }
+
+        if (getlist)
+          {
+          const char **stringlist;
+          int rc = pcre_get_substring_list((char *)bptr, use_offsets, count,
+            &stringlist);
+          if (rc < 0)
+            fprintf(outfile, "get substring list failed %d\n", rc);
+          else
+            {
+            for (i = 0; i < count; i++)
+              fprintf(outfile, "%2dL %s\n", i, stringlist[i]);
+            if (stringlist[i] != NULL)
+              fprintf(outfile, "string list not terminated by NULL\n");
+            /* free((void *)stringlist); */
+            pcre_free_substring_list(stringlist);
+            }
+          }
+        }
+
+      /* There was a partial match */
+
+      else if (count == PCRE_ERROR_PARTIAL)
+        {
+        fprintf(outfile, "Partial match\n");
+        break;  /* Out of the /g loop */
+        }
+
+      /* Failed to match. If this is a /g or /G loop and we previously set
+      g_notempty after a null match, this is not necessarily the end.
+      We want to advance the start offset, and continue. In the case of UTF-8
+      matching, the advance must be one character, not one byte. Fudge the
+      offset values to achieve this. We won't be at the end of the string -
+      that was checked before setting g_notempty. */
+
+      else
+        {
+        if (g_notempty != 0)
+          {
+          int onechar = 1;
+          use_offsets[0] = start_offset;
+          if (use_utf8)
+            {
+            while (start_offset + onechar < len)
+              {
+              int tb = bptr[start_offset+onechar];
+              if (tb <= 127) break;
+              tb &= 0xc0;
+              if (tb != 0 && tb != 0xc0) onechar++;
+              }
+            }
+          use_offsets[1] = start_offset + onechar;
+          }
+        else
+          {
+          if (count == PCRE_ERROR_NOMATCH)
+            {
+            if (gmatched == 0) fprintf(outfile, "No match\n");
+            }
+          else fprintf(outfile, "Error %d\n", count);
+          break;  /* Out of the /g loop */
+          }
+        }
+
+      /* If not /g or /G we are done */
+
+      if (!do_g && !do_G) break;
+
+      /* If we have matched an empty string, first check to see if we are at
+      the end of the subject. If so, the /g loop is over. Otherwise, mimic
+      what Perl's /g options does. This turns out to be rather cunning. First
+      we set PCRE_NOTEMPTY and PCRE_ANCHORED and try the match again at the
+      same point. If this fails (picked up above) we advance to the next
+      character. */
+
+      g_notempty = 0;
+      if (use_offsets[0] == use_offsets[1])
+        {
+        if (use_offsets[0] == len) break;
+        g_notempty = PCRE_NOTEMPTY | PCRE_ANCHORED;
+        }
+
+      /* For /g, update the start offset, leaving the rest alone */
+
+      if (do_g) start_offset = use_offsets[1];
+
+      /* For /G, update the pointer and length */
+
+      else
+        {
+        bptr += use_offsets[1];
+        len -= use_offsets[1];
+        }
+      }  /* End of loop for /g and /G */
+    }    /* End of loop for data lines */
+
+  CONTINUE:
+
+#if !defined NOPOSIX
+  if (posix || do_posix) regfree(&preg);
+#endif
+
+  if (re != NULL) free(re);
+  if (extra != NULL) free(extra);
+  if (tables != NULL)
+    {
+    free((void *)tables);
+    setlocale(LC_CTYPE, "C");
+    }
+  }
+
+if (infile == stdin) fprintf(outfile, "\n");
+return 0;
+}
+
+/* End */
diff --git a/connectors/jk/native/iis/pcre/perltest b/connectors/jk/native/iis/pcre/perltest
new file mode 100755
index 0000000..44afbea
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/perltest
@@ -0,0 +1,211 @@
+#! /usr/bin/perl
+
+# Program for testing regular expressions with perl to check that PCRE handles
+# them the same. This is the version that supports /8 for UTF-8 testing. As it
+# stands, it requires at least Perl 5.8 for UTF-8 support. For Perl 5.6, it
+# can be used as is for non-UTF-8 testing, but you have to uncomment the
+# "use utf8" lines in order to to UTF-8 stuff (and you mustn't uncomment them
+# for non-UTF-8 use).
+
+
+# Function for turning a string into a string of printing chars. There are
+# currently problems with UTF-8 strings; this fudges round them.
+
+sub pchars {
+my($t) = "";
+
+if ($utf8)
+  {
+#  use utf8;                    <=============== For UTF-8 in Perl 5.6
+  @p = unpack('U*', $_[0]);
+  foreach $c (@p)
+    {
+    if ($c >= 32 && $c < 127) { $t .= chr $c; }
+      else { $t .= sprintf("\\x{%02x}", $c); }
+    }
+  }
+
+else
+  {
+  foreach $c (split(//, $_[0]))
+    {
+    if (ord $c >= 32 && ord $c < 127) { $t .= $c; }
+      else { $t .= sprintf("\\x%02x", ord $c); }
+    }
+  }
+
+$t;
+}
+
+
+
+# Read lines from named file or stdin and write to named file or stdout; lines
+# consist of a regular expression, in delimiters and optionally followed by
+# options, followed by a set of test data, terminated by an empty line.
+
+# Sort out the input and output files
+
+if (@ARGV > 0)
+  {
+  open(INFILE, "<$ARGV[0]") || die "Failed to open $ARGV[0]\n";
+  $infile = "INFILE";
+  }
+else { $infile = "STDIN"; }
+
+if (@ARGV > 1)
+  {
+  open(OUTFILE, ">$ARGV[1]") || die "Failed to open $ARGV[1]\n";
+  $outfile = "OUTFILE";
+  }
+else { $outfile = "STDOUT"; }
+
+printf($outfile "Perl $] Regular Expressions\n\n");
+
+# Main loop
+
+NEXT_RE:
+for (;;)
+  {
+  printf "  re> " if $infile eq "STDIN";
+  last if ! ($_ = <$infile>);
+  printf $outfile "$_" if $infile ne "STDIN";
+  next if ($_ eq "");
+
+  $pattern = $_;
+
+  while ($pattern !~ /^\s*(.).*\1/s)
+    {
+    printf "    > " if $infile eq "STDIN";
+    last if ! ($_ = <$infile>);
+    printf $outfile "$_" if $infile ne "STDIN";
+    $pattern .= $_;
+    }
+
+   chomp($pattern);
+   $pattern =~ s/\s+$//;
+
+  # The private /+ modifier means "print $' afterwards".
+
+  $showrest = ($pattern =~ s/\+(?=[a-z]*$)//);
+
+  # The private /8 modifier means "operate in UTF-8". Currently, Perl
+  # has bugs that we try to work around using this flag.
+
+  $utf8 = ($pattern =~ s/8(?=[a-z]*$)//);
+
+  # Check that the pattern is valid
+
+  if ($utf8)
+    {
+#    use utf8;                    <=============== For UTF-8 in Perl 5.6
+    eval "\$_ =~ ${pattern}";
+    }
+  else
+    {
+    eval "\$_ =~ ${pattern}";
+    }
+
+  if ($@)
+    {
+    printf $outfile "Error: $@";
+    next NEXT_RE;
+    }
+
+  # If the /g modifier is present, we want to put a loop round the matching;
+  # otherwise just a single "if".
+
+  $cmd = ($pattern =~ /g[a-z]*$/)? "while" : "if";
+
+  # If the pattern is actually the null string, Perl uses the most recently
+  # executed (and successfully compiled) regex is used instead. This is a
+  # nasty trap for the unwary! The PCRE test suite does contain null strings
+  # in places - if they are allowed through here all sorts of weird and
+  # unexpected effects happen. To avoid this, we replace such patterns with
+  # a non-null pattern that has the same effect.
+
+  $pattern = "/(?#)/$2" if ($pattern =~ /^(.)\1(.*)$/);
+
+  # Read data lines and test them
+
+  for (;;)
+    {
+    printf "data> " if $infile eq "STDIN";
+    last NEXT_RE if ! ($_ = <$infile>);
+    chomp;
+    printf $outfile "$_\n" if $infile ne "STDIN";
+
+    s/\s+$//;
+    s/^\s+//;
+
+    last if ($_ eq "");
+
+    $x = eval "\"$_\"";   # To get escapes processed
+
+    # Empty array for holding results, then do the matching.
+
+    @subs = ();
+
+    $pushes = "push \@subs,\$&;" .
+         "push \@subs,\$1;" .
+         "push \@subs,\$2;" .
+         "push \@subs,\$3;" .
+         "push \@subs,\$4;" .
+         "push \@subs,\$5;" .
+         "push \@subs,\$6;" .
+         "push \@subs,\$7;" .
+         "push \@subs,\$8;" .
+         "push \@subs,\$9;" .
+         "push \@subs,\$10;" .
+         "push \@subs,\$11;" .
+         "push \@subs,\$12;" .
+         "push \@subs,\$13;" .
+         "push \@subs,\$14;" .
+         "push \@subs,\$15;" .
+         "push \@subs,\$16;" .
+         "push \@subs,\$'; }";
+
+    if ($utf8)
+      {
+#      use utf8;                    <=============== For UTF-8 in Perl 5.6
+      eval "${cmd} (\$x =~ ${pattern}) {" . $pushes;
+      }
+    else
+      {
+      eval "${cmd} (\$x =~ ${pattern}) {" . $pushes;
+      }
+
+    if ($@)
+      {
+      printf $outfile "Error: $@\n";
+      next NEXT_RE;
+      }
+    elsif (scalar(@subs) == 0)
+      {
+      printf $outfile "No match\n";
+      }
+    else
+      {
+      while (scalar(@subs) != 0)
+        {
+        printf $outfile (" 0: %s\n", &pchars($subs[0]));
+        printf $outfile (" 0+ %s\n", &pchars($subs[17])) if $showrest;
+        $last_printed = 0;
+        for ($i = 1; $i <= 16; $i++)
+          {
+          if (defined $subs[$i])
+            {
+            while ($last_printed++ < $i-1)
+              { printf $outfile ("%2d: <unset>\n", $last_printed); }
+            printf $outfile ("%2d: %s\n", $i, &pchars($subs[$i]));
+            $last_printed = $i;
+            }
+          }
+        splice(@subs, 0, 18);
+        }
+      }
+    }
+  }
+
+# printf $outfile "\n";
+
+# End
diff --git a/connectors/jk/native/iis/pcre/perltest8 b/connectors/jk/native/iis/pcre/perltest8
new file mode 100755
index 0000000..2fe522d
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/perltest8
@@ -0,0 +1,208 @@
+#! /usr/bin/perl
+
+# Program for testing regular expressions with perl to check that PCRE handles
+# them the same. This is the version that supports /8 for UTF-8 testing. It
+# requires at least Perl 5.6.
+
+
+# Function for turning a string into a string of printing chars. There are
+# currently problems with UTF-8 strings; this fudges round them.
+
+sub pchars {
+my($t) = "";
+
+if ($utf8)
+  {
+  use utf8;
+  @p = unpack('U*', $_[0]);
+  foreach $c (@p)
+    {
+    if ($c >= 32 && $c < 127) { $t .= chr $c; }
+      else { $t .= sprintf("\\x{%02x}", $c); }
+    }
+  }
+
+else
+  {
+  foreach $c (split(//, $_[0]))
+    {
+    if (ord $c >= 32 && ord $c < 127) { $t .= $c; }
+      else { $t .= sprintf("\\x%02x", ord $c); }
+    }
+  }
+
+$t;
+}
+
+
+
+# Read lines from named file or stdin and write to named file or stdout; lines
+# consist of a regular expression, in delimiters and optionally followed by
+# options, followed by a set of test data, terminated by an empty line.
+
+# Sort out the input and output files
+
+if (@ARGV > 0)
+  {
+  open(INFILE, "<$ARGV[0]") || die "Failed to open $ARGV[0]\n";
+  $infile = "INFILE";
+  }
+else { $infile = "STDIN"; }
+
+if (@ARGV > 1)
+  {
+  open(OUTFILE, ">$ARGV[1]") || die "Failed to open $ARGV[1]\n";
+  $outfile = "OUTFILE";
+  }
+else { $outfile = "STDOUT"; }
+
+printf($outfile "Perl $] Regular Expressions\n\n");
+
+# Main loop
+
+NEXT_RE:
+for (;;)
+  {
+  printf "  re> " if $infile eq "STDIN";
+  last if ! ($_ = <$infile>);
+  printf $outfile "$_" if $infile ne "STDIN";
+  next if ($_ eq "");
+
+  $pattern = $_;
+
+  while ($pattern !~ /^\s*(.).*\1/s)
+    {
+    printf "    > " if $infile eq "STDIN";
+    last if ! ($_ = <$infile>);
+    printf $outfile "$_" if $infile ne "STDIN";
+    $pattern .= $_;
+    }
+
+   chomp($pattern);
+   $pattern =~ s/\s+$//;
+
+  # The private /+ modifier means "print $' afterwards".
+
+  $showrest = ($pattern =~ s/\+(?=[a-z]*$)//);
+
+  # The private /8 modifier means "operate in UTF-8". Currently, Perl
+  # has bugs that we try to work around using this flag.
+
+  $utf8 = ($pattern =~ s/8(?=[a-z]*$)//);
+
+  # Check that the pattern is valid
+
+  if ($utf8)
+    {
+    use utf8;
+    eval "\$_ =~ ${pattern}";
+    }
+  else
+    {
+    eval "\$_ =~ ${pattern}";
+    }
+
+  if ($@)
+    {
+    printf $outfile "Error: $@";
+    next NEXT_RE;
+    }
+
+  # If the /g modifier is present, we want to put a loop round the matching;
+  # otherwise just a single "if".
+
+  $cmd = ($pattern =~ /g[a-z]*$/)? "while" : "if";
+
+  # If the pattern is actually the null string, Perl uses the most recently
+  # executed (and successfully compiled) regex is used instead. This is a
+  # nasty trap for the unwary! The PCRE test suite does contain null strings
+  # in places - if they are allowed through here all sorts of weird and
+  # unexpected effects happen. To avoid this, we replace such patterns with
+  # a non-null pattern that has the same effect.
+
+  $pattern = "/(?#)/$2" if ($pattern =~ /^(.)\1(.*)$/);
+
+  # Read data lines and test them
+
+  for (;;)
+    {
+    printf "data> " if $infile eq "STDIN";
+    last NEXT_RE if ! ($_ = <$infile>);
+    chomp;
+    printf $outfile "$_\n" if $infile ne "STDIN";
+
+    s/\s+$//;
+    s/^\s+//;
+
+    last if ($_ eq "");
+
+    $x = eval "\"$_\"";   # To get escapes processed
+
+    # Empty array for holding results, then do the matching.
+
+    @subs = ();
+
+    $pushes = "push \@subs,\$&;" .
+         "push \@subs,\$1;" .
+         "push \@subs,\$2;" .
+         "push \@subs,\$3;" .
+         "push \@subs,\$4;" .
+         "push \@subs,\$5;" .
+         "push \@subs,\$6;" .
+         "push \@subs,\$7;" .
+         "push \@subs,\$8;" .
+         "push \@subs,\$9;" .
+         "push \@subs,\$10;" .
+         "push \@subs,\$11;" .
+         "push \@subs,\$12;" .
+         "push \@subs,\$13;" .
+         "push \@subs,\$14;" .
+         "push \@subs,\$15;" .
+         "push \@subs,\$16;" .
+         "push \@subs,\$'; }";
+
+    if ($utf8)
+      {
+      use utf8;
+      eval "${cmd} (\$x =~ ${pattern}) {" . $pushes;
+      }
+    else
+      {
+      eval "${cmd} (\$x =~ ${pattern}) {" . $pushes;
+      }
+
+    if ($@)
+      {
+      printf $outfile "Error: $@\n";
+      next NEXT_RE;
+      }
+    elsif (scalar(@subs) == 0)
+      {
+      printf $outfile "No match\n";
+      }
+    else
+      {
+      while (scalar(@subs) != 0)
+        {
+        printf $outfile (" 0: %s\n", &pchars($subs[0]));
+        printf $outfile (" 0+ %s\n", &pchars($subs[17])) if $showrest;
+        $last_printed = 0;
+        for ($i = 1; $i <= 16; $i++)
+          {
+          if (defined $subs[$i])
+            {
+            while ($last_printed++ < $i-1)
+              { printf $outfile ("%2d: <unset>\n", $last_printed); }
+            printf $outfile ("%2d: %s\n", $i, &pchars($subs[$i]));
+            $last_printed = $i;
+            }
+          }
+        splice(@subs, 0, 18);
+        }
+      }
+    }
+  }
+
+printf $outfile "\n";
+
+# End
diff --git a/connectors/jk/native/iis/pcre/pgrep.c b/connectors/jk/native/iis/pcre/pgrep.c
new file mode 100644
index 0000000..ad1b87e
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/pgrep.c
@@ -0,0 +1,225 @@
+/*************************************************
+*               PCRE grep program                *
+*************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "config.h"
+#include "pcre.h"
+
+#define FALSE 0
+#define TRUE 1
+
+typedef int BOOL;
+
+
+
+/*************************************************
+*               Global variables                 *
+*************************************************/
+
+static pcre *pattern;
+static pcre_extra *hints;
+
+static BOOL count_only = FALSE;
+static BOOL filenames_only = FALSE;
+static BOOL invert = FALSE;
+static BOOL number = FALSE;
+static BOOL silent = FALSE;
+static BOOL whole_lines = FALSE;
+
+
+
+#if ! HAVE_STRERROR
+/*************************************************
+*     Provide strerror() for non-ANSI libraries  *
+*************************************************/
+
+/* Some old-fashioned systems still around (e.g. SunOS4) don't have strerror()
+in their libraries, but can provide the same facility by this simple
+alternative function. */
+
+extern int   sys_nerr;
+extern char *sys_errlist[];
+
+char *
+strerror(int n)
+{
+if (n < 0 || n >= sys_nerr) return "unknown error number";
+return sys_errlist[n];
+}
+#endif /* HAVE_STRERROR */
+
+
+
+/*************************************************
+*              Grep an individual file           *
+*************************************************/
+
+static int
+pgrep(FILE *in, char *name)
+{
+int rc = 1;
+int linenumber = 0;
+int count = 0;
+int offsets[99];
+char buffer[BUFSIZ];
+
+while (fgets(buffer, sizeof(buffer), in) != NULL)
+  {
+  BOOL match;
+  int length = (int)strlen(buffer);
+  if (length > 0 && buffer[length-1] == '\n') buffer[--length] = 0;
+  linenumber++;
+
+  match = pcre_exec(pattern, hints, buffer, length, 0, 0, offsets, 99) >= 0;
+  if (match && whole_lines && offsets[1] != length) match = FALSE;
+
+  if (match != invert)
+    {
+    if (count_only) count++;
+
+    else if (filenames_only)
+      {
+      fprintf(stdout, "%s\n", (name == NULL)? "<stdin>" : name);
+      return 0;
+      }
+
+    else if (silent) return 0;
+
+    else
+      {
+      if (name != NULL) fprintf(stdout, "%s:", name);
+      if (number) fprintf(stdout, "%d:", linenumber);
+      fprintf(stdout, "%s\n", buffer);
+      }
+
+    rc = 0;
+    }
+  }
+
+if (count_only)
+  {
+  if (name != NULL) fprintf(stdout, "%s:", name);
+  fprintf(stdout, "%d\n", count);
+  }
+
+return rc;
+}
+
+
+
+
+/*************************************************
+*                Usage function                  *
+*************************************************/
+
+static int
+usage(int rc)
+{
+fprintf(stderr, "Usage: pgrep [-Vchilnsvx] pattern [file] ...\n");
+return rc;
+}
+
+
+
+
+/*************************************************
+*                Main program                    *
+*************************************************/
+
+int
+main(int argc, char **argv)
+{
+int i;
+int rc = 1;
+int options = 0;
+int errptr;
+const char *error;
+BOOL filenames = TRUE;
+
+/* Process the options */
+
+for (i = 1; i < argc; i++)
+  {
+  char *s;
+  if (argv[i][0] != '-') break;
+  s = argv[i] + 1;
+  while (*s != 0)
+    {
+    switch (*s++)
+      {
+      case 'c': count_only = TRUE; break;
+      case 'h': filenames = FALSE; break;
+      case 'i': options |= PCRE_CASELESS; break;
+      case 'l': filenames_only = TRUE;
+      case 'n': number = TRUE; break;
+      case 's': silent = TRUE; break;
+      case 'v': invert = TRUE; break;
+      case 'x': whole_lines = TRUE; options |= PCRE_ANCHORED; break;
+
+      case 'V':
+      fprintf(stderr, "PCRE version %s\n", pcre_version());
+      break;
+
+      default:
+      fprintf(stderr, "pgrep: unknown option %c\n", s[-1]);
+      return usage(2);
+      }
+    }
+  }
+
+/* There must be at least a regexp argument */
+
+if (i >= argc) return usage(0);
+
+/* Compile the regular expression. */
+
+pattern = pcre_compile(argv[i++], options, &error, &errptr, NULL);
+if (pattern == NULL)
+  {
+  fprintf(stderr, "pgrep: error in regex at offset %d: %s\n", errptr, error);
+  return 2;
+  }
+
+/* Study the regular expression, as we will be running it may times */
+
+hints = pcre_study(pattern, 0, &error);
+if (error != NULL)
+  {
+  fprintf(stderr, "pgrep: error while studing regex: %s\n", error);
+  return 2;
+  }
+
+/* If there are no further arguments, do the business on stdin and exit */
+
+if (i >= argc) return pgrep(stdin, NULL);
+
+/* Otherwise, work through the remaining arguments as files. If there is only
+one, don't give its name on the output. */
+
+if (i == argc - 1) filenames = FALSE;
+if (filenames_only) filenames = TRUE;
+
+for (; i < argc; i++)
+  {
+  FILE *in = fopen(argv[i], "r");
+  if (in == NULL)
+    {
+    fprintf(stderr, "%s: failed to open: %s\n", argv[i], strerror(errno));
+    rc = 2;
+    }
+  else
+    {
+    int frc = pgrep(in, filenames? argv[i] : NULL);
+    if (frc == 0 && rc == 1) rc = 0;
+    fclose(in);
+    }
+  }
+
+return rc;
+}
+
+/* End */
diff --git a/connectors/jk/native/iis/pcre/printint.c b/connectors/jk/native/iis/pcre/printint.c
new file mode 100644
index 0000000..ed18495
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/printint.c
@@ -0,0 +1,471 @@
+/*************************************************
+*      Perl-Compatible Regular Expressions       *
+*************************************************/
+
+/*
+This is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language. See
+the file Tech.Notes for some information on the internals.
+
+Written by: Philip Hazel <ph10@cam.ac.uk>
+
+           Copyright (c) 1997-2004 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+    * Neither the name of the University of Cambridge nor the names of its
+      contributors may be used to endorse or promote products derived from
+      this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+
+/* This module contains a debugging function for printing out the internal form
+of a compiled regular expression. It is kept in a separate file so that it can
+be #included both in the pcretest program, and in the library itself when
+compiled with the debugging switch. */
+
+
+static const char *OP_names[] = { OP_NAME_LIST };
+
+
+/*************************************************
+*       Print single- or multi-byte character    *
+*************************************************/
+
+/* These tables are actually copies of ones in pcre.c. If we compile the
+library with debugging, they are included twice, but that isn't really a
+problem - compiling with debugging is pretty rare and these are very small. */
+
+static const int utf8_t3[] = { 0xff, 0x1f, 0x0f, 0x07, 0x03, 0x01};
+
+static const uschar utf8_t4[] = {
+  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,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 };
+
+static int
+print_char(FILE *f, uschar *ptr, BOOL utf8)
+{
+int c = *ptr;
+
+if (!utf8 || (c & 0xc0) != 0xc0)
+  {
+  if (isprint(c)) fprintf(f, "%c", c); else fprintf(f, "\\x%02x", c);
+  return 0;
+  }
+else
+  {
+  int i;
+  int a = utf8_t4[c & 0x3f];  /* Number of additional bytes */
+  int s = 6*a;
+  c = (c & utf8_t3[a]) << s;
+  for (i = 1; i <= a; i++)
+    {
+    /* This is a check for malformed UTF-8; it should only occur if the sanity
+    check has been turned off. Rather than swallow random bytes, just stop if
+    we hit a bad one. Print it with \X instead of \x as an indication. */
+
+    if ((ptr[i] & 0xc0) != 0x80)
+      {
+      fprintf(f, "\\X{%x}", c);
+      return i - 1;
+      }
+
+    /* The byte is OK */
+
+    s -= 6;
+    c |= (ptr[i] & 0x3f) << s;
+    }
+  if (c < 128) fprintf(f, "\\x%02x", c); else fprintf(f, "\\x{%x}", c);
+  return a;
+  }
+}
+
+
+
+
+#ifdef SUPPORT_UCP
+/*************************************************
+*          Find Unicode property name            *
+*************************************************/
+
+static const char *
+get_ucpname(int property)
+{
+int i;
+for (i = sizeof(utt)/sizeof(ucp_type_table); i >= 0; i--)
+  {
+  if (property == utt[i].value) break;
+  }
+return (i >= 0)? utt[i].name : "??";
+}
+#endif /* SUPPORT_UCP */
+
+
+
+/*************************************************
+*         Print compiled regex                   *
+*************************************************/
+
+/* Make this function work for a regex with integers either byte order.
+However, we assume that what we are passed is a compiled regex. */
+
+static void
+print_internals(pcre *external_re, FILE *f)
+{
+real_pcre *re = (real_pcre *)external_re;
+uschar *codestart, *code;
+BOOL utf8;
+
+unsigned int options = re->options;
+int offset = re->name_table_offset;
+int count = re->name_count;
+int size = re->name_entry_size;
+
+if (re->magic_number != MAGIC_NUMBER)
+  {
+  offset = ((offset << 8) & 0xff00) | ((offset >> 8) & 0xff);
+  count = ((count << 8) & 0xff00) | ((count >> 8) & 0xff);
+  size = ((size << 8) & 0xff00) | ((size >> 8) & 0xff);
+  options = ((options << 24) & 0xff000000) |
+            ((options <<  8) & 0x00ff0000) |
+            ((options >>  8) & 0x0000ff00) |
+            ((options >> 24) & 0x000000ff);
+  }
+
+code = codestart = (uschar *)re + offset + count * size;
+utf8 = (options & PCRE_UTF8) != 0;
+
+for(;;)
+  {
+  uschar *ccode;
+  int c;
+  int extra = 0;
+
+  fprintf(f, "%3d ", code - codestart);
+
+  if (*code >= OP_BRA)
+    {
+    if (*code - OP_BRA > EXTRACT_BASIC_MAX)
+      fprintf(f, "%3d Bra extra\n", GET(code, 1));
+    else
+      fprintf(f, "%3d Bra %d\n", GET(code, 1), *code - OP_BRA);
+    code += OP_lengths[OP_BRA];
+    continue;
+    }
+
+  switch(*code)
+    {
+    case OP_END:
+    fprintf(f, "    %s\n", OP_names[*code]);
+    fprintf(f, "------------------------------------------------------------------\n");
+    return;
+
+    case OP_OPT:
+    fprintf(f, " %.2x %s", code[1], OP_names[*code]);
+    break;
+
+    case OP_CHAR:
+      {
+      fprintf(f, "    ");
+      do
+        {
+        code++;
+        code += 1 + print_char(f, code, utf8);
+        }
+      while (*code == OP_CHAR);
+      fprintf(f, "\n");
+      continue;
+      }
+    break;
+
+    case OP_CHARNC:
+      {
+      fprintf(f, " NC ");
+      do
+        {
+        code++;
+        code += 1 + print_char(f, code, utf8);
+        }
+      while (*code == OP_CHARNC);
+      fprintf(f, "\n");
+      continue;
+      }
+    break;
+
+    case OP_KETRMAX:
+    case OP_KETRMIN:
+    case OP_ALT:
+    case OP_KET:
+    case OP_ASSERT:
+    case OP_ASSERT_NOT:
+    case OP_ASSERTBACK:
+    case OP_ASSERTBACK_NOT:
+    case OP_ONCE:
+    case OP_COND:
+    case OP_REVERSE:
+    fprintf(f, "%3d %s", GET(code, 1), OP_names[*code]);
+    break;
+
+    case OP_BRANUMBER:
+    printf("%3d %s", GET2(code, 1), OP_names[*code]);
+    break;
+
+    case OP_CREF:
+    if (GET2(code, 1) == CREF_RECURSE)
+      fprintf(f, "    Cond recurse");
+    else
+      fprintf(f, "%3d %s", GET2(code,1), OP_names[*code]);
+    break;
+
+    case OP_STAR:
+    case OP_MINSTAR:
+    case OP_PLUS:
+    case OP_MINPLUS:
+    case OP_QUERY:
+    case OP_MINQUERY:
+    case OP_TYPESTAR:
+    case OP_TYPEMINSTAR:
+    case OP_TYPEPLUS:
+    case OP_TYPEMINPLUS:
+    case OP_TYPEQUERY:
+    case OP_TYPEMINQUERY:
+    fprintf(f, "    ");
+    if (*code >= OP_TYPESTAR)
+      {
+      fprintf(f, "%s", OP_names[code[1]]);
+#ifdef SUPPORT_UCP
+      if (code[1] == OP_PROP || code[1] == OP_NOTPROP)
+        {
+        fprintf(f, " %s ", get_ucpname(code[2]));
+        extra = 1;
+        }
+#endif
+      }
+    else extra = print_char(f, code+1, utf8);
+    fprintf(f, "%s", OP_names[*code]);
+    break;
+
+    case OP_EXACT:
+    case OP_UPTO:
+    case OP_MINUPTO:
+    fprintf(f, "    ");
+    extra = print_char(f, code+3, utf8);
+    fprintf(f, "{");
+    if (*code != OP_EXACT) fprintf(f, ",");
+    fprintf(f, "%d}", GET2(code,1));
+    if (*code == OP_MINUPTO) fprintf(f, "?");
+    break;
+
+    case OP_TYPEEXACT:
+    case OP_TYPEUPTO:
+    case OP_TYPEMINUPTO:
+    fprintf(f, "    %s", OP_names[code[3]]);
+#ifdef SUPPORT_UCP
+    if (code[3] == OP_PROP || code[3] == OP_NOTPROP)
+      {
+      fprintf(f, " %s ", get_ucpname(code[4]));
+      extra = 1;
+      }
+#endif
+    fprintf(f, "{");
+    if (*code != OP_TYPEEXACT) fprintf(f, "0,");
+    fprintf(f, "%d}", GET2(code,1));
+    if (*code == OP_TYPEMINUPTO) fprintf(f, "?");
+    break;
+
+    case OP_NOT:
+    if (isprint(c = code[1])) fprintf(f, "    [^%c]", c);
+      else fprintf(f, "    [^\\x%02x]", c);
+    break;
+
+    case OP_NOTSTAR:
+    case OP_NOTMINSTAR:
+    case OP_NOTPLUS:
+    case OP_NOTMINPLUS:
+    case OP_NOTQUERY:
+    case OP_NOTMINQUERY:
+    if (isprint(c = code[1])) fprintf(f, "    [^%c]", c);
+      else fprintf(f, "    [^\\x%02x]", c);
+    fprintf(f, "%s", OP_names[*code]);
+    break;
+
+    case OP_NOTEXACT:
+    case OP_NOTUPTO:
+    case OP_NOTMINUPTO:
+    if (isprint(c = code[3])) fprintf(f, "    [^%c]{", c);
+      else fprintf(f, "    [^\\x%02x]{", c);
+    if (*code != OP_NOTEXACT) fprintf(f, ",");
+    fprintf(f, "%d}", GET2(code,1));
+    if (*code == OP_NOTMINUPTO) fprintf(f, "?");
+    break;
+
+    case OP_RECURSE:
+    fprintf(f, "%3d %s", GET(code, 1), OP_names[*code]);
+    break;
+
+    case OP_REF:
+    fprintf(f, "    \\%d", GET2(code,1));
+    ccode = code + OP_lengths[*code];
+    goto CLASS_REF_REPEAT;
+
+    case OP_CALLOUT:
+    fprintf(f, "    %s %d %d %d", OP_names[*code], code[1], GET(code,2),
+      GET(code, 2 + LINK_SIZE));
+    break;
+
+#ifdef SUPPORT_UCP
+    case OP_PROP:
+    case OP_NOTPROP:
+    fprintf(f, "    %s %s", OP_names[*code], get_ucpname(code[1]));
+    break;
+#endif
+
+    /* OP_XCLASS can only occur in UTF-8 mode. However, there's no harm in
+    having this code always here, and it makes it less messy without all those
+    #ifdefs. */
+
+    case OP_CLASS:
+    case OP_NCLASS:
+    case OP_XCLASS:
+      {
+      int i, min, max;
+      BOOL printmap;
+
+      fprintf(f, "    [");
+
+      if (*code == OP_XCLASS)
+        {
+        extra = GET(code, 1);
+        ccode = code + LINK_SIZE + 1;
+        printmap = (*ccode & XCL_MAP) != 0;
+        if ((*ccode++ & XCL_NOT) != 0) fprintf(f, "^");
+        }
+      else
+        {
+        printmap = TRUE;
+        ccode = code + 1;
+        }
+
+      /* Print a bit map */
+
+      if (printmap)
+        {
+        for (i = 0; i < 256; i++)
+          {
+          if ((ccode[i/8] & (1 << (i&7))) != 0)
+            {
+            int j;
+            for (j = i+1; j < 256; j++)
+              if ((ccode[j/8] & (1 << (j&7))) == 0) break;
+            if (i == '-' || i == ']') fprintf(f, "\\");
+            if (isprint(i)) fprintf(f, "%c", i); else fprintf(f, "\\x%02x", i);
+            if (--j > i)
+              {
+              if (j != i + 1) fprintf(f, "-");
+              if (j == '-' || j == ']') fprintf(f, "\\");
+              if (isprint(j)) fprintf(f, "%c", j); else fprintf(f, "\\x%02x", j);
+              }
+            i = j;
+            }
+          }
+        ccode += 32;
+        }
+
+      /* For an XCLASS there is always some additional data */
+
+      if (*code == OP_XCLASS)
+        {
+        int ch;
+        while ((ch = *ccode++) != XCL_END)
+          {
+#ifdef SUPPORT_UCP
+          if (ch == XCL_PROP)
+            {
+            fprintf(f, "\\p{%s}", get_ucpname(*ccode++));
+            }
+          else if (ch == XCL_NOTPROP)
+            {
+            fprintf(f, "\\P{%s}", get_ucpname(*ccode++));
+            }
+          else
+#endif
+            {
+            ccode += 1 + print_char(f, ccode, TRUE);
+            if (ch == XCL_RANGE)
+              {
+              fprintf(f, "-");
+              ccode += 1 + print_char(f, ccode, TRUE);
+              }
+            }
+          }
+        }
+
+      /* Indicate a non-UTF8 class which was created by negation */
+
+      fprintf(f, "]%s", (*code == OP_NCLASS)? " (neg)" : "");
+
+      /* Handle repeats after a class or a back reference */
+
+      CLASS_REF_REPEAT:
+      switch(*ccode)
+        {
+        case OP_CRSTAR:
+        case OP_CRMINSTAR:
+        case OP_CRPLUS:
+        case OP_CRMINPLUS:
+        case OP_CRQUERY:
+        case OP_CRMINQUERY:
+        fprintf(f, "%s", OP_names[*ccode]);
+        extra += OP_lengths[*ccode];
+        break;
+
+        case OP_CRRANGE:
+        case OP_CRMINRANGE:
+        min = GET2(ccode,1);
+        max = GET2(ccode,3);
+        if (max == 0) fprintf(f, "{%d,}", min);
+        else fprintf(f, "{%d,%d}", min, max);
+        if (*ccode == OP_CRMINRANGE) fprintf(f, "?");
+        extra += OP_lengths[*ccode];
+        break;
+        }
+      }
+    break;
+
+    /* Anything else is just an item with no data*/
+
+    default:
+    fprintf(f, "    %s", OP_names[*code]);
+    break;
+    }
+
+  code += OP_lengths[*code] + extra;
+  fprintf(f, "\n");
+  }
+}
+
+/* End of printint.c */
diff --git a/connectors/jk/native/iis/pcre/study.c b/connectors/jk/native/iis/pcre/study.c
new file mode 100644
index 0000000..d67070a
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/study.c
@@ -0,0 +1,484 @@
+/*************************************************
+*      Perl-Compatible Regular Expressions       *
+*************************************************/
+
+/*
+This is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language. See
+the file Tech.Notes for some information on the internals.
+
+Written by: Philip Hazel <ph10@cam.ac.uk>
+
+           Copyright (c) 1997-2004 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+    * Neither the name of the University of Cambridge nor the names of its
+      contributors may be used to endorse or promote products derived from
+      this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+
+/* Include the internals header, which itself includes Standard C headers plus
+the external pcre header. */
+
+#include "internal.h"
+
+
+
+/*************************************************
+*      Set a bit and maybe its alternate case    *
+*************************************************/
+
+/* Given a character, set its bit in the table, and also the bit for the other
+version of a letter if we are caseless.
+
+Arguments:
+  start_bits    points to the bit map
+  c             is the character
+  caseless      the caseless flag
+  cd            the block with char table pointers
+
+Returns:        nothing
+*/
+
+static void
+set_bit(uschar *start_bits, unsigned int c, BOOL caseless, compile_data *cd)
+{
+start_bits[c/8] |= (1 << (c&7));
+if (caseless && (cd->ctypes[c] & ctype_letter) != 0)
+  start_bits[cd->fcc[c]/8] |= (1 << (cd->fcc[c]&7));
+}
+
+
+
+/*************************************************
+*          Create bitmap of starting chars       *
+*************************************************/
+
+/* This function scans a compiled unanchored expression and attempts to build a
+bitmap of the set of initial characters. If it can't, it returns FALSE. As time
+goes by, we may be able to get more clever at doing this.
+
+Arguments:
+  code         points to an expression
+  start_bits   points to a 32-byte table, initialized to 0
+  caseless     the current state of the caseless flag
+  utf8         TRUE if in UTF-8 mode
+  cd           the block with char table pointers
+
+Returns:       TRUE if table built, FALSE otherwise
+*/
+
+static BOOL
+set_start_bits(const uschar *code, uschar *start_bits, BOOL caseless,
+  BOOL utf8, compile_data *cd)
+{
+register int c;
+
+/* This next statement and the later reference to dummy are here in order to
+trick the optimizer of the IBM C compiler for OS/2 into generating correct
+code. Apparently IBM isn't going to fix the problem, and we would rather not
+disable optimization (in this module it actually makes a big difference, and
+the pcre module can use all the optimization it can get). */
+
+volatile int dummy;
+
+do
+  {
+  const uschar *tcode = code + 1 + LINK_SIZE;
+  BOOL try_next = TRUE;
+
+  while (try_next)
+    {
+    /* If a branch starts with a bracket or a positive lookahead assertion,
+    recurse to set bits from within them. That's all for this branch. */
+
+    if ((int)*tcode >= OP_BRA || *tcode == OP_ASSERT)
+      {
+      if (!set_start_bits(tcode, start_bits, caseless, utf8, cd))
+        return FALSE;
+      try_next = FALSE;
+      }
+
+    else switch(*tcode)
+      {
+      default:
+      return FALSE;
+
+      /* Skip over callout */
+
+      case OP_CALLOUT:
+      tcode += 2 + 2*LINK_SIZE;
+      break;
+
+      /* Skip over extended extraction bracket number */
+
+      case OP_BRANUMBER:
+      tcode += 3;
+      break;
+
+      /* Skip over lookbehind and negative lookahead assertions */
+
+      case OP_ASSERT_NOT:
+      case OP_ASSERTBACK:
+      case OP_ASSERTBACK_NOT:
+      do tcode += GET(tcode, 1); while (*tcode == OP_ALT);
+      tcode += 1+LINK_SIZE;
+      break;
+
+      /* Skip over an option setting, changing the caseless flag */
+
+      case OP_OPT:
+      caseless = (tcode[1] & PCRE_CASELESS) != 0;
+      tcode += 2;
+      break;
+
+      /* BRAZERO does the bracket, but carries on. */
+
+      case OP_BRAZERO:
+      case OP_BRAMINZERO:
+      if (!set_start_bits(++tcode, start_bits, caseless, utf8, cd))
+        return FALSE;
+      dummy = 1;
+      do tcode += GET(tcode,1); while (*tcode == OP_ALT);
+      tcode += 1+LINK_SIZE;
+      break;
+
+      /* Single-char * or ? sets the bit and tries the next item */
+
+      case OP_STAR:
+      case OP_MINSTAR:
+      case OP_QUERY:
+      case OP_MINQUERY:
+      set_bit(start_bits, tcode[1], caseless, cd);
+      tcode += 2;
+#ifdef SUPPORT_UTF8
+      if (utf8) while ((*tcode & 0xc0) == 0x80) tcode++;
+#endif
+      break;
+
+      /* Single-char upto sets the bit and tries the next */
+
+      case OP_UPTO:
+      case OP_MINUPTO:
+      set_bit(start_bits, tcode[3], caseless, cd);
+      tcode += 4;
+#ifdef SUPPORT_UTF8
+      if (utf8) while ((*tcode & 0xc0) == 0x80) tcode++;
+#endif
+      break;
+
+      /* At least one single char sets the bit and stops */
+
+      case OP_EXACT:       /* Fall through */
+      tcode += 2;
+
+      case OP_CHAR:
+      case OP_CHARNC:
+      case OP_PLUS:
+      case OP_MINPLUS:
+      set_bit(start_bits, tcode[1], caseless, cd);
+      try_next = FALSE;
+      break;
+
+      /* Single character type sets the bits and stops */
+
+      case OP_NOT_DIGIT:
+      for (c = 0; c < 32; c++)
+        start_bits[c] |= ~cd->cbits[c+cbit_digit];
+      try_next = FALSE;
+      break;
+
+      case OP_DIGIT:
+      for (c = 0; c < 32; c++)
+        start_bits[c] |= cd->cbits[c+cbit_digit];
+      try_next = FALSE;
+      break;
+
+      case OP_NOT_WHITESPACE:
+      for (c = 0; c < 32; c++)
+        start_bits[c] |= ~cd->cbits[c+cbit_space];
+      try_next = FALSE;
+      break;
+
+      case OP_WHITESPACE:
+      for (c = 0; c < 32; c++)
+        start_bits[c] |= cd->cbits[c+cbit_space];
+      try_next = FALSE;
+      break;
+
+      case OP_NOT_WORDCHAR:
+      for (c = 0; c < 32; c++)
+        start_bits[c] |= ~cd->cbits[c+cbit_word];
+      try_next = FALSE;
+      break;
+
+      case OP_WORDCHAR:
+      for (c = 0; c < 32; c++)
+        start_bits[c] |= cd->cbits[c+cbit_word];
+      try_next = FALSE;
+      break;
+
+      /* One or more character type fudges the pointer and restarts, knowing
+      it will hit a single character type and stop there. */
+
+      case OP_TYPEPLUS:
+      case OP_TYPEMINPLUS:
+      tcode++;
+      break;
+
+      case OP_TYPEEXACT:
+      tcode += 3;
+      break;
+
+      /* Zero or more repeats of character types set the bits and then
+      try again. */
+
+      case OP_TYPEUPTO:
+      case OP_TYPEMINUPTO:
+      tcode += 2;               /* Fall through */
+
+      case OP_TYPESTAR:
+      case OP_TYPEMINSTAR:
+      case OP_TYPEQUERY:
+      case OP_TYPEMINQUERY:
+      switch(tcode[1])
+        {
+        case OP_ANY:
+        return FALSE;
+
+        case OP_NOT_DIGIT:
+        for (c = 0; c < 32; c++)
+          start_bits[c] |= ~cd->cbits[c+cbit_digit];
+        break;
+
+        case OP_DIGIT:
+        for (c = 0; c < 32; c++)
+          start_bits[c] |= cd->cbits[c+cbit_digit];
+        break;
+
+        case OP_NOT_WHITESPACE:
+        for (c = 0; c < 32; c++)
+          start_bits[c] |= ~cd->cbits[c+cbit_space];
+        break;
+
+        case OP_WHITESPACE:
+        for (c = 0; c < 32; c++)
+          start_bits[c] |= cd->cbits[c+cbit_space];
+        break;
+
+        case OP_NOT_WORDCHAR:
+        for (c = 0; c < 32; c++)
+          start_bits[c] |= ~cd->cbits[c+cbit_word];
+        break;
+
+        case OP_WORDCHAR:
+        for (c = 0; c < 32; c++)
+          start_bits[c] |= cd->cbits[c+cbit_word];
+        break;
+        }
+
+      tcode += 2;
+      break;
+
+      /* Character class where all the information is in a bit map: set the
+      bits and either carry on or not, according to the repeat count. If it was
+      a negative class, and we are operating with UTF-8 characters, any byte
+      with a value >= 0xc4 is a potentially valid starter because it starts a
+      character with a value > 255. */
+
+      case OP_NCLASS:
+      if (utf8)
+        {
+        start_bits[24] |= 0xf0;              /* Bits for 0xc4 - 0xc8 */
+        memset(start_bits+25, 0xff, 7);      /* Bits for 0xc9 - 0xff */
+        }
+      /* Fall through */
+
+      case OP_CLASS:
+        {
+        tcode++;
+
+        /* In UTF-8 mode, the bits in a bit map correspond to character
+        values, not to byte values. However, the bit map we are constructing is
+        for byte values. So we have to do a conversion for characters whose
+        value is > 127. In fact, there are only two possible starting bytes for
+        characters in the range 128 - 255. */
+
+        if (utf8)
+          {
+          for (c = 0; c < 16; c++) start_bits[c] |= tcode[c];
+          for (c = 128; c < 256; c++)
+            {
+            if ((tcode[c/8] && (1 << (c&7))) != 0)
+              {
+              int d = (c >> 6) | 0xc0;            /* Set bit for this starter */
+              start_bits[d/8] |= (1 << (d&7));    /* and then skip on to the */
+              c = (c & 0xc0) + 0x40 - 1;          /* next relevant character. */
+              }
+            }
+          }
+
+        /* In non-UTF-8 mode, the two bit maps are completely compatible. */
+
+        else
+          {
+          for (c = 0; c < 32; c++) start_bits[c] |= tcode[c];
+          }
+
+        /* Advance past the bit map, and act on what follows */
+
+        tcode += 32;
+        switch (*tcode)
+          {
+          case OP_CRSTAR:
+          case OP_CRMINSTAR:
+          case OP_CRQUERY:
+          case OP_CRMINQUERY:
+          tcode++;
+          break;
+
+          case OP_CRRANGE:
+          case OP_CRMINRANGE:
+          if (((tcode[1] << 8) + tcode[2]) == 0) tcode += 5;
+            else try_next = FALSE;
+          break;
+
+          default:
+          try_next = FALSE;
+          break;
+          }
+        }
+      break; /* End of bitmap class handling */
+
+      }      /* End of switch */
+    }        /* End of try_next loop */
+
+  code += GET(code, 1);   /* Advance to next branch */
+  }
+while (*code == OP_ALT);
+return TRUE;
+}
+
+
+
+/*************************************************
+*          Study a compiled expression           *
+*************************************************/
+
+/* This function is handed a compiled expression that it must study to produce
+information that will speed up the matching. It returns a pcre_extra block
+which then gets handed back to pcre_exec().
+
+Arguments:
+  re        points to the compiled expression
+  options   contains option bits
+  errorptr  points to where to place error messages;
+            set NULL unless error
+
+Returns:    pointer to a pcre_extra block, with study_data filled in and the
+              appropriate flag set;
+            NULL on error or if no optimization possible
+*/
+
+EXPORT pcre_extra *
+pcre_study(const pcre *external_re, int options, const char **errorptr)
+{
+uschar start_bits[32];
+pcre_extra *extra;
+pcre_study_data *study;
+const uschar *tables;
+const real_pcre *re = (const real_pcre *)external_re;
+uschar *code = (uschar *)re + re->name_table_offset +
+  (re->name_count * re->name_entry_size);
+compile_data compile_block;
+
+*errorptr = NULL;
+
+if (re == NULL || re->magic_number != MAGIC_NUMBER)
+  {
+  *errorptr = "argument is not a compiled regular expression";
+  return NULL;
+  }
+
+if ((options & ~PUBLIC_STUDY_OPTIONS) != 0)
+  {
+  *errorptr = "unknown or incorrect option bit(s) set";
+  return NULL;
+  }
+
+/* For an anchored pattern, or an unanchored pattern that has a first char, or
+a multiline pattern that matches only at "line starts", no further processing
+at present. */
+
+if ((re->options & (PCRE_ANCHORED|PCRE_FIRSTSET|PCRE_STARTLINE)) != 0)
+  return NULL;
+
+/* Set the character tables in the block that is passed around */
+
+tables = re->tables;
+if (tables == NULL)
+  (void)pcre_fullinfo(external_re, NULL, PCRE_INFO_DEFAULT_TABLES, (void*)&tables);
+
+compile_block.lcc = tables + lcc_offset;
+compile_block.fcc = tables + fcc_offset;
+compile_block.cbits = tables + cbits_offset;
+compile_block.ctypes = tables + ctypes_offset;
+
+/* See if we can find a fixed set of initial characters for the pattern. */
+
+memset(start_bits, 0, 32 * sizeof(uschar));
+if (!set_start_bits(code, start_bits, (re->options & PCRE_CASELESS) != 0,
+  (re->options & PCRE_UTF8) != 0, &compile_block)) return NULL;
+
+/* Get a pcre_extra block and a pcre_study_data block. The study data is put in
+the latter, which is pointed to by the former, which may also get additional
+data set later by the calling program. At the moment, the size of
+pcre_study_data is fixed. We nevertheless save it in a field for returning via
+the pcre_fullinfo() function so that if it becomes variable in the future, we
+don't have to change that code. */
+
+extra = (pcre_extra *)(pcre_malloc)
+  (sizeof(pcre_extra) + sizeof(pcre_study_data));
+
+if (extra == NULL)
+  {
+  *errorptr = "failed to get memory";
+  return NULL;
+  }
+
+study = (pcre_study_data *)((char *)extra + sizeof(pcre_extra));
+extra->flags = PCRE_EXTRA_STUDY_DATA;
+extra->study_data = study;
+
+study->size = sizeof(pcre_study_data);
+study->options = PCRE_STUDY_MAPPED;
+memcpy(study->start_bits, start_bits, sizeof(start_bits));
+
+return extra;
+}
+
+/* End of study.c */
diff --git a/connectors/jk/native/iis/pcre/testdata/testinput1 b/connectors/jk/native/iis/pcre/testdata/testinput1
new file mode 100644
index 0000000..c4b99c6
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/testdata/testinput1
Binary files differ
diff --git a/connectors/jk/native/iis/pcre/testdata/testinput2 b/connectors/jk/native/iis/pcre/testdata/testinput2
new file mode 100644
index 0000000..d118daf
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/testdata/testinput2
@@ -0,0 +1,1396 @@
+/(a)b|/
+
+/abc/
+    abc
+    defabc
+    \Aabc
+    *** Failers
+    \Adefabc
+    ABC
+
+/^abc/
+    abc
+    \Aabc
+    *** Failers
+    defabc
+    \Adefabc
+
+/a+bc/
+
+/a*bc/
+
+/a{3}bc/
+
+/(abc|a+z)/
+
+/^abc$/
+    abc
+    *** Failers
+    def\nabc
+
+/ab\gdef/X
+
+/(?X)ab\gdef/X
+
+/x{5,4}/
+
+/z{65536}/
+
+/[abcd/
+
+/(?X)[\B]/
+
+/[z-a]/
+
+/^*/
+
+/(abc/
+
+/(?# abc/
+
+/(?z)abc/
+
+/.*b/
+
+/.*?b/
+
+/cat|dog|elephant/
+    this sentence eventually mentions a cat
+    this sentences rambles on and on for a while and then reaches elephant
+
+/cat|dog|elephant/S
+    this sentence eventually mentions a cat
+    this sentences rambles on and on for a while and then reaches elephant
+
+/cat|dog|elephant/iS
+    this sentence eventually mentions a CAT cat
+    this sentences rambles on and on for a while to elephant ElePhant
+
+/a|[bcd]/S
+
+/(a|[^\dZ])/S
+
+/(a|b)*[\s]/S
+
+/(ab\2)/
+
+/{4,5}abc/
+
+/(a)(b)(c)\2/
+    abcb
+    \O0abcb
+    \O3abcb
+    \O6abcb
+    \O9abcb
+    \O12abcb 
+
+/(a)bc|(a)(b)\2/
+    abc
+    \O0abc
+    \O3abc
+    \O6abc
+    aba
+    \O0aba
+    \O3aba
+    \O6aba
+    \O9aba
+    \O12aba
+
+/abc$/E
+    abc
+    *** Failers
+    abc\n
+    abc\ndef
+
+/(a)(b)(c)(d)(e)\6/
+
+/the quick brown fox/
+    the quick brown fox
+    this is a line with the quick brown fox
+
+/the quick brown fox/A
+    the quick brown fox
+    *** Failers
+    this is a line with the quick brown fox
+
+/ab(?z)cd/
+
+/^abc|def/
+    abcdef
+    abcdef\B
+
+/.*((abc)$|(def))/
+    defabc
+    \Zdefabc
+
+/abc/P
+    abc
+    *** Failers
+    
+/^abc|def/P
+    abcdef
+    abcdef\B
+
+/.*((abc)$|(def))/P
+    defabc
+    \Zdefabc
+  
+/the quick brown fox/P
+    the quick brown fox
+    *** Failers 
+    The Quick Brown Fox 
+
+/the quick brown fox/Pi
+    the quick brown fox
+    The Quick Brown Fox 
+
+/abc.def/P
+    *** Failers
+    abc\ndef
+    
+/abc$/P
+    abc
+    abc\n 
+
+/(abc)\2/P
+
+/(abc\1)/P
+    abc
+
+/)/
+
+/a[]b/
+
+/[^aeiou ]{3,}/
+    co-processors, and for 
+    
+/<.*>/
+    abc<def>ghi<klm>nop
+
+/<.*?>/
+    abc<def>ghi<klm>nop
+
+/<.*>/U
+    abc<def>ghi<klm>nop
+    
+/(?U)<.*>/
+    abc<def>ghi<klm>nop
+
+/<.*?>/U
+    abc<def>ghi<klm>nop
+    
+/={3,}/U
+    abc========def
+    
+/(?U)={3,}?/
+    abc========def
+    
+/(?<!bar|cattle)foo/
+    foo
+    catfoo 
+    *** Failers
+    the barfoo
+    and cattlefoo   
+
+/(?<=a+)b/
+
+/(?<=aaa|b{0,3})b/
+
+/(?<!(foo)a\1)bar/
+
+/(?i)abc/
+
+/(a|(?m)a)/
+
+/(?i)^1234/
+
+/(^b|(?i)^d)/
+
+/(?s).*/
+
+/[abcd]/S
+
+/(?i)[abcd]/S
+
+/(?m)[xy]|(b|c)/S
+
+/(^a|^b)/m
+
+/(?i)(^a|^b)/m
+
+/(a)(?(1)a|b|c)/
+
+/(?(?=a)a|b|c)/
+
+/(?(1a)/
+
+/(?(?i))/
+
+/(?(abc))/
+
+/(?(?<ab))/
+
+/((?s)blah)\s+\1/
+
+/((?i)blah)\s+\1/
+
+/((?i)b)/DS
+
+/(a*b|(?i:c*(?-i)d))/S
+
+/a$/
+    a
+    a\n
+    *** Failers 
+    \Za
+    \Za\n   
+
+/a$/m
+    a
+    a\n
+    \Za\n   
+    *** Failers 
+    \Za
+    
+/\Aabc/m
+
+/^abc/m 
+
+/^((a+)(?U)([ab]+)(?-U)([bc]+)(\w*))/
+  aaaaabbbbbcccccdef
+
+/(?<=foo)[ab]/S
+
+/(?<!foo)(alpha|omega)/S
+
+/(?!alphabet)[ab]/S
+
+/(?<=foo\n)^bar/m
+    foo\nbarbar 
+    ***Failers
+    rhubarb 
+    barbell
+    abc\nbarton 
+
+/^(?<=foo\n)bar/m
+    foo\nbarbar 
+    ***Failers
+    rhubarb 
+    barbell
+    abc\nbarton 
+
+/(?>^abc)/m
+    abc
+    def\nabc
+    *** Failers
+    defabc   
+
+/(?<=ab(c+)d)ef/
+
+/(?<=ab(?<=c+)d)ef/
+
+/(?<=ab(c|de)f)g/
+
+/The next three are in testinput2 because they have variable length branches/
+
+/(?<=bullock|donkey)-cart/
+    the bullock-cart
+    a donkey-cart race
+    *** Failers
+    cart
+    horse-and-cart    
+      
+/(?<=ab(?i)x|y|z)/
+
+/(?>.*)(?<=(abcd)|(xyz))/
+    alphabetabcd
+    endingxyz
+
+/(?<=ab(?i)x(?-i)y|(?i)z|b)ZZ/
+    abxyZZ
+    abXyZZ
+    ZZZ
+    zZZ
+    bZZ
+    BZZ     
+    *** Failers
+    ZZ 
+    abXYZZ 
+    zzz
+    bzz  
+
+/(?<!(foo)a)bar/
+    bar
+    foobbar 
+    *** Failers
+    fooabar  
+
+/This one is here because Perl 5.005_02 doesn't fail it/
+
+/^(a)?(?(1)a|b)+$/
+    *** Failers
+    a 
+
+/This one is here because I think Perl 5.005_02 gets the setting of $1 wrong/
+
+/^(a\1?){4}$/
+    aaaaaa
+    
+/These are syntax tests from Perl 5.005/
+
+/a[b-a]/
+
+/a[]b/
+
+/a[/
+
+/*a/
+
+/(*)b/
+
+/abc)/
+
+/(abc/
+
+/a**/
+
+/)(/
+
+/\1/
+
+/\2/
+
+/(a)|\2/
+
+/a[b-a]/i
+
+/a[]b/i
+
+/a[/i
+
+/*a/i
+
+/(*)b/i
+
+/abc)/i
+
+/(abc/i
+
+/a**/i
+
+/)(/i
+
+/:(?:/
+
+/(?<%)b/
+
+/a(?{)b/
+
+/a(?{{})b/
+
+/a(?{}})b/
+
+/a(?{"{"})b/
+
+/a(?{"{"}})b/
+
+/(?(1?)a|b)/
+
+/(?(1)a|b|c)/
+
+/[a[:xyz:/
+
+/(?<=x+)y/
+
+/a{37,17}/
+
+/abc/\
+
+/abc/\P
+
+/abc/\i
+
+/(a)bc(d)/
+    abcd
+    abcd\C2
+    abcd\C5
+     
+/(.{20})/
+    abcdefghijklmnopqrstuvwxyz
+    abcdefghijklmnopqrstuvwxyz\C1
+    abcdefghijklmnopqrstuvwxyz\G1
+     
+/(.{15})/
+    abcdefghijklmnopqrstuvwxyz
+    abcdefghijklmnopqrstuvwxyz\C1\G1
+
+/(.{16})/
+    abcdefghijklmnopqrstuvwxyz
+    abcdefghijklmnopqrstuvwxyz\C1\G1\L
+    
+/^(a|(bc))de(f)/
+    adef\G1\G2\G3\G4\L 
+    bcdef\G1\G2\G3\G4\L 
+    adefghijk\C0 
+    
+/^abc\00def/
+    abc\00def\L\C0 
+    
+/word ((?:[a-zA-Z0-9]+ )((?:[a-zA-Z0-9]+ )((?:[a-zA-Z0-9]+ )((?:[a-zA-Z0-9]+ 
+)((?:[a-zA-Z0-9]+ )((?:[a-zA-Z0-9]+ )((?:[a-zA-Z0-9]+ )((?:[a-zA-Z0-9]+ 
+)?)?)?)?)?)?)?)?)?otherword/M
+
+/.*X/D
+
+/.*X/Ds
+
+/(.*X|^B)/D
+
+/(.*X|^B)/Ds
+    
+/(?s)(.*X|^B)/D
+
+/(?s:.*X|^B)/D
+
+/\Biss\B/+
+    Mississippi
+
+/\Biss\B/+P
+    Mississippi
+
+/iss/G+
+    Mississippi
+
+/\Biss\B/G+
+    Mississippi
+
+/\Biss\B/g+
+    Mississippi
+    *** Failers
+    Mississippi\A
+
+/(?<=[Ms])iss/g+
+    Mississippi
+
+/(?<=[Ms])iss/G+
+    Mississippi
+
+/^iss/g+
+    ississippi
+    
+/.*iss/g+
+    abciss\nxyzisspqr 
+
+/.i./+g
+    Mississippi
+    Mississippi\A
+    Missouri river
+    Missouri river\A  
+
+/^.is/+g
+    Mississippi
+
+/^ab\n/g+
+    ab\nab\ncd
+
+/^ab\n/mg+
+    ab\nab\ncd
+
+/abc/
+
+/abc|bac/
+
+/(abc|bac)/
+
+/(abc|(c|dc))/
+
+/(abc|(d|de)c)/
+
+/a*/
+
+/a+/
+
+/(baa|a+)/
+
+/a{0,3}/
+
+/baa{3,}/
+
+/"([^\\"]+|\\.)*"/
+
+/(abc|ab[cd])/
+
+/(a|.)/
+
+/a|ba|\w/
+
+/abc(?=pqr)/
+
+/...(?<=abc)/
+
+/abc(?!pqr)/
+
+/ab./
+
+/ab[xyz]/
+
+/abc*/
+
+/ab.c*/
+
+/a.c*/
+
+/.c*/
+
+/ac*/
+
+/(a.c*|b.c*)/
+
+/a.c*|aba/
+
+/.+a/
+
+/(?=abcda)a.*/
+
+/(?=a)a.*/
+
+/a(b)*/
+
+/a\d*/
+
+/ab\d*/
+
+/a(\d)*/
+
+/abcde{0,0}/
+
+/ab\d+/
+
+/a(?(1)b)/
+
+/a(?(1)bag|big)/
+
+/a(?(1)bag|big)*/
+
+/a(?(1)bag|big)+/
+
+/a(?(1)b..|b..)/
+
+/ab\d{0}e/
+
+/a?b?/
+    a
+    b
+    ab
+    \
+    *** Failers
+    \N     
+    
+/|-/
+    abcd
+    -abc
+    \Nab-c
+    *** Failers
+    \Nabc     
+
+/a*(b+)(z)(z)/P
+    aaaabbbbzzzz
+    aaaabbbbzzzz\O0
+    aaaabbbbzzzz\O1
+    aaaabbbbzzzz\O2
+    aaaabbbbzzzz\O3
+    aaaabbbbzzzz\O4
+    aaaabbbbzzzz\O5
+    
+/^.?abcd/S 
+
+/\(             # ( at start
+  (?:           # Non-capturing bracket
+  (?>[^()]+)    # Either a sequence of non-brackets (no backtracking)
+  |             # Or
+  (?R)          # Recurse - i.e. nested bracketed string
+  )*            # Zero or more contents
+  \)            # Closing )
+  /x
+    (abcd)
+    (abcd)xyz
+    xyz(abcd)
+    (ab(xy)cd)pqr 
+    (ab(xycd)pqr 
+    () abc () 
+    12(abcde(fsh)xyz(foo(bar))lmno)89
+    *** Failers
+    abcd 
+    abcd)
+    (abcd  
+
+/\(  ( (?>[^()]+) | (?R) )* \) /xg
+    (ab(xy)cd)pqr 
+    1(abcd)(x(y)z)pqr
+
+/\(  (?: (?>[^()]+) | (?R) ) \) /x
+    (abcd)
+    (ab(xy)cd)
+    (a(b(c)d)e) 
+    ((ab)) 
+    *** Failers
+    ()   
+
+/\(  (?: (?>[^()]+) | (?R) )? \) /x
+    ()
+    12(abcde(fsh)xyz(foo(bar))lmno)89
+
+/\(  ( (?>[^()]+) | (?R) )* \) /x
+    (ab(xy)cd)
+
+/\( ( ( (?>[^()]+) | (?R) )* ) \) /x
+    (ab(xy)cd)
+
+/\( (123)? ( ( (?>[^()]+) | (?R) )* ) \) /x
+    (ab(xy)cd)
+    (123ab(xy)cd)
+
+/\( ( (123)? ( (?>[^()]+) | (?R) )* ) \) /x
+    (ab(xy)cd)
+    (123ab(xy)cd)
+
+/\( (((((((((( ( (?>[^()]+) | (?R) )* )))))))))) \) /x
+    (ab(xy)cd)
+
+/\( ( ( (?>[^()<>]+) | ((?>[^()]+)) | (?R) )* ) \) /x
+    (abcd(xyz<p>qrs)123)
+
+/\( ( ( (?>[^()]+) | ((?R)) )* ) \) /x
+    (ab(cd)ef)
+    (ab(cd(ef)gh)ij)
+
+/^[[:alnum:]]/D
+
+/^[[:^alnum:]]/D
+
+/^[[:alpha:]]/D
+
+/^[[:^alpha:]]/D
+             
+/^[[:ascii:]]/D
+
+/^[[:^ascii:]]/D
+
+/^[[:blank:]]/D
+
+/^[[:cntrl:]]/D
+
+/^[[:digit:]]/D
+
+/^[[:graph:]]/D
+
+/^[[:lower:]]/D
+
+/^[[:print:]]/D
+
+/^[[:punct:]]/D
+
+/^[[:space:]]/D
+
+/^[[:upper:]]/D
+
+/^[[:xdigit:]]/D
+
+/^[[:word:]]/D
+
+/^[[:^cntrl:]]/D
+
+/^[12[:^digit:]]/D
+
+/^[[:^blank:]]/D
+
+/[01[:alpha:]%]/D
+
+/[[.ch.]]/
+
+/[[=ch=]]/
+
+/[[:rhubarb:]]/
+
+/[[:upper:]]/i
+    A
+    a 
+    
+/[[:lower:]]/i
+    A
+    a 
+
+/((?-i)[[:lower:]])[[:lower:]]/i
+    ab
+    aB
+    *** Failers
+    Ab
+    AB        
+
+/[\200-\410]/
+
+/^(?(0)f|b)oo/
+
+/This one's here because of the large output vector needed/
+
+/(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\w+)\s+(\270)/
+    \O900 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 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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 ABC ABC
+
+/This one's here because Perl does this differently and PCRE can't at present/
+
+/(main(O)?)+/
+    mainmain
+    mainOmain
+    
+/These are all cases where Perl does it differently (nested captures)/
+
+/^(a(b)?)+$/
+    aba
+   
+/^(aa(bb)?)+$/
+    aabbaa    
+    
+/^(aa|aa(bb))+$/
+    aabbaa 
+    
+/^(aa(bb)??)+$/
+    aabbaa    
+    
+/^(?:aa(bb)?)+$/
+    aabbaa    
+    
+/^(aa(b(b))?)+$/
+    aabbaa    
+
+/^(?:aa(b(b))?)+$/
+    aabbaa    
+
+/^(?:aa(b(?:b))?)+$/
+    aabbaa    
+
+/^(?:aa(bb(?:b))?)+$/
+    aabbbaa    
+    
+/^(?:aa(b(?:bb))?)+$/
+    aabbbaa    
+
+/^(?:aa(?:b(b))?)+$/
+    aabbaa    
+
+/^(?:aa(?:b(bb))?)+$/
+    aabbbaa    
+
+/^(aa(b(bb))?)+$/
+    aabbbaa    
+
+/^(aa(bb(bb))?)+$/
+    aabbbbaa    
+
+/--------------------------------------------------------------------/ 
+    
+/#/xMD
+
+/a#/xMD
+
+/[\s]/D
+
+/[\S]/D
+
+/a(?i)b/D
+    ab
+    aB
+    *** Failers 
+    AB  
+
+/(a(?i)b)/D
+    ab
+    aB
+    *** Failers 
+    AB  
+    
+/   (?i)abc/xD
+
+/#this is a comment
+  (?i)abc/xD
+
+/123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890/D
+
+/\Q123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890/D
+
+/\Q\E/D
+    \
+
+/\Q\Ex/D
+
+/ \Q\E/D
+
+/a\Q\E/D
+  abc
+  bca
+  bac  
+
+/a\Q\Eb/D
+  abc
+
+/\Q\Eabc/D
+
+/x*+\w/D
+    *** Failers
+    xxxxx
+    
+/x?+/D
+
+/x++/D
+
+/x{1,3}+/D 
+
+/(x)*+/D
+
+/^(\w++|\s++)*$/
+    now is the time for all good men to come to the aid of the party
+    *** Failers
+    this is not a line with only words and spaces!
+    
+/(\d++)(\w)/
+    12345a
+    *** Failers
+    12345+ 
+
+/a++b/
+    aaab
+
+/(a++b)/
+    aaab
+
+/(a++)b/
+    aaab
+
+/([^()]++|\([^()]*\))+/
+    ((abc(ade)ufh()()x
+    
+/\(([^()]++|\([^()]+\))+\)/ 
+    (abc)
+    (abc(def)xyz)
+    *** Failers
+    ((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa   
+
+/(abc){1,3}+/D
+
+/a+?+/
+
+/a{2,3}?+b/
+
+/(?U)a+?+/
+
+/a{2,3}?+b/U
+
+/x(?U)a++b/D
+    xaaaab
+
+/(?U)xa++b/D
+    xaaaab
+
+/^((a+)(?U)([ab]+)(?-U)([bc]+)(\w*))/D
+
+/^x(?U)a+b/D
+
+/^x(?U)(a+)b/D
+
+/[.x.]/
+
+/[=x=]/
+
+/[:x:]/
+
+/\l/
+
+/\L/
+
+/\N{name}/
+
+/\u/
+
+/\U/
+
+/[/
+
+/[a-/
+
+/[[:space:]/
+
+/[\s]/DM
+
+/[[:space:]]/DM
+
+/[[:space:]abcde]/DM
+
+/< (?: (?(R) \d++  | [^<>]*+) | (?R)) * >/x
+    <>
+    <abcd>
+    <abc <123> hij>
+    <abc <def> hij>
+    <abc<>def> 
+    <abc<>      
+    *** Failers
+    <abc
+
+|8J\$WE\<\.rX\+ix\[d1b\!H\#\?vV0vrK\:ZH1\=2M\>iV\;\?aPhFB\<\*vW\@QW\@sO9\}cfZA\-i\'w\%hKd6gt1UJP\,15_\#QY\$M\^Mss_U\/\]\&LK9\[5vQub\^w\[KDD\<EjmhUZ\?\.akp2dF\>qmj\;2\}YWFdYx\.Ap\]hjCPTP\(n28k\+3\;o\&WXqs\/gOXdr\$\:r\'do0\;b4c\(f_Gr\=\"\\4\)\[01T7ajQJvL\$W\~mL_sS\/4h\:x\*\[ZN\=KLs\&L5zX\/\/\>it\,o\:aU\(\;Z\>pW\&T7oP\'2K\^E\:x9\'c\[\%z\-\,64JQ5AeH_G\#KijUKghQw\^\\vea3a\?kka_G\$8\#\`\*kynsxzBLru\'\]k_\[7FrVx\}\^\=\$blx\>s\-N\%j\;D\*aZDnsw\:YKZ\%Q\.Kne9\#hP\?\+b3\(SOvL\,\^\;\&u5\@\?5C5Bhb\=m\-vEh_L15Jl\]U\)0RP6\{q\%L\^_z5E\'Dw6X\b|DM
+
+|\$\<\.X\+ix\[d1b\!H\#\?vV0vrK\:ZH1\=2M\>iV\;\?aPhFB\<\*vW\@QW\@sO9\}cfZA\-i\'w\%hKd6gt1UJP\,15_\#QY\$M\^Mss_U\/\]\&LK9\[5vQub\^w\[KDD\<EjmhUZ\?\.akp2dF\>qmj\;2\}YWFdYx\.Ap\]hjCPTP\(n28k\+3\;o\&WXqs\/gOXdr\$\:r\'do0\;b4c\(f_Gr\=\"\\4\)\[01T7ajQJvL\$W\~mL_sS\/4h\:x\*\[ZN\=KLs\&L5zX\/\/\>it\,o\:aU\(\;Z\>pW\&T7oP\'2K\^E\:x9\'c\[\%z\-\,64JQ5AeH_G\#KijUKghQw\^\\vea3a\?kka_G\$8\#\`\*kynsxzBLru\'\]k_\[7FrVx\}\^\=\$blx\>s\-N\%j\;D\*aZDnsw\:YKZ\%Q\.Kne9\#hP\?\+b3\(SOvL\,\^\;\&u5\@\?5C5Bhb\=m\-vEh_L15Jl\]U\)0RP6\{q\%L\^_z5E\'Dw6X\b|DM
+
+/(.*)\d+\1/I
+
+/(.*)\d+/I
+    
+/(.*)\d+\1/Is
+
+/(.*)\d+/Is
+
+/(.*(xyz))\d+\2/I
+
+/((.*))\d+\1/I
+    abc123bc
+    
+/a[b]/I
+
+/(?=a).*/I
+
+/(?=abc).xyz/iI
+
+/(?=abc)(?i).xyz/I
+
+/(?=a)(?=b)/I
+
+/(?=.)a/I
+
+/((?=abcda)a)/I
+
+/((?=abcda)ab)/I
+
+/()a/I
+
+/(?(1)ab|ac)/I
+
+/(?(1)abz|acz)/I
+
+/(?(1)abz)/I
+
+/(?(1)abz)123/I
+
+/(a)+/I
+
+/(a){2,3}/I
+
+/(a)*/I
+
+/[a]/I
+
+/[ab]/I
+
+/[ab]/IS
+
+/[^a]/I
+
+/\d456/I
+
+/\d456/IS
+
+/a^b/I
+
+/^a/mI
+  abcde
+  xy\nabc 
+  *** Failers 
+  xyabc 
+
+/c|abc/I
+
+/(?i)[ab]/IS
+
+/[ab](?i)cd/IS
+
+/abc(?C)def/
+    abcdef
+    1234abcdef 
+    *** Failers
+    abcxyz
+    abcxyzf   
+
+/abc(?C)de(?C1)f/
+    123abcdef
+    
+/(?C1)\dabc(?C2)def/ 
+    1234abcdef
+    *** Failers
+    abcdef 
+    
+/(?C255)ab/
+
+/(?C256)ab/
+
+/(?Cab)xx/ 
+
+/(?C12vr)x/
+
+/abc(?C)def/
+    *** Failers
+    \x83\x0\x61bcdef
+
+/(abc)(?C)de(?C1)f/
+    123abcdef
+    123abcdef\C+ 
+    123abcdef\C- 
+    *** Failers
+    123abcdef\C!1 
+    
+/(?C0)(abc(?C1))*/
+    abcabcabc
+    abcabc\C!1!3   
+    *** Failers
+    abcabcabc\C!1!3   
+
+/(\d{3}(?C))*/
+    123\C+
+    123456\C+
+    123456789\C+  
+
+/((xyz)(?C)p|(?C1)xyzabc)/
+    xyzabc\C+
+
+/(X)((xyz)(?C)p|(?C1)xyzabc)/
+    Xxyzabc\C+
+
+/(?=(abc))(?C)abcdef/
+    abcdef\C+
+    
+/(?!(abc)(?C1)d)(?C2)abcxyz/
+    abcxyz\C+ 
+
+/(?<=(abc)(?C))xyz/
+   abcxyz\C+
+   
+/(?C)abc/ 
+
+/(?C)^abc/
+
+/(?C)a|b/S
+
+/(?R)/
+
+/(a|(?R))/
+
+/(ab|(bc|(de|(?R))))/
+
+/x(ab|(bc|(de|(?R))))/
+    xab
+    xbc
+    xde
+    xxab
+    xxxab
+    *** Failers
+    xyab   
+
+/(ab|(bc|(de|(?1))))/
+
+/x(ab|(bc|(de|(?1)x)x)x)/
+
+/^([^()]|\((?1)*\))*$/
+    abc
+    a(b)c
+    a(b(c))d  
+    *** Failers)
+    a(b(c)d  
+
+/^>abc>([^()]|\((?1)*\))*<xyz<$/
+   >abc>123<xyz<
+   >abc>1(2)3<xyz<
+   >abc>(1(2)3)<xyz<
+
+/(a(?1)b)/D
+
+/(a(?1)+b)/D
+
+/^\W*(?:((.)\W*(?1)\W*\2|)|((.)\W*(?3)\W*\4|\W*.\W*))\W*$/i
+    1221
+    Satan, oscillate my metallic sonatas!
+    A man, a plan, a canal: Panama!
+    Able was I ere I saw Elba. 
+    *** Failers
+    The quick brown fox  
+    
+/^(\d+|\((?1)([+*-])(?1)\)|-(?1))$/
+    12
+    (((2+2)*-3)-7)
+    -12
+    *** Failers
+    ((2+2)*-3)-7)
+         
+/^(x(y|(?1){2})z)/
+    xyz
+    xxyzxyzz 
+    *** Failers
+    xxyzz
+    xxyzxyzxyzz   
+
+/((< (?: (?(R) \d++  | [^<>]*+) | (?2)) * >))/x
+    <>
+    <abcd>
+    <abc <123> hij>
+    <abc <def> hij>
+    <abc<>def> 
+    <abc<>      
+    *** Failers
+    <abc
+
+/(?1)/
+
+/((?2)(abc)/
+
+/^(abc)def(?1)/
+    abcdefabc
+
+/^(a|b|c)=(?1)+/
+    a=a
+    a=b
+    a=bc  
+
+/^(a|b|c)=((?1))+/
+    a=a
+    a=b
+    a=bc  
+
+/a(?P<name1>b|c)d(?P<longername2>e)/D
+    abde
+    acde 
+
+/(?:a(?P<c>c(?P<d>d)))(?P<a>a)/D
+
+/(?P<a>a)...(?P=a)bbb(?P>a)d/D
+
+/^\W*(?:(?P<one>(?P<two>.)\W*(?P>one)\W*(?P=two)|)|(?P<three>(?P<four>.)\W*(?P>three)\W*(?P=four)|\W*.\W*))\W*$/i
+    1221
+    Satan, oscillate my metallic sonatas!
+    A man, a plan, a canal: Panama!
+    Able was I ere I saw Elba. 
+    *** Failers
+    The quick brown fox  
+    
+/((?(R)a|b))\1(?1)?/
+  bb
+  bbaa 
+
+/(.*)a/sI
+
+/(.*)a\1/sI
+
+/(.*)a(b)\2/sI
+
+/((.*)a|(.*)b)z/sI
+
+/((.*)a|(.*)b)z\1/sI
+
+/((.*)a|(.*)b)z\2/sI
+
+/((.*)a|(.*)b)z\3/sI
+
+/((.*)a|^(.*)b)z\3/sI
+
+/(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)a/sI
+
+/(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)a\31/sI
+
+/(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)a\32/sI
+
+/(a)(bc)/ND
+  abc
+
+/(?P<one>a)(bc)/ND
+  abc
+
+/(a)(?P<named>bc)/ND
+
+/(a+)*zz/
+  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaazzbbbbbb\M
+  aaaaaaaaaaaaaz\M
+
+/(aaa(?C1)bbb|ab)/
+   aaabbb
+   aaabbb\C*0
+   aaabbb\C*1
+   aaabbb\C*-1
+
+/ab(?P<one>cd)ef(?P<two>gh)/
+    abcdefgh
+    abcdefgh\C1\Gtwo
+    abcdefgh\Cone\Ctwo
+    abcdefgh\Cthree  
+
+/(?P<Tes>)(?P<Test>)/D
+
+/(?P<Test>)(?P<Tes>)/D
+
+/(?P<Z>zz)(?P<A>aa)/
+    zzaa\CZ
+    zzaa\CA
+
+/(?P<x>eks)(?P<x>eccs)/
+
+/(?P<abc>abc(?P<def>def)(?P<abc>xyz))/
+
+"\[((?P<elem>\d+)(,(?P>elem))*)\]"
+    [10,20,30,5,5,4,4,2,43,23,4234]
+    *** Failers
+    []  
+
+"\[((?P<elem>\d+)(,(?P>elem))*)?\]"
+    [10,20,30,5,5,4,4,2,43,23,4234]
+    [] 
+
+/(a(b(?2)c))?/D
+
+/(a(b(?2)c))*/D
+
+/(a(b(?2)c)){0,2}/D
+
+/[ab]{1}+/D
+
+/((w\/|-|with)*(free|immediate)*.*?shipping\s*[!.-]*)/i
+     Baby Bjorn Active Carrier - With free SHIPPING!!
+
+/((w\/|-|with)*(free|immediate)*.*?shipping\s*[!.-]*)/iS
+     Baby Bjorn Active Carrier - With free SHIPPING!!
+     
+/a*.*b/SD
+
+/(a|b)*.?c/SD 
+
+/abc(?C255)de(?C)f/D
+
+/abcde/CD
+  abcde
+  abcdfe 
+  
+/a*b/CD
+  ab
+  aaaab
+  aaaacb   
+
+/a+b/CD
+  ab
+  aaaab
+  aaaacb   
+
+/(abc|def)x/CD
+  abcx
+  defx
+  abcdefzx
+
+/(ab|cd){3,4}/C
+  ababab
+  abcdabcd
+  abcdcdcdcdcd  
+
+/([ab]{,4}c|xy)/CD
+    Note: that { does NOT introduce a quantifier
+
+/([ab]{1,4}c|xy){4,5}?123/CD
+    aacaacaacaacaac123
+
+/\b.*/I
+  ab cd\>1
+  
+/\b.*/Is 
+  ab cd\>1
+  
+/(?!.bcd).*/I
+  Xbcd12345 
+
+/abcde/
+    ab\P
+    abc\P
+    abcd\P
+    abcde\P   
+    the quick brown abc\P
+    ** Failers\P
+    the quick brown abxyz fox\P
+    
+"^(0?[1-9]|[12][0-9]|3[01])/(0?[1-9]|1[012])/(20)?\d\d$"
+    13/05/04\P
+    13/5/2004\P
+    02/05/09\P 
+    1\P
+    1/2\P
+    1/2/0\P
+    1/2/04\P    
+    0\P
+    02/\P
+    02/0\P   
+    02/1\P
+    ** Failers\P
+    \P
+    123\P
+    33/4/04\P
+    3/13/04\P
+    0/1/2003\P
+    0/\P 
+    02/0/\P 
+    02/13\P  
+
+/0{0,2}ABC/I
+    
+/\d{3,}ABC/I
+    
+/\d*ABC/I
+
+/[abc]+DE/I
+
+/[abc]?123/
+    123\P
+    a\P
+    b\P
+    c\P
+    c12\P
+    c123\P      
+
+/^(?:\d){3,5}X/
+    1\P
+    123\P
+    123X
+    1234\P
+    1234X
+    12345\P
+    12345X      
+    *** Failers 
+    1X 
+    123456\P 
+
+/abc/>testsavedregex
+<testsavedregex
+    abc
+    ** Failers
+    bca
+    
+/abc/F>testsavedregex
+<testsavedregex
+    abc
+    ** Failers
+    bca
+
+/(a|b)/S>testsavedregex
+<testsavedregex
+    abc
+    ** Failers
+    def  
+    
+/(a|b)/SF>testsavedregex
+<testsavedregex
+    abc
+    ** Failers
+    def  
+    
+~<(\w+)/?>(.)*</(\1)>~smg
+    <!DOCTYPE seite SYSTEM "http://www.lco.lineas.de/xmlCms.dtd">\n<seite>\n<dokumenteninformation>\n<seitentitel>Partner der LCO</seitentitel>\n<sprache>de</sprache>\n<seitenbeschreibung>Partner der LINEAS Consulting\nGmbH</seitenbeschreibung>\n<schluesselworte>LINEAS Consulting GmbH Hamburg\nPartnerfirmen</schluesselworte>\n<revisit>30 days</revisit>\n<robots>index,follow</robots>\n<menueinformation>\n<aktiv>ja</aktiv>\n<menueposition>3</menueposition>\n<menuetext>Partner</menuetext>\n</menueinformation>\n<lastedited>\n<autor>LCO</autor>\n<firma>LINEAS Consulting</firma>\n<datum>15.10.2003</datum>\n</lastedited>\n</dokumenteninformation>\n<inhalt>\n\n<absatzueberschrift>Die Partnerfirmen der LINEAS Consulting\nGmbH</absatzueberschrift>\n\n<absatz><link ziel="http://www.ca.com/" zielfenster="_blank">\n<bild name="logo_ca.gif" rahmen="no"/></link> <link\nziel="http://www.ey.com/" zielfenster="_blank"><bild\nname="logo_euy.gif" rahmen="no"/></link>\n</absatz>\n\n<absatz><link ziel="http://www.cisco.de/" zielfenster="_blank">\n<bild name="logo_cisco.gif" rahmen="ja"/></link></absatz>\n\n<absatz><link ziel="http://www.atelion.de/"\nzielfenster="_blank"><bild\nname="logo_atelion.gif" rahmen="no"/></link>\n</absatz>\n\n<absatz><link ziel="http://www.line-information.de/"\nzielfenster="_blank">\n<bild name="logo_line_information.gif" rahmen="no"/></link>\n</absatz>\n\n<absatz><bild name="logo_aw.gif" rahmen="no"/></absatz>\n\n<absatz><link ziel="http://www.incognis.de/"\nzielfenster="_blank"><bild\nname="logo_incognis.gif" rahmen="no"/></link></absatz>\n\n<absatz><link ziel="http://www.addcraft.com/"\nzielfenster="_blank"><bild\nname="logo_addcraft.gif" rahmen="no"/></link></absatz>\n\n<absatz><link ziel="http://www.comendo.com/"\nzielfenster="_blank"><bild\nname="logo_comendo.gif" rahmen="no"/></link></absatz>\n\n</inhalt>\n</seite>
+
+/^a/IF
+
+/ End of testinput2 /
diff --git a/connectors/jk/native/iis/pcre/testdata/testinput3 b/connectors/jk/native/iis/pcre/testdata/testinput3
new file mode 100644
index 0000000..c2abdbf
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/testdata/testinput3
@@ -0,0 +1,65 @@
+/^[\w]+/
+    *** Failers
+    École
+
+/^[\w]+/Lfr_FR
+    École
+
+/^[\w]+/
+    *** Failers
+    École
+
+/^[\W]+/
+    École
+
+/^[\W]+/Lfr_FR
+    *** Failers
+    École
+
+/[\b]/
+    \b
+    *** Failers
+    a
+
+/[\b]/Lfr_FR
+    \b
+    *** Failers
+    a
+
+/^\w+/
+    *** Failers
+    École
+
+/^\w+/Lfr_FR
+    École
+
+/(.+)\b(.+)/
+    École
+
+/(.+)\b(.+)/Lfr_FR
+    *** Failers
+    École
+
+/École/i
+    École
+    *** Failers
+    école
+
+/École/iLfr_FR
+    École
+    école
+
+/\w/IS
+
+/\w/ISLfr_FR
+
+/^[\xc8-\xc9]/iLfr_FR
+    École
+    école
+
+/^[\xc8-\xc9]/Lfr_FR
+    École
+    *** Failers 
+    école
+
+/ End of testinput3 /
diff --git a/connectors/jk/native/iis/pcre/testdata/testinput4 b/connectors/jk/native/iis/pcre/testdata/testinput4
new file mode 100644
index 0000000..c16e9d9
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/testdata/testinput4
@@ -0,0 +1,513 @@
+/-- Do not use the \x{} construct except with patterns that have the --/
+/-- /8 option set, because PCRE doesn't recognize them as UTF-8 unless --/
+/-- that option is set. However, the latest Perls recognize them always. --/
+
+/a.b/8
+    acb
+    a\x7fb
+    a\x{100}b 
+    *** Failers
+    a\nb  
+
+/a(.{3})b/8
+    a\x{4000}xyb 
+    a\x{4000}\x7fyb 
+    a\x{4000}\x{100}yb 
+    *** Failers
+    a\x{4000}b 
+    ac\ncb 
+
+/a(.*?)(.)/
+    a\xc0\x88b
+
+/a(.*?)(.)/8
+    a\x{100}b
+
+/a(.*)(.)/
+    a\xc0\x88b
+
+/a(.*)(.)/8
+    a\x{100}b
+
+/a(.)(.)/
+    a\xc0\x92bcd
+
+/a(.)(.)/8
+    a\x{240}bcd
+
+/a(.?)(.)/
+    a\xc0\x92bcd
+
+/a(.?)(.)/8
+    a\x{240}bcd
+
+/a(.??)(.)/
+    a\xc0\x92bcd
+
+/a(.??)(.)/8
+    a\x{240}bcd
+
+/a(.{3})b/8
+    a\x{1234}xyb 
+    a\x{1234}\x{4321}yb 
+    a\x{1234}\x{4321}\x{3412}b 
+    *** Failers
+    a\x{1234}b 
+    ac\ncb 
+
+/a(.{3,})b/8
+    a\x{1234}xyb 
+    a\x{1234}\x{4321}yb 
+    a\x{1234}\x{4321}\x{3412}b 
+    axxxxbcdefghijb 
+    a\x{1234}\x{4321}\x{3412}\x{3421}b 
+    *** Failers
+    a\x{1234}b 
+
+/a(.{3,}?)b/8
+    a\x{1234}xyb 
+    a\x{1234}\x{4321}yb 
+    a\x{1234}\x{4321}\x{3412}b 
+    axxxxbcdefghijb 
+    a\x{1234}\x{4321}\x{3412}\x{3421}b 
+    *** Failers
+    a\x{1234}b 
+
+/a(.{3,5})b/8
+    a\x{1234}xyb 
+    a\x{1234}\x{4321}yb 
+    a\x{1234}\x{4321}\x{3412}b 
+    axxxxbcdefghijb 
+    a\x{1234}\x{4321}\x{3412}\x{3421}b 
+    axbxxbcdefghijb 
+    axxxxxbcdefghijb 
+    *** Failers
+    a\x{1234}b 
+    axxxxxxbcdefghijb 
+
+/a(.{3,5}?)b/8
+    a\x{1234}xyb 
+    a\x{1234}\x{4321}yb 
+    a\x{1234}\x{4321}\x{3412}b 
+    axxxxbcdefghijb 
+    a\x{1234}\x{4321}\x{3412}\x{3421}b 
+    axbxxbcdefghijb 
+    axxxxxbcdefghijb 
+    *** Failers
+    a\x{1234}b 
+    axxxxxxbcdefghijb 
+
+/^[a\x{c0}]/8
+    *** Failers
+    \x{100}
+
+/(?<=aXb)cd/8
+    aXbcd
+
+/(?<=a\x{100}b)cd/8
+    a\x{100}bcd
+
+/(?<=a\x{100000}b)cd/8
+    a\x{100000}bcd
+    
+/(?:\x{100}){3}b/8
+    \x{100}\x{100}\x{100}b
+    *** Failers 
+    \x{100}\x{100}b
+
+/\x{ab}/8
+    \x{ab} 
+    \xc2\xab
+    *** Failers 
+    \x00{ab}
+
+/(?<=(.))X/8
+    WXYZ
+    \x{256}XYZ 
+    *** Failers
+    XYZ 
+
+/X(\C{3})/8
+    X\x{1234}
+
+/X(\C{4})/8
+    X\x{1234}YZ
+    
+/X\C*/8
+    XYZabcdce
+    
+/X\C*?/8
+    XYZabcde
+    
+/X\C{3,5}/8
+    Xabcdefg   
+    X\x{1234} 
+    X\x{1234}YZ
+    X\x{1234}\x{512}  
+    X\x{1234}\x{512}YZ
+
+/X\C{3,5}?/8
+    Xabcdefg   
+    X\x{1234} 
+    X\x{1234}YZ
+    X\x{1234}\x{512}  
+
+/[^a]+/8g
+    bcd
+    \x{100}aY\x{256}Z 
+    
+/^[^a]{2}/8
+    \x{100}bc
+ 
+/^[^a]{2,}/8
+    \x{100}bcAa
+
+/^[^a]{2,}?/8
+    \x{100}bca
+
+/[^a]+/8ig
+    bcd
+    \x{100}aY\x{256}Z 
+    
+/^[^a]{2}/8i
+    \x{100}bc
+ 
+/^[^a]{2,}/8i
+    \x{100}bcAa
+
+/^[^a]{2,}?/8i
+    \x{100}bca
+
+/\x{100}{0,0}/8
+    abcd
+ 
+/\x{100}?/8
+    abcd
+    \x{100}\x{100} 
+
+/\x{100}{0,3}/8 
+    \x{100}\x{100} 
+    \x{100}\x{100}\x{100}\x{100} 
+    
+/\x{100}*/8
+    abce
+    \x{100}\x{100}\x{100}\x{100} 
+
+/\x{100}{1,1}/8
+    abcd\x{100}\x{100}\x{100}\x{100} 
+
+/\x{100}{1,3}/8
+    abcd\x{100}\x{100}\x{100}\x{100} 
+
+/\x{100}+/8
+    abcd\x{100}\x{100}\x{100}\x{100} 
+
+/\x{100}{3}/8
+    abcd\x{100}\x{100}\x{100}XX
+
+/\x{100}{3,5}/8
+    abcd\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}XX
+
+/\x{100}{3,}/8
+    abcd\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}XX
+
+/(?<=a\x{100}{2}b)X/8+
+    Xyyya\x{100}\x{100}bXzzz
+
+/\D*/8
+  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+
+/\D*/8
+  \x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}
+
+/\D/8
+    1X2
+    1\x{100}2 
+  
+/>\S/8
+    > >X Y
+    > >\x{100} Y
+  
+/\d/8
+    \x{100}3
+    
+/\s/8
+    \x{100} X
+    
+/\D+/8
+    12abcd34
+    *** Failers
+    1234  
+
+/\D{2,3}/8
+    12abcd34
+    12ab34
+    *** Failers  
+    1234
+    12a34  
+
+/\D{2,3}?/8
+    12abcd34
+    12ab34
+    *** Failers  
+    1234
+    12a34  
+
+/\d+/8
+    12abcd34
+    *** Failers
+
+/\d{2,3}/8
+    12abcd34
+    1234abcd
+    *** Failers  
+    1.4 
+
+/\d{2,3}?/8
+    12abcd34
+    1234abcd
+    *** Failers  
+    1.4 
+
+/\S+/8
+    12abcd34
+    *** Failers
+    \    \ 
+
+/\S{2,3}/8
+    12abcd34
+    1234abcd
+    *** Failers
+    \     \  
+
+/\S{2,3}?/8
+    12abcd34
+    1234abcd
+    *** Failers
+    \     \  
+
+/>\s+</8+
+    12>      <34
+    *** Failers
+
+/>\s{2,3}</8+
+    ab>  <cd
+    ab>   <ce
+    *** Failers
+    ab>    <cd 
+
+/>\s{2,3}?</8+
+    ab>  <cd
+    ab>   <ce
+    *** Failers
+    ab>    <cd 
+
+/\w+/8
+    12      34
+    *** Failers
+    +++=*! 
+
+/\w{2,3}/8
+    ab  cd
+    abcd ce
+    *** Failers
+    a.b.c
+
+/\w{2,3}?/8
+    ab  cd
+    abcd ce
+    *** Failers
+    a.b.c
+
+/\W+/8
+    12====34
+    *** Failers
+    abcd 
+
+/\W{2,3}/8
+    ab====cd
+    ab==cd
+    *** Failers
+    a.b.c
+
+/\W{2,3}?/8
+    ab====cd
+    ab==cd
+    *** Failers
+    a.b.c
+
+/[\x{100}]/8
+    \x{100}
+    Z\x{100}
+    \x{100}Z
+    *** Failers 
+
+/[Z\x{100}]/8
+    Z\x{100}
+    \x{100}
+    \x{100}Z
+    *** Failers 
+
+/[\x{100}\x{200}]/8
+   ab\x{100}cd
+   ab\x{200}cd
+   *** Failers  
+
+/[\x{100}-\x{200}]/8
+   ab\x{100}cd
+   ab\x{200}cd
+   ab\x{111}cd 
+   *** Failers  
+
+/[z-\x{200}]/8
+   ab\x{100}cd
+   ab\x{200}cd
+   ab\x{111}cd 
+   abzcd
+   ab|cd  
+   *** Failers  
+
+/[Q\x{100}\x{200}]/8
+   ab\x{100}cd
+   ab\x{200}cd
+   Q? 
+   *** Failers  
+
+/[Q\x{100}-\x{200}]/8
+   ab\x{100}cd
+   ab\x{200}cd
+   ab\x{111}cd 
+   Q? 
+   *** Failers  
+
+/[Qz-\x{200}]/8
+   ab\x{100}cd
+   ab\x{200}cd
+   ab\x{111}cd 
+   abzcd
+   ab|cd  
+   Q? 
+   *** Failers  
+
+/[\x{100}\x{200}]{1,3}/8
+   ab\x{100}cd
+   ab\x{200}cd
+   ab\x{200}\x{100}\x{200}\x{100}cd
+   *** Failers  
+
+/[\x{100}\x{200}]{1,3}?/8
+   ab\x{100}cd
+   ab\x{200}cd
+   ab\x{200}\x{100}\x{200}\x{100}cd
+   *** Failers  
+
+/[Q\x{100}\x{200}]{1,3}/8
+   ab\x{100}cd
+   ab\x{200}cd
+   ab\x{200}\x{100}\x{200}\x{100}cd
+   *** Failers  
+
+/[Q\x{100}\x{200}]{1,3}?/8
+   ab\x{100}cd
+   ab\x{200}cd
+   ab\x{200}\x{100}\x{200}\x{100}cd
+   *** Failers  
+
+/(?<=[\x{100}\x{200}])X/8
+    abc\x{200}X
+    abc\x{100}X 
+    *** Failers
+    X  
+
+/(?<=[Q\x{100}\x{200}])X/8
+    abc\x{200}X
+    abc\x{100}X 
+    abQX 
+    *** Failers
+    X  
+
+/(?<=[\x{100}\x{200}]{3})X/8
+    abc\x{100}\x{200}\x{100}X
+    *** Failers
+    abc\x{200}X
+    X  
+
+/[^\x{100}\x{200}]X/8
+    AX
+    \x{150}X
+    \x{500}X 
+    *** Failers
+    \x{100}X
+    \x{200}X   
+
+/[^Q\x{100}\x{200}]X/8
+    AX
+    \x{150}X
+    \x{500}X 
+    *** Failers
+    \x{100}X
+    \x{200}X   
+    QX 
+
+/[^\x{100}-\x{200}]X/8
+    AX
+    \x{500}X 
+    *** Failers
+    \x{100}X
+    \x{150}X
+    \x{200}X   
+
+/a\Cb/
+    aXb
+    a\nb
+  
+/a\Cb/8
+    aXb
+    a\nb
+    *** Failers 
+    a\x{100}b 
+
+/[z-\x{100}]/8i
+    z
+    Z 
+    \x{100}
+    *** Failers
+    \x{102}
+    y    
+
+/[\xFF]/
+    >\xff<
+
+/[\xff]/8
+    >\x{ff}<
+
+/[^\xFF]/
+    XYZ
+
+/[^\xff]/8
+    XYZ
+    \x{123} 
+
+/^[ac]*b/8
+  xb
+
+/^[ac\x{100}]*b/8
+  xb
+
+/^[^x]*b/8i
+  xb
+
+/^[^x]*b/8
+  xb
+  
+/^\d*b/8
+  xb 
+
+/(|a)/g8
+    catac
+    a\x{256}a 
+
+/^\x{85}$/8i
+    \x{85}
+
+/ End of testinput4 /
diff --git a/connectors/jk/native/iis/pcre/testdata/testinput5 b/connectors/jk/native/iis/pcre/testdata/testinput5
new file mode 100644
index 0000000..fe6ee3e
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/testdata/testinput5
@@ -0,0 +1,263 @@
+/\x{100}/8DM
+
+/\x{1000}/8DM
+
+/\x{10000}/8DM
+
+/\x{100000}/8DM
+
+/\x{1000000}/8DM
+
+/\x{4000000}/8DM
+
+/\x{7fffFFFF}/8DM
+
+/[\x{ff}]/8DM
+
+/[\x{100}]/8DM
+
+/\x{ffffffff}/8
+
+/\x{100000000}/8
+
+/^\x{100}a\x{1234}/8
+    \x{100}a\x{1234}bcd
+
+/\x80/8D
+
+/\xff/8D
+
+/\x{0041}\x{2262}\x{0391}\x{002e}/D8
+    \x{0041}\x{2262}\x{0391}\x{002e}
+    
+/\x{D55c}\x{ad6d}\x{C5B4}/D8 
+    \x{D55c}\x{ad6d}\x{C5B4} 
+
+/\x{65e5}\x{672c}\x{8a9e}/D8
+    \x{65e5}\x{672c}\x{8a9e}
+
+/\x{80}/D8
+
+/\x{084}/D8
+
+/\x{104}/D8
+
+/\x{861}/D8
+
+/\x{212ab}/D8
+
+/.{3,5}X/D8
+    \x{212ab}\x{212ab}\x{212ab}\x{861}X
+
+
+/.{3,5}?/D8
+    \x{212ab}\x{212ab}\x{212ab}\x{861}
+
+/-- These tests are here rather than in testinput4 because Perl 5.6 has --/
+/-- some problems with UTF-8 support, in the area of \x{..} where the   --/
+/-- value is < 255. It grumbles about invalid UTF-8 strings.            --/
+
+/^[a\x{c0}]b/8
+    \x{c0}b
+    
+/^([a\x{c0}]*?)aa/8
+    a\x{c0}aaaa/ 
+
+/^([a\x{c0}]*?)aa/8
+    a\x{c0}aaaa/ 
+    a\x{c0}a\x{c0}aaa/ 
+
+/^([a\x{c0}]*)aa/8
+    a\x{c0}aaaa/ 
+    a\x{c0}a\x{c0}aaa/ 
+
+/^([a\x{c0}]*)a\x{c0}/8
+    a\x{c0}aaaa/ 
+    a\x{c0}a\x{c0}aaa/ 
+    
+/-- --/ 
+    
+/(?<=\C)X/8
+    Should produce an error diagnostic
+    
+/-- This one is here not because it's different to Perl, but because the --/
+/-- way the captured single-byte is displayed. (In Perl it becomes a --/
+/-- character, and you can't tell the difference.) --/
+    
+/X(\C)(.*)/8
+    X\x{1234}
+    X\nabc 
+    
+/^[ab]/8D
+    bar
+    *** Failers
+    c
+    \x{ff}
+    \x{100}  
+
+/^[^ab]/8D
+    c
+    \x{ff}
+    \x{100}  
+    *** Failers 
+    aaa
+  
+/[^ab\xC0-\xF0]/8SD
+    \x{f1}
+    \x{bf}
+    \x{100}
+    \x{1000}   
+    *** Failers
+    \x{c0} 
+    \x{f0} 
+
+/Ä€{3,4}/8SD
+  \x{100}\x{100}\x{100}\x{100\x{100}
+
+/(\x{100}+|x)/8SD
+
+/(\x{100}*a|x)/8SD
+
+/(\x{100}{0,2}a|x)/8SD
+
+/(\x{100}{1,2}a|x)/8SD
+
+/\x{100}*(\d+|"(?1)")/8
+    1234
+    "1234" 
+    \x{100}1234
+    "\x{100}1234"  
+    \x{100}\x{100}12ab 
+    \x{100}\x{100}"12" 
+    *** Failers 
+    \x{100}\x{100}abcd
+
+/\x{100}/8D
+
+/\x{100}*/8D
+
+/a\x{100}*/8D
+
+/ab\x{100}*/8D
+
+/a\x{100}\x{101}*/8D
+
+/a\x{100}\x{101}+/8D
+
+/\x{100}*A/8D
+    A
+
+/\x{100}*\d(?R)/8D
+
+/[^\x{c4}]/D
+
+/[^\x{c4}]/8D
+
+/[\x{100}]/8DM
+    \x{100}
+    Z\x{100}
+    \x{100}Z
+    *** Failers 
+
+/[Z\x{100}]/8DM
+    Z\x{100}
+    \x{100}
+    \x{100}Z
+    *** Failers 
+
+/[\x{200}-\x{100}]/8
+
+/[Ä€-Ä„]/8
+    \x{100}
+    \x{104}
+    *** Failers
+    \x{105}
+    \x{ff}    
+
+/[z-\x{100}]/8D
+
+/[z\Qa-d]Ä€\E]/8D
+    \x{100}
+    Ā 
+
+/[\xFF]/D
+    >\xff<
+
+/[\xff]/D8
+    >\x{ff}<
+
+/[^\xFF]/D
+
+/[^\xff]/8D
+
+/[Ä-Ü]/8
+    Ö # Matches without Study
+    \x{d6}
+    
+/[Ä-Ü]/8S
+    Ö <-- Same with Study
+    \x{d6}
+    
+/[\x{c4}-\x{dc}]/8 
+    Ö # Matches without Study
+    \x{d6} 
+
+/[\x{c4}-\x{dc}]/8S
+    Ö <-- Same with Study
+    \x{d6} 
+
+/[Ã]/8
+
+/Ã/8
+
+/ÃÃÃxxx/8
+
+/ÃÃÃxxx/8?D
+
+/abc/8
+    Ã]
+    Ã
+    ÃÃÃ
+    ÃÃÃ\?
+
+/anything/8
+    \xc0\x80
+    \xc1\x8f 
+    \xe0\x9f\x80
+    \xf0\x8f\x80\x80 
+    \xf8\x87\x80\x80\x80  
+    \xfc\x83\x80\x80\x80\x80
+    \xfe\x80\x80\x80\x80\x80  
+    \xff\x80\x80\x80\x80\x80  
+    \xc3\x8f
+    \xe0\xaf\x80
+    \xe1\x80\x80
+    \xf0\x9f\x80\x80 
+    \xf1\x8f\x80\x80 
+    \xf8\x88\x80\x80\x80  
+    \xf9\x87\x80\x80\x80  
+    \xfc\x84\x80\x80\x80\x80
+    \xfd\x83\x80\x80\x80\x80
+
+/\x{100}abc(xyz(?1))/8D
+
+/[^\x{100}]abc(xyz(?1))/8D
+
+/[ab\x{100}]abc(xyz(?1))/8D
+
+/(\x{100}(b(?2)c))?/D8
+
+/(\x{100}(b(?2)c)){0,2}/D8
+
+/(\x{100}(b(?1)c))?/D8
+
+/(\x{100}(b(?1)c)){0,2}/D8
+
+/\W/8
+    A.B
+    A\x{100}B 
+  
+/\w/8
+    \x{100}X   
+
+/ End of testinput5 /
diff --git a/connectors/jk/native/iis/pcre/testdata/testinput6 b/connectors/jk/native/iis/pcre/testdata/testinput6
new file mode 100644
index 0000000..01a3947
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/testdata/testinput6
@@ -0,0 +1,517 @@
+/^\pC\pL\pM\pN\pP\pS\pZ</8
+    \x7f\x{c0}\x{30f}\x{660}\x{66c}\x{f01}\x{1680}<
+    \np\x{300}9!\$ < 
+    ** Failers 
+    ap\x{300}9!\$ < 
+  
+/^\PC/8
+    X
+    ** Failers 
+    \x7f
+  
+/^\PL/8
+    9
+    ** Failers 
+    \x{c0}
+  
+/^\PM/8
+    X
+    ** Failers 
+    \x{30f}
+  
+/^\PN/8
+    X
+    ** Failers 
+    \x{660}
+  
+/^\PP/8
+    X
+    ** Failers 
+    \x{66c}
+  
+/^\PS/8
+    X
+    ** Failers 
+    \x{f01}
+  
+/^\PZ/8
+    X
+    ** Failers 
+    \x{1680}
+    
+/^\p{Cc}/8
+    \x{017}
+    \x{09f} 
+    ** Failers
+    \x{0600} 
+  
+/^\p{Cf}/8
+    \x{601}
+    ** Failers
+    \x{09f} 
+  
+/^\p{Cn}/8
+    ** Failers
+    \x{09f} 
+  
+/^\p{Co}/8
+    \x{f8ff}
+    ** Failers
+    \x{09f} 
+  
+/^\p{Cs}/8
+    \x{dfff}
+    ** Failers
+    \x{09f} 
+  
+/^\p{Ll}/8
+    a
+    ** Failers 
+    Z
+    \x{dfff}  
+  
+/^\p{Lm}/8
+    \x{2b0}
+    ** Failers
+    a 
+  
+/^\p{Lo}/8
+    \x{1bb}
+    ** Failers
+    a 
+    \x{2b0}
+  
+/^\p{Lt}/8
+    \x{1c5}
+    ** Failers
+    a 
+    \x{2b0}
+  
+/^\p{Lu}/8
+    A
+    ** Failers
+    \x{2b0}
+  
+/^\p{Mc}/8
+    \x{903}
+    ** Failers
+    X
+    \x{300}
+       
+/^\p{Me}/8
+    \x{488}
+    ** Failers
+    X
+    \x{903}
+    \x{300}
+  
+/^\p{Mn}/8
+    \x{300}
+    ** Failers
+    X
+    \x{903}
+  
+/^\p{Nd}+/8
+    0123456789\x{660}\x{661}\x{662}\x{663}\x{664}\x{665}\x{666}\x{667}\x{668}\x{669}\x{66a}
+    \x{6f0}\x{6f1}\x{6f2}\x{6f3}\x{6f4}\x{6f5}\x{6f6}\x{6f7}\x{6f8}\x{6f9}\x{6fa}
+    \x{966}\x{967}\x{968}\x{969}\x{96a}\x{96b}\x{96c}\x{96d}\x{96e}\x{96f}\x{970}
+    ** Failers
+    X
+  
+/^\p{Nl}/8
+    \x{16ee}
+    ** Failers
+    X
+    \x{966}
+  
+/^\p{No}/8
+    \x{b2}
+    \x{b3}
+    ** Failers
+    X
+    \x{16ee}
+  
+/^\p{Pc}/8
+    \x5f
+    \x{203f}
+    ** Failers
+    X
+    -
+    \x{58a}
+  
+/^\p{Pd}/8
+    -
+    \x{58a}
+    ** Failers
+    X
+    \x{203f}
+  
+/^\p{Pe}/8
+    )
+    ]
+    }
+    \x{f3b}
+    ** Failers
+    X
+    \x{203f}
+    (
+    [
+    {
+    \x{f3c}
+  
+/^\p{Pf}/8
+    \x{bb}
+    \x{2019}
+    ** Failers
+    X
+    \x{203f}
+  
+/^\p{Pi}/8
+    \x{ab}
+    \x{2018}
+    ** Failers
+    X
+    \x{203f}
+  
+/^\p{Po}/8
+    !
+    \x{37e}
+    ** Failers
+    X
+    \x{203f}
+  
+/^\p{Ps}/8
+    (
+    [
+    {
+    \x{f3c}
+    ** Failers
+    X
+    )
+    ]
+    }
+    \x{f3b}
+  
+/^\p{Sc}+/8
+    $\x{a2}\x{a3}\x{a4}\x{a5}\x{a6}
+    \x{9f2}
+    ** Failers
+    X
+    \x{2c2}
+  
+/^\p{Sk}/8
+    \x{2c2}
+    ** Failers
+    X
+    \x{9f2}
+  
+/^\p{Sm}+/8
+    +<|~\x{ac}\x{2044}
+    ** Failers
+    X
+    \x{9f2}
+  
+/^\p{So}/8
+    \x{a6}
+    \x{482} 
+    ** Failers
+    X
+    \x{9f2}
+  
+/^\p{Zl}/8
+    \x{2028}
+    ** Failers
+    X
+    \x{2029}
+  
+/^\p{Zp}/8
+    \x{2029}
+    ** Failers
+    X
+    \x{2028}
+  
+/^\p{Zs}/8
+    \ \
+    \x{a0}
+    \x{1680}
+    \x{180e}
+    \x{2000}
+    \x{2001}     
+    ** Failers
+    \x{2028}
+    \x{200d} 
+  
+/\p{Nd}+(..)/8
+      \x{660}\x{661}\x{662}ABC
+  
+/\p{Nd}+?(..)/8
+      \x{660}\x{661}\x{662}ABC
+  
+/\p{Nd}{2,}(..)/8
+      \x{660}\x{661}\x{662}ABC
+  
+/\p{Nd}{2,}?(..)/8
+      \x{660}\x{661}\x{662}ABC
+  
+/\p{Nd}*(..)/8
+      \x{660}\x{661}\x{662}ABC
+  
+/\p{Nd}*?(..)/8
+      \x{660}\x{661}\x{662}ABC
+  
+/\p{Nd}{2}(..)/8
+      \x{660}\x{661}\x{662}ABC
+  
+/\p{Nd}{2,3}(..)/8
+      \x{660}\x{661}\x{662}ABC
+  
+/\p{Nd}{2,3}?(..)/8
+      \x{660}\x{661}\x{662}ABC
+  
+/\p{Nd}?(..)/8
+      \x{660}\x{661}\x{662}ABC
+  
+/\p{Nd}??(..)/8
+      \x{660}\x{661}\x{662}ABC
+  
+/\p{Nd}*+(..)/8
+      \x{660}\x{661}\x{662}ABC
+  
+/\p{Nd}*+(...)/8
+      \x{660}\x{661}\x{662}ABC
+  
+/\p{Nd}*+(....)/8
+      ** Failers
+      \x{660}\x{661}\x{662}ABC
+  
+/\p{Lu}/8i
+    A
+    a\x{10a0}B 
+    ** Failers 
+    a
+    \x{1d00}  
+
+/\p{^Lu}/8i
+    1234
+    ** Failers
+    ABC 
+
+/\P{Lu}/8i
+    1234
+    ** Failers
+    ABC 
+
+/(?<=A\p{Nd})XYZ/8
+    A2XYZ
+    123A5XYZPQR
+    ABA\x{660}XYZpqr
+    ** Failers
+    AXYZ
+    XYZ     
+    
+/(?<!\pL)XYZ/8
+    1XYZ
+    AB=XYZ.. 
+    XYZ 
+    ** Failers
+    WXYZ 
+
+/[\p{L}]/D
+
+/[\p{^L}]/D
+
+/[\P{L}]/D
+
+/[\P{^L}]/D
+
+/[abc\p{L}\x{0660}]/8D
+
+/[\p{Nd}]/8DM
+    1234
+
+/[\p{Nd}+-]+/8DM
+    1234
+    12-34
+    12+\x{661}-34  
+    ** Failers
+    abcd  
+
+/[\P{Nd}]+/8
+    abcd
+    ** Failers
+    1234
+
+/\D+/8
+    11111111111111111111111111111111111111111111111111111111111111111111111
+    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+     
+/\P{Nd}+/8
+    11111111111111111111111111111111111111111111111111111111111111111111111
+    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+
+/[\D]+/8
+    11111111111111111111111111111111111111111111111111111111111111111111111
+    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+
+/[\P{Nd}]+/8
+    11111111111111111111111111111111111111111111111111111111111111111111111
+    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+
+/[\D\P{Nd}]+/8
+    11111111111111111111111111111111111111111111111111111111111111111111111
+    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+
+/\pL/8
+    a
+    A 
+
+/\pL/8i
+    a
+    A 
+    
+/\p{Lu}/8 
+    A
+    aZ
+    ** Failers
+    abc   
+
+/\p{Lu}/8i
+    A
+    aZ
+    ** Failers
+    abc   
+
+/\p{Ll}/8 
+    a
+    Az
+    ** Failers
+    ABC   
+
+/\p{Ll}/8i 
+    a
+    Az
+    ** Failers
+    ABC   
+
+/^\x{c0}$/8i
+    \x{c0}
+    \x{e0} 
+
+/^\x{e0}$/8i
+    \x{c0}
+    \x{e0} 
+
+/A\x{391}\x{10427}\x{ff3a}\x{1fb0}/8
+    A\x{391}\x{10427}\x{ff3a}\x{1fb0}
+    ** Failers
+    a\x{391}\x{10427}\x{ff3a}\x{1fb0}   
+    A\x{3b1}\x{10427}\x{ff3a}\x{1fb0}
+    A\x{391}\x{1044F}\x{ff3a}\x{1fb0}
+    A\x{391}\x{10427}\x{ff5a}\x{1fb0}
+    A\x{391}\x{10427}\x{ff3a}\x{1fb8}
+
+/A\x{391}\x{10427}\x{ff3a}\x{1fb0}/8i
+    A\x{391}\x{10427}\x{ff3a}\x{1fb0}
+    a\x{391}\x{10427}\x{ff3a}\x{1fb0}   
+    A\x{3b1}\x{10427}\x{ff3a}\x{1fb0}
+    A\x{391}\x{1044F}\x{ff3a}\x{1fb0}
+    A\x{391}\x{10427}\x{ff5a}\x{1fb0}
+    A\x{391}\x{10427}\x{ff3a}\x{1fb8}
+
+/A\x{391}\x{10427}\x{ff3a}\x{1fb0}/8iD
+
+/A\x{391}\x{10427}\x{ff3a}\x{1fb0}/8D
+
+/AB\x{1fb0}/8D
+
+/AB\x{1fb0}/8Di
+
+/\x{391}+/8i
+    \x{391}\x{3b1}\x{3b1}\x{3b1}\x{391}
+
+/\x{391}{3,5}(.)/8i
+    \x{391}\x{3b1}\x{3b1}\x{3b1}\x{391}X
+
+/\x{391}{3,5}?(.)/8i
+    \x{391}\x{3b1}\x{3b1}\x{3b1}\x{391}X
+
+/[\x{391}\x{ff3a}]/8i
+    \x{391}
+    \x{ff3a}
+    \x{3b1}
+    \x{ff5a}   
+    
+/[\x{c0}\x{391}]/8i
+    \x{c0}
+    \x{e0} 
+
+/[\x{105}-\x{109}]/8iD
+    \x{104}
+    \x{105}
+    \x{109}  
+    ** Failers
+    \x{100}
+    \x{10a} 
+    
+/[z-\x{100}]/8iD 
+    Z
+    z
+    \x{39c}
+    \x{178}
+    |
+    \x{80}
+    \x{ff}
+    \x{100}
+    \x{101} 
+    ** Failers
+    \x{102}
+    Y
+    y           
+
+/[z-\x{100}]/8Di
+
+/^\X/8
+    A
+    A\x{300}BC 
+    A\x{300}\x{301}\x{302}BC 
+    *** Failers
+    \x{300}  
+
+/^[\X]/8
+    X123
+    *** Failers
+    AXYZ
+
+/^(\X*)C/8
+    A\x{300}\x{301}\x{302}BCA\x{300}\x{301} 
+    A\x{300}\x{301}\x{302}BCA\x{300}\x{301}C 
+
+/^(\X*?)C/8
+    A\x{300}\x{301}\x{302}BCA\x{300}\x{301} 
+    A\x{300}\x{301}\x{302}BCA\x{300}\x{301}C 
+
+/^(\X*)(.)/8
+    A\x{300}\x{301}\x{302}BCA\x{300}\x{301} 
+    A\x{300}\x{301}\x{302}BCA\x{300}\x{301}C 
+
+/^(\X*?)(.)/8
+    A\x{300}\x{301}\x{302}BCA\x{300}\x{301} 
+    A\x{300}\x{301}\x{302}BCA\x{300}\x{301}C 
+
+/^\X(.)/8
+    *** Failers
+    A\x{300}\x{301}\x{302}
+
+/^\X{2,3}(.)/8
+    A\x{300}\x{301}B\x{300}X
+    A\x{300}\x{301}B\x{300}C\x{300}\x{301}
+    A\x{300}\x{301}B\x{300}C\x{300}\x{301}X
+    A\x{300}\x{301}B\x{300}C\x{300}\x{301}DA\x{300}X
+    
+/^\X{2,3}?(.)/8
+    A\x{300}\x{301}B\x{300}X
+    A\x{300}\x{301}B\x{300}C\x{300}\x{301}
+    A\x{300}\x{301}B\x{300}C\x{300}\x{301}X
+    A\x{300}\x{301}B\x{300}C\x{300}\x{301}DA\x{300}X
+    
+/ End of testinput6 /
diff --git a/connectors/jk/native/iis/pcre/testdata/testoutput1 b/connectors/jk/native/iis/pcre/testdata/testoutput1
new file mode 100644
index 0000000..07e7376
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/testdata/testoutput1
Binary files differ
diff --git a/connectors/jk/native/iis/pcre/testdata/testoutput2 b/connectors/jk/native/iis/pcre/testdata/testoutput2
new file mode 100644
index 0000000..f2e7d54
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/testdata/testoutput2
@@ -0,0 +1,5607 @@
+PCRE version 5.0 13-Sep-2004
+
+/(a)b|/
+Capturing subpattern count = 1
+No options
+No first char
+No need char
+
+/abc/
+Capturing subpattern count = 0
+No options
+First char = 'a'
+Need char = 'c'
+    abc
+ 0: abc
+    defabc
+ 0: abc
+    \Aabc
+ 0: abc
+    *** Failers
+No match
+    \Adefabc
+No match
+    ABC
+No match
+
+/^abc/
+Capturing subpattern count = 0
+Options: anchored
+No first char
+No need char
+    abc
+ 0: abc
+    \Aabc
+ 0: abc
+    *** Failers
+No match
+    defabc
+No match
+    \Adefabc
+No match
+
+/a+bc/
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+First char = 'a'
+Need char = 'c'
+
+/a*bc/
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+No first char
+Need char = 'c'
+
+/a{3}bc/
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+First char = 'a'
+Need char = 'c'
+
+/(abc|a+z)/
+Capturing subpattern count = 1
+Partial matching not supported
+No options
+First char = 'a'
+No need char
+
+/^abc$/
+Capturing subpattern count = 0
+Options: anchored
+No first char
+No need char
+    abc
+ 0: abc
+    *** Failers
+No match
+    def\nabc
+No match
+
+/ab\gdef/X
+Failed: unrecognized character follows \ at offset 3
+
+/(?X)ab\gdef/X
+Failed: unrecognized character follows \ at offset 7
+
+/x{5,4}/
+Failed: numbers out of order in {} quantifier at offset 5
+
+/z{65536}/
+Failed: number too big in {} quantifier at offset 7
+
+/[abcd/
+Failed: missing terminating ] for character class at offset 5
+
+/(?X)[\B]/
+Failed: invalid escape sequence in character class at offset 6
+
+/[z-a]/
+Failed: range out of order in character class at offset 3
+
+/^*/
+Failed: nothing to repeat at offset 1
+
+/(abc/
+Failed: missing ) at offset 4
+
+/(?# abc/
+Failed: missing ) after comment at offset 7
+
+/(?z)abc/
+Failed: unrecognized character after (? at offset 2
+
+/.*b/
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+First char at start or follows \n
+Need char = 'b'
+
+/.*?b/
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+First char at start or follows \n
+Need char = 'b'
+
+/cat|dog|elephant/
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+    this sentence eventually mentions a cat
+ 0: cat
+    this sentences rambles on and on for a while and then reaches elephant
+ 0: elephant
+
+/cat|dog|elephant/S
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+Starting byte set: c d e 
+    this sentence eventually mentions a cat
+ 0: cat
+    this sentences rambles on and on for a while and then reaches elephant
+ 0: elephant
+
+/cat|dog|elephant/iS
+Capturing subpattern count = 0
+Options: caseless
+No first char
+No need char
+Starting byte set: C D E c d e 
+    this sentence eventually mentions a CAT cat
+ 0: CAT
+    this sentences rambles on and on for a while to elephant ElePhant
+ 0: elephant
+
+/a|[bcd]/S
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+Starting byte set: a b c d 
+
+/(a|[^\dZ])/S
+Capturing subpattern count = 1
+No options
+No first char
+No need char
+Starting byte set: \x00 \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \x09 \x0a 
+  \x0b \x0c \x0d \x0e \x0f \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 
+  \x1a \x1b \x1c \x1d \x1e \x1f \x20 ! " # $ % & ' ( ) * + , - . / : ; < = > 
+  ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y [ \ ] ^ _ ` a b c d 
+  e f g h i j k l m n o p q r s t u v w x y z { | } ~ \x7f \x80 \x81 \x82 \x83 
+  \x84 \x85 \x86 \x87 \x88 \x89 \x8a \x8b \x8c \x8d \x8e \x8f \x90 \x91 \x92 
+  \x93 \x94 \x95 \x96 \x97 \x98 \x99 \x9a \x9b \x9c \x9d \x9e \x9f \xa0 \xa1 
+  \xa2 \xa3 \xa4 \xa5 \xa6 \xa7 \xa8 \xa9 \xaa \xab \xac \xad \xae \xaf \xb0 
+  \xb1 \xb2 \xb3 \xb4 \xb5 \xb6 \xb7 \xb8 \xb9 \xba \xbb \xbc \xbd \xbe \xbf 
+  \xc0 \xc1 \xc2 \xc3 \xc4 \xc5 \xc6 \xc7 \xc8 \xc9 \xca \xcb \xcc \xcd \xce 
+  \xcf \xd0 \xd1 \xd2 \xd3 \xd4 \xd5 \xd6 \xd7 \xd8 \xd9 \xda \xdb \xdc \xdd 
+  \xde \xdf \xe0 \xe1 \xe2 \xe3 \xe4 \xe5 \xe6 \xe7 \xe8 \xe9 \xea \xeb \xec 
+  \xed \xee \xef \xf0 \xf1 \xf2 \xf3 \xf4 \xf5 \xf6 \xf7 \xf8 \xf9 \xfa \xfb 
+  \xfc \xfd \xfe \xff 
+
+/(a|b)*[\s]/S
+Capturing subpattern count = 1
+No options
+No first char
+No need char
+Starting byte set: \x09 \x0a \x0c \x0d \x20 a b 
+
+/(ab\2)/
+Failed: reference to non-existent subpattern at offset 6
+
+/{4,5}abc/
+Failed: nothing to repeat at offset 4
+
+/(a)(b)(c)\2/
+Capturing subpattern count = 3
+Max back reference = 2
+No options
+First char = 'a'
+Need char = 'c'
+    abcb
+ 0: abcb
+ 1: a
+ 2: b
+ 3: c
+    \O0abcb
+Matched, but too many substrings
+    \O3abcb
+Matched, but too many substrings
+ 0: abcb
+    \O6abcb
+Matched, but too many substrings
+ 0: abcb
+ 1: a
+    \O9abcb
+Matched, but too many substrings
+ 0: abcb
+ 1: a
+ 2: b
+    \O12abcb 
+ 0: abcb
+ 1: a
+ 2: b
+ 3: c
+
+/(a)bc|(a)(b)\2/
+Capturing subpattern count = 3
+Max back reference = 2
+No options
+First char = 'a'
+No need char
+    abc
+ 0: abc
+ 1: a
+    \O0abc
+Matched, but too many substrings
+    \O3abc
+Matched, but too many substrings
+ 0: abc
+    \O6abc
+ 0: abc
+ 1: a
+    aba
+ 0: aba
+ 1: <unset>
+ 2: a
+ 3: b
+    \O0aba
+Matched, but too many substrings
+    \O3aba
+Matched, but too many substrings
+ 0: aba
+    \O6aba
+Matched, but too many substrings
+ 0: aba
+ 1: <unset>
+    \O9aba
+Matched, but too many substrings
+ 0: aba
+ 1: <unset>
+ 2: a
+    \O12aba
+ 0: aba
+ 1: <unset>
+ 2: a
+ 3: b
+
+/abc$/E
+Capturing subpattern count = 0
+Options: dollar_endonly
+First char = 'a'
+Need char = 'c'
+    abc
+ 0: abc
+    *** Failers
+No match
+    abc\n
+No match
+    abc\ndef
+No match
+
+/(a)(b)(c)(d)(e)\6/
+Failed: reference to non-existent subpattern at offset 17
+
+/the quick brown fox/
+Capturing subpattern count = 0
+No options
+First char = 't'
+Need char = 'x'
+    the quick brown fox
+ 0: the quick brown fox
+    this is a line with the quick brown fox
+ 0: the quick brown fox
+
+/the quick brown fox/A
+Capturing subpattern count = 0
+Options: anchored
+No first char
+No need char
+    the quick brown fox
+ 0: the quick brown fox
+    *** Failers
+No match
+    this is a line with the quick brown fox
+No match
+
+/ab(?z)cd/
+Failed: unrecognized character after (? at offset 4
+
+/^abc|def/
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+    abcdef
+ 0: abc
+    abcdef\B
+ 0: def
+
+/.*((abc)$|(def))/
+Capturing subpattern count = 3
+Partial matching not supported
+No options
+First char at start or follows \n
+No need char
+    defabc
+ 0: defabc
+ 1: abc
+ 2: abc
+    \Zdefabc
+ 0: def
+ 1: def
+ 2: <unset>
+ 3: def
+
+/abc/P
+    abc
+ 0: abc
+    *** Failers
+No match: POSIX code 17: match failed
+    
+/^abc|def/P
+    abcdef
+ 0: abc
+    abcdef\B
+ 0: def
+
+/.*((abc)$|(def))/P
+    defabc
+ 0: defabc
+ 1: abc
+ 2: abc
+    \Zdefabc
+ 0: def
+ 1: def
+ 3: def
+  
+/the quick brown fox/P
+    the quick brown fox
+ 0: the quick brown fox
+    *** Failers 
+No match: POSIX code 17: match failed
+    The Quick Brown Fox 
+No match: POSIX code 17: match failed
+
+/the quick brown fox/Pi
+    the quick brown fox
+ 0: the quick brown fox
+    The Quick Brown Fox 
+ 0: The Quick Brown Fox
+
+/abc.def/P
+    *** Failers
+No match: POSIX code 17: match failed
+    abc\ndef
+No match: POSIX code 17: match failed
+    
+/abc$/P
+    abc
+ 0: abc
+    abc\n 
+ 0: abc
+
+/(abc)\2/P
+Failed: POSIX code 15: bad back reference at offset 7     
+
+/(abc\1)/P
+    abc
+No match: POSIX code 17: match failed
+
+/)/
+Failed: unmatched parentheses at offset 0
+
+/a[]b/
+Failed: missing terminating ] for character class at offset 4
+
+/[^aeiou ]{3,}/
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+No first char
+No need char
+    co-processors, and for 
+ 0: -pr
+    
+/<.*>/
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+First char = '<'
+Need char = '>'
+    abc<def>ghi<klm>nop
+ 0: <def>ghi<klm>
+
+/<.*?>/
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+First char = '<'
+Need char = '>'
+    abc<def>ghi<klm>nop
+ 0: <def>
+
+/<.*>/U
+Capturing subpattern count = 0
+Partial matching not supported
+Options: ungreedy
+First char = '<'
+Need char = '>'
+    abc<def>ghi<klm>nop
+ 0: <def>
+    
+/(?U)<.*>/
+Capturing subpattern count = 0
+Partial matching not supported
+Options: ungreedy
+First char = '<'
+Need char = '>'
+    abc<def>ghi<klm>nop
+ 0: <def>
+
+/<.*?>/U
+Capturing subpattern count = 0
+Partial matching not supported
+Options: ungreedy
+First char = '<'
+Need char = '>'
+    abc<def>ghi<klm>nop
+ 0: <def>ghi<klm>
+    
+/={3,}/U
+Capturing subpattern count = 0
+Partial matching not supported
+Options: ungreedy
+First char = '='
+Need char = '='
+    abc========def
+ 0: ===
+    
+/(?U)={3,}?/
+Capturing subpattern count = 0
+Partial matching not supported
+Options: ungreedy
+First char = '='
+Need char = '='
+    abc========def
+ 0: ========
+    
+/(?<!bar|cattle)foo/
+Capturing subpattern count = 0
+No options
+First char = 'f'
+Need char = 'o'
+    foo
+ 0: foo
+    catfoo 
+ 0: foo
+    *** Failers
+No match
+    the barfoo
+No match
+    and cattlefoo   
+No match
+
+/(?<=a+)b/
+Failed: lookbehind assertion is not fixed length at offset 6
+
+/(?<=aaa|b{0,3})b/
+Failed: lookbehind assertion is not fixed length at offset 14
+
+/(?<!(foo)a\1)bar/
+Failed: lookbehind assertion is not fixed length at offset 12
+
+/(?i)abc/
+Capturing subpattern count = 0
+Options: caseless
+First char = 'a' (caseless)
+Need char = 'c' (caseless)
+
+/(a|(?m)a)/
+Capturing subpattern count = 1
+No options
+First char = 'a'
+No need char
+
+/(?i)^1234/
+Capturing subpattern count = 0
+Options: anchored caseless
+No first char
+No need char
+
+/(^b|(?i)^d)/
+Capturing subpattern count = 1
+Options: anchored
+Case state changes
+No first char
+No need char
+
+/(?s).*/
+Capturing subpattern count = 0
+Partial matching not supported
+Options: anchored dotall
+No first char
+No need char
+
+/[abcd]/S
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+Starting byte set: a b c d 
+
+/(?i)[abcd]/S
+Capturing subpattern count = 0
+Options: caseless
+No first char
+No need char
+Starting byte set: A B C D a b c d 
+
+/(?m)[xy]|(b|c)/S
+Capturing subpattern count = 1
+Options: multiline
+No first char
+No need char
+Starting byte set: b c x y 
+
+/(^a|^b)/m
+Capturing subpattern count = 1
+Options: multiline
+First char at start or follows \n
+No need char
+
+/(?i)(^a|^b)/m
+Capturing subpattern count = 1
+Options: caseless multiline
+First char at start or follows \n
+No need char
+
+/(a)(?(1)a|b|c)/
+Failed: conditional group contains more than two branches at offset 13
+
+/(?(?=a)a|b|c)/
+Failed: conditional group contains more than two branches at offset 12
+
+/(?(1a)/
+Failed: malformed number after (?( at offset 4
+
+/(?(?i))/
+Failed: assertion expected after (?( at offset 3
+
+/(?(abc))/
+Failed: assertion expected after (?( at offset 3
+
+/(?(?<ab))/
+Failed: unrecognized character after (?< at offset 5
+
+/((?s)blah)\s+\1/
+Capturing subpattern count = 1
+Max back reference = 1
+Partial matching not supported
+No options
+First char = 'b'
+Need char = 'h'
+
+/((?i)blah)\s+\1/
+Capturing subpattern count = 1
+Max back reference = 1
+Partial matching not supported
+No options
+Case state changes
+First char = 'b' (caseless)
+Need char = 'h' (caseless)
+
+/((?i)b)/DS
+------------------------------------------------------------------
+  0  15 Bra 0
+  3   7 Bra 1
+  6  01 Opt
+  8  NC b
+ 10   7 Ket
+ 13  00 Opt
+ 15  15 Ket
+ 18     End
+------------------------------------------------------------------
+Capturing subpattern count = 1
+No options
+Case state changes
+First char = 'b' (caseless)
+No need char
+Study returned NULL
+
+/(a*b|(?i:c*(?-i)d))/S
+Capturing subpattern count = 1
+Partial matching not supported
+No options
+Case state changes
+No first char
+No need char
+Starting byte set: C a b c d 
+
+/a$/
+Capturing subpattern count = 0
+No options
+First char = 'a'
+No need char
+    a
+ 0: a
+    a\n
+ 0: a
+    *** Failers 
+No match
+    \Za
+No match
+    \Za\n   
+No match
+
+/a$/m
+Capturing subpattern count = 0
+Options: multiline
+First char = 'a'
+No need char
+    a
+ 0: a
+    a\n
+ 0: a
+    \Za\n   
+ 0: a
+    *** Failers 
+No match
+    \Za
+No match
+    
+/\Aabc/m
+Capturing subpattern count = 0
+Options: anchored multiline
+No first char
+No need char
+
+/^abc/m 
+Capturing subpattern count = 0
+Options: multiline
+First char at start or follows \n
+Need char = 'c'
+
+/^((a+)(?U)([ab]+)(?-U)([bc]+)(\w*))/
+Capturing subpattern count = 5
+Partial matching not supported
+Options: anchored
+No first char
+No need char
+  aaaaabbbbbcccccdef
+ 0: aaaaabbbbbcccccdef
+ 1: aaaaabbbbbcccccdef
+ 2: aaaaa
+ 3: b
+ 4: bbbbccccc
+ 5: def
+
+/(?<=foo)[ab]/S
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+Starting byte set: a b 
+
+/(?<!foo)(alpha|omega)/S
+Capturing subpattern count = 1
+No options
+No first char
+Need char = 'a'
+Starting byte set: a o 
+
+/(?!alphabet)[ab]/S
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+Starting byte set: a b 
+
+/(?<=foo\n)^bar/m
+Capturing subpattern count = 0
+Options: multiline
+No first char
+Need char = 'r'
+    foo\nbarbar 
+ 0: bar
+    ***Failers
+No match
+    rhubarb 
+No match
+    barbell
+No match
+    abc\nbarton 
+No match
+
+/^(?<=foo\n)bar/m
+Capturing subpattern count = 0
+Options: multiline
+First char at start or follows \n
+Need char = 'r'
+    foo\nbarbar 
+ 0: bar
+    ***Failers
+No match
+    rhubarb 
+No match
+    barbell
+No match
+    abc\nbarton 
+No match
+
+/(?>^abc)/m
+Capturing subpattern count = 0
+Options: multiline
+First char at start or follows \n
+Need char = 'c'
+    abc
+ 0: abc
+    def\nabc
+ 0: abc
+    *** Failers
+No match
+    defabc   
+No match
+
+/(?<=ab(c+)d)ef/
+Failed: lookbehind assertion is not fixed length at offset 11
+
+/(?<=ab(?<=c+)d)ef/
+Failed: lookbehind assertion is not fixed length at offset 12
+
+/(?<=ab(c|de)f)g/
+Failed: lookbehind assertion is not fixed length at offset 13
+
+/The next three are in testinput2 because they have variable length branches/
+Capturing subpattern count = 0
+No options
+First char = 'T'
+Need char = 's'
+
+/(?<=bullock|donkey)-cart/
+Capturing subpattern count = 0
+No options
+First char = '-'
+Need char = 't'
+    the bullock-cart
+ 0: -cart
+    a donkey-cart race
+ 0: -cart
+    *** Failers
+No match
+    cart
+No match
+    horse-and-cart    
+No match
+      
+/(?<=ab(?i)x|y|z)/
+Capturing subpattern count = 0
+No options
+Case state changes
+No first char
+No need char
+
+/(?>.*)(?<=(abcd)|(xyz))/
+Capturing subpattern count = 2
+Partial matching not supported
+No options
+First char at start or follows \n
+No need char
+    alphabetabcd
+ 0: alphabetabcd
+ 1: abcd
+    endingxyz
+ 0: endingxyz
+ 1: <unset>
+ 2: xyz
+
+/(?<=ab(?i)x(?-i)y|(?i)z|b)ZZ/
+Capturing subpattern count = 0
+No options
+Case state changes
+First char = 'Z'
+Need char = 'Z'
+    abxyZZ
+ 0: ZZ
+    abXyZZ
+ 0: ZZ
+    ZZZ
+ 0: ZZ
+    zZZ
+ 0: ZZ
+    bZZ
+ 0: ZZ
+    BZZ     
+ 0: ZZ
+    *** Failers
+No match
+    ZZ 
+No match
+    abXYZZ 
+No match
+    zzz
+No match
+    bzz  
+No match
+
+/(?<!(foo)a)bar/
+Capturing subpattern count = 1
+No options
+First char = 'b'
+Need char = 'r'
+    bar
+ 0: bar
+    foobbar 
+ 0: bar
+    *** Failers
+No match
+    fooabar  
+No match
+
+/This one is here because Perl 5.005_02 doesn't fail it/
+Capturing subpattern count = 0
+No options
+First char = 'T'
+Need char = 't'
+
+/^(a)?(?(1)a|b)+$/
+Capturing subpattern count = 1
+Options: anchored
+No first char
+No need char
+    *** Failers
+No match
+    a 
+No match
+
+/This one is here because I think Perl 5.005_02 gets the setting of $1 wrong/
+Capturing subpattern count = 0
+No options
+First char = 'T'
+Need char = 'g'
+
+/^(a\1?){4}$/
+Capturing subpattern count = 1
+Max back reference = 1
+Options: anchored
+No first char
+No need char
+    aaaaaa
+ 0: aaaaaa
+ 1: aa
+    
+/These are syntax tests from Perl 5.005/
+Capturing subpattern count = 0
+No options
+First char = 'T'
+Need char = '5'
+
+/a[b-a]/
+Failed: range out of order in character class at offset 4
+
+/a[]b/
+Failed: missing terminating ] for character class at offset 4
+
+/a[/
+Failed: missing terminating ] for character class at offset 2
+
+/*a/
+Failed: nothing to repeat at offset 0
+
+/(*)b/
+Failed: nothing to repeat at offset 1
+
+/abc)/
+Failed: unmatched parentheses at offset 3
+
+/(abc/
+Failed: missing ) at offset 4
+
+/a**/
+Failed: nothing to repeat at offset 2
+
+/)(/
+Failed: unmatched parentheses at offset 0
+
+/\1/
+Failed: reference to non-existent subpattern at offset 2
+
+/\2/
+Failed: reference to non-existent subpattern at offset 2
+
+/(a)|\2/
+Failed: reference to non-existent subpattern at offset 6
+
+/a[b-a]/i
+Failed: range out of order in character class at offset 4
+
+/a[]b/i
+Failed: missing terminating ] for character class at offset 4
+
+/a[/i
+Failed: missing terminating ] for character class at offset 2
+
+/*a/i
+Failed: nothing to repeat at offset 0
+
+/(*)b/i
+Failed: nothing to repeat at offset 1
+
+/abc)/i
+Failed: unmatched parentheses at offset 3
+
+/(abc/i
+Failed: missing ) at offset 4
+
+/a**/i
+Failed: nothing to repeat at offset 2
+
+/)(/i
+Failed: unmatched parentheses at offset 0
+
+/:(?:/
+Failed: missing ) at offset 4
+
+/(?<%)b/
+Failed: unrecognized character after (?< at offset 3
+
+/a(?{)b/
+Failed: unrecognized character after (? at offset 3
+
+/a(?{{})b/
+Failed: unrecognized character after (? at offset 3
+
+/a(?{}})b/
+Failed: unrecognized character after (? at offset 3
+
+/a(?{"{"})b/
+Failed: unrecognized character after (? at offset 3
+
+/a(?{"{"}})b/
+Failed: unrecognized character after (? at offset 3
+
+/(?(1?)a|b)/
+Failed: malformed number after (?( at offset 4
+
+/(?(1)a|b|c)/
+Failed: conditional group contains more than two branches at offset 10
+
+/[a[:xyz:/
+Failed: missing terminating ] for character class at offset 8
+
+/(?<=x+)y/
+Failed: lookbehind assertion is not fixed length at offset 6
+
+/a{37,17}/
+Failed: numbers out of order in {} quantifier at offset 7
+
+/abc/\
+Failed: \ at end of pattern at offset 4
+
+/abc/\P
+Failed: POSIX code 9: bad escape sequence at offset 4     
+
+/abc/\i
+Failed: \ at end of pattern at offset 4
+
+/(a)bc(d)/
+Capturing subpattern count = 2
+No options
+First char = 'a'
+Need char = 'd'
+    abcd
+ 0: abcd
+ 1: a
+ 2: d
+    abcd\C2
+ 0: abcd
+ 1: a
+ 2: d
+ 2C d (1)
+    abcd\C5
+ 0: abcd
+ 1: a
+ 2: d
+copy substring 5 failed -7
+     
+/(.{20})/
+Capturing subpattern count = 1
+Partial matching not supported
+No options
+No first char
+No need char
+    abcdefghijklmnopqrstuvwxyz
+ 0: abcdefghijklmnopqrst
+ 1: abcdefghijklmnopqrst
+    abcdefghijklmnopqrstuvwxyz\C1
+ 0: abcdefghijklmnopqrst
+ 1: abcdefghijklmnopqrst
+copy substring 1 failed -6
+    abcdefghijklmnopqrstuvwxyz\G1
+ 0: abcdefghijklmnopqrst
+ 1: abcdefghijklmnopqrst
+ 1G abcdefghijklmnopqrst (20)
+     
+/(.{15})/
+Capturing subpattern count = 1
+Partial matching not supported
+No options
+No first char
+No need char
+    abcdefghijklmnopqrstuvwxyz
+ 0: abcdefghijklmno
+ 1: abcdefghijklmno
+    abcdefghijklmnopqrstuvwxyz\C1\G1
+ 0: abcdefghijklmno
+ 1: abcdefghijklmno
+ 1C abcdefghijklmno (15)
+ 1G abcdefghijklmno (15)
+
+/(.{16})/
+Capturing subpattern count = 1
+Partial matching not supported
+No options
+No first char
+No need char
+    abcdefghijklmnopqrstuvwxyz
+ 0: abcdefghijklmnop
+ 1: abcdefghijklmnop
+    abcdefghijklmnopqrstuvwxyz\C1\G1\L
+ 0: abcdefghijklmnop
+ 1: abcdefghijklmnop
+copy substring 1 failed -6
+ 1G abcdefghijklmnop (16)
+ 0L abcdefghijklmnop
+ 1L abcdefghijklmnop
+    
+/^(a|(bc))de(f)/
+Capturing subpattern count = 3
+Options: anchored
+No first char
+No need char
+    adef\G1\G2\G3\G4\L 
+ 0: adef
+ 1: a
+ 2: <unset>
+ 3: f
+ 1G a (1)
+ 2G  (0)
+ 3G f (1)
+get substring 4 failed -7
+ 0L adef
+ 1L a
+ 2L 
+ 3L f
+    bcdef\G1\G2\G3\G4\L 
+ 0: bcdef
+ 1: bc
+ 2: bc
+ 3: f
+ 1G bc (2)
+ 2G bc (2)
+ 3G f (1)
+get substring 4 failed -7
+ 0L bcdef
+ 1L bc
+ 2L bc
+ 3L f
+    adefghijk\C0 
+ 0: adef
+ 1: a
+ 2: <unset>
+ 3: f
+ 0C adef (4)
+    
+/^abc\00def/
+Capturing subpattern count = 0
+Options: anchored
+No first char
+No need char
+    abc\00def\L\C0 
+ 0: abc\x00def
+ 0C abc (7)
+ 0L abc
+    
+/word ((?:[a-zA-Z0-9]+ )((?:[a-zA-Z0-9]+ )((?:[a-zA-Z0-9]+ )((?:[a-zA-Z0-9]+ 
+)((?:[a-zA-Z0-9]+ )((?:[a-zA-Z0-9]+ )((?:[a-zA-Z0-9]+ )((?:[a-zA-Z0-9]+ 
+)?)?)?)?)?)?)?)?)?otherword/M
+Memory allocation (code space): 432
+Capturing subpattern count = 8
+Partial matching not supported
+No options
+First char = 'w'
+Need char = 'd'
+
+/.*X/D
+------------------------------------------------------------------
+  0   7 Bra 0
+  3     Any*
+  5     X
+  7   7 Ket
+ 10     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+First char at start or follows \n
+Need char = 'X'
+
+/.*X/Ds
+------------------------------------------------------------------
+  0   7 Bra 0
+  3     Any*
+  5     X
+  7   7 Ket
+ 10     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Partial matching not supported
+Options: anchored dotall
+No first char
+Need char = 'X'
+
+/(.*X|^B)/D
+------------------------------------------------------------------
+  0  19 Bra 0
+  3   7 Bra 1
+  6     Any*
+  8     X
+ 10   6 Alt
+ 13     ^
+ 14     B
+ 16  13 Ket
+ 19  19 Ket
+ 22     End
+------------------------------------------------------------------
+Capturing subpattern count = 1
+Partial matching not supported
+No options
+First char at start or follows \n
+No need char
+
+/(.*X|^B)/Ds
+------------------------------------------------------------------
+  0  19 Bra 0
+  3   7 Bra 1
+  6     Any*
+  8     X
+ 10   6 Alt
+ 13     ^
+ 14     B
+ 16  13 Ket
+ 19  19 Ket
+ 22     End
+------------------------------------------------------------------
+Capturing subpattern count = 1
+Partial matching not supported
+Options: anchored dotall
+No first char
+No need char
+    
+/(?s)(.*X|^B)/D
+------------------------------------------------------------------
+  0  19 Bra 0
+  3   7 Bra 1
+  6     Any*
+  8     X
+ 10   6 Alt
+ 13     ^
+ 14     B
+ 16  13 Ket
+ 19  19 Ket
+ 22     End
+------------------------------------------------------------------
+Capturing subpattern count = 1
+Partial matching not supported
+Options: anchored dotall
+No first char
+No need char
+
+/(?s:.*X|^B)/D
+------------------------------------------------------------------
+  0  25 Bra 0
+  3   9 Bra 0
+  6  04 Opt
+  8     Any*
+ 10     X
+ 12   8 Alt
+ 15  04 Opt
+ 17     ^
+ 18     B
+ 20  17 Ket
+ 23  00 Opt
+ 25  25 Ket
+ 28     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+First char at start or follows \n
+No need char
+
+/\Biss\B/+
+Capturing subpattern count = 0
+No options
+First char = 'i'
+Need char = 's'
+    Mississippi
+ 0: iss
+ 0+ issippi
+
+/\Biss\B/+P
+    Mississippi
+ 0: iss
+ 0+ issippi
+
+/iss/G+
+Capturing subpattern count = 0
+No options
+First char = 'i'
+Need char = 's'
+    Mississippi
+ 0: iss
+ 0+ issippi
+ 0: iss
+ 0+ ippi
+
+/\Biss\B/G+
+Capturing subpattern count = 0
+No options
+First char = 'i'
+Need char = 's'
+    Mississippi
+ 0: iss
+ 0+ issippi
+
+/\Biss\B/g+
+Capturing subpattern count = 0
+No options
+First char = 'i'
+Need char = 's'
+    Mississippi
+ 0: iss
+ 0+ issippi
+ 0: iss
+ 0+ ippi
+    *** Failers
+No match
+    Mississippi\A
+No match
+
+/(?<=[Ms])iss/g+
+Capturing subpattern count = 0
+No options
+First char = 'i'
+Need char = 's'
+    Mississippi
+ 0: iss
+ 0+ issippi
+ 0: iss
+ 0+ ippi
+
+/(?<=[Ms])iss/G+
+Capturing subpattern count = 0
+No options
+First char = 'i'
+Need char = 's'
+    Mississippi
+ 0: iss
+ 0+ issippi
+
+/^iss/g+
+Capturing subpattern count = 0
+Options: anchored
+No first char
+No need char
+    ississippi
+ 0: iss
+ 0+ issippi
+    
+/.*iss/g+
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+First char at start or follows \n
+Need char = 's'
+    abciss\nxyzisspqr 
+ 0: abciss
+ 0+ \x0axyzisspqr
+ 0: xyziss
+ 0+ pqr
+
+/.i./+g
+Capturing subpattern count = 0
+No options
+No first char
+Need char = 'i'
+    Mississippi
+ 0: Mis
+ 0+ sissippi
+ 0: sis
+ 0+ sippi
+ 0: sip
+ 0+ pi
+    Mississippi\A
+ 0: Mis
+ 0+ sissippi
+ 0: sis
+ 0+ sippi
+ 0: sip
+ 0+ pi
+    Missouri river
+ 0: Mis
+ 0+ souri river
+ 0: ri 
+ 0+ river
+ 0: riv
+ 0+ er
+    Missouri river\A  
+ 0: Mis
+ 0+ souri river
+
+/^.is/+g
+Capturing subpattern count = 0
+Options: anchored
+No first char
+No need char
+    Mississippi
+ 0: Mis
+ 0+ sissippi
+
+/^ab\n/g+
+Capturing subpattern count = 0
+Options: anchored
+No first char
+No need char
+    ab\nab\ncd
+ 0: ab\x0a
+ 0+ ab\x0acd
+
+/^ab\n/mg+
+Capturing subpattern count = 0
+Options: multiline
+First char at start or follows \n
+Need char = 10
+    ab\nab\ncd
+ 0: ab\x0a
+ 0+ ab\x0acd
+ 0: ab\x0a
+ 0+ cd
+
+/abc/
+Capturing subpattern count = 0
+No options
+First char = 'a'
+Need char = 'c'
+
+/abc|bac/
+Capturing subpattern count = 0
+No options
+No first char
+Need char = 'c'
+
+/(abc|bac)/
+Capturing subpattern count = 1
+No options
+No first char
+Need char = 'c'
+
+/(abc|(c|dc))/
+Capturing subpattern count = 2
+No options
+No first char
+Need char = 'c'
+
+/(abc|(d|de)c)/
+Capturing subpattern count = 2
+No options
+No first char
+Need char = 'c'
+
+/a*/
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+No first char
+No need char
+
+/a+/
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+First char = 'a'
+No need char
+
+/(baa|a+)/
+Capturing subpattern count = 1
+Partial matching not supported
+No options
+No first char
+Need char = 'a'
+
+/a{0,3}/
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+No first char
+No need char
+
+/baa{3,}/
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+First char = 'b'
+Need char = 'a'
+
+/"([^\\"]+|\\.)*"/
+Capturing subpattern count = 1
+Partial matching not supported
+No options
+First char = '"'
+Need char = '"'
+
+/(abc|ab[cd])/
+Capturing subpattern count = 1
+No options
+First char = 'a'
+No need char
+
+/(a|.)/
+Capturing subpattern count = 1
+No options
+No first char
+No need char
+
+/a|ba|\w/
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+
+/abc(?=pqr)/
+Capturing subpattern count = 0
+No options
+First char = 'a'
+Need char = 'r'
+
+/...(?<=abc)/
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+
+/abc(?!pqr)/
+Capturing subpattern count = 0
+No options
+First char = 'a'
+Need char = 'c'
+
+/ab./
+Capturing subpattern count = 0
+No options
+First char = 'a'
+Need char = 'b'
+
+/ab[xyz]/
+Capturing subpattern count = 0
+No options
+First char = 'a'
+Need char = 'b'
+
+/abc*/
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+First char = 'a'
+Need char = 'b'
+
+/ab.c*/
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+First char = 'a'
+Need char = 'b'
+
+/a.c*/
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+First char = 'a'
+No need char
+
+/.c*/
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+No first char
+No need char
+
+/ac*/
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+First char = 'a'
+No need char
+
+/(a.c*|b.c*)/
+Capturing subpattern count = 1
+Partial matching not supported
+No options
+No first char
+No need char
+
+/a.c*|aba/
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+First char = 'a'
+No need char
+
+/.+a/
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+No first char
+Need char = 'a'
+
+/(?=abcda)a.*/
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+First char = 'a'
+Need char = 'a'
+
+/(?=a)a.*/
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+First char = 'a'
+No need char
+
+/a(b)*/
+Capturing subpattern count = 1
+No options
+First char = 'a'
+No need char
+
+/a\d*/
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+First char = 'a'
+No need char
+
+/ab\d*/
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+First char = 'a'
+Need char = 'b'
+
+/a(\d)*/
+Capturing subpattern count = 1
+No options
+First char = 'a'
+No need char
+
+/abcde{0,0}/
+Capturing subpattern count = 0
+No options
+First char = 'a'
+Need char = 'd'
+
+/ab\d+/
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+First char = 'a'
+Need char = 'b'
+
+/a(?(1)b)/
+Capturing subpattern count = 0
+No options
+First char = 'a'
+No need char
+
+/a(?(1)bag|big)/
+Capturing subpattern count = 0
+No options
+First char = 'a'
+Need char = 'g'
+
+/a(?(1)bag|big)*/
+Capturing subpattern count = 0
+No options
+First char = 'a'
+No need char
+
+/a(?(1)bag|big)+/
+Capturing subpattern count = 0
+No options
+First char = 'a'
+Need char = 'g'
+
+/a(?(1)b..|b..)/
+Capturing subpattern count = 0
+No options
+First char = 'a'
+Need char = 'b'
+
+/ab\d{0}e/
+Capturing subpattern count = 0
+No options
+First char = 'a'
+Need char = 'e'
+
+/a?b?/
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+    a
+ 0: a
+    b
+ 0: b
+    ab
+ 0: ab
+    \
+ 0: 
+    *** Failers
+ 0: 
+    \N     
+No match
+    
+/|-/
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+    abcd
+ 0: 
+    -abc
+ 0: 
+    \Nab-c
+ 0: -
+    *** Failers
+ 0: 
+    \Nabc     
+No match
+
+/a*(b+)(z)(z)/P
+    aaaabbbbzzzz
+ 0: aaaabbbbzz
+ 1: bbbb
+ 2: z
+ 3: z
+    aaaabbbbzzzz\O0
+    aaaabbbbzzzz\O1
+ 0: aaaabbbbzz
+    aaaabbbbzzzz\O2
+ 0: aaaabbbbzz
+ 1: bbbb
+    aaaabbbbzzzz\O3
+ 0: aaaabbbbzz
+ 1: bbbb
+ 2: z
+    aaaabbbbzzzz\O4
+ 0: aaaabbbbzz
+ 1: bbbb
+ 2: z
+ 3: z
+    aaaabbbbzzzz\O5
+ 0: aaaabbbbzz
+ 1: bbbb
+ 2: z
+ 3: z
+    
+/^.?abcd/S 
+Capturing subpattern count = 0
+Options: anchored
+No first char
+Need char = 'd'
+Study returned NULL
+
+/\(             # ( at start
+  (?:           # Non-capturing bracket
+  (?>[^()]+)    # Either a sequence of non-brackets (no backtracking)
+  |             # Or
+  (?R)          # Recurse - i.e. nested bracketed string
+  )*            # Zero or more contents
+  \)            # Closing )
+  /x
+Capturing subpattern count = 0
+Partial matching not supported
+Options: extended
+First char = '('
+Need char = ')'
+    (abcd)
+ 0: (abcd)
+    (abcd)xyz
+ 0: (abcd)
+    xyz(abcd)
+ 0: (abcd)
+    (ab(xy)cd)pqr 
+ 0: (ab(xy)cd)
+    (ab(xycd)pqr 
+ 0: (xycd)
+    () abc () 
+ 0: ()
+    12(abcde(fsh)xyz(foo(bar))lmno)89
+ 0: (abcde(fsh)xyz(foo(bar))lmno)
+    *** Failers
+No match
+    abcd 
+No match
+    abcd)
+No match
+    (abcd  
+No match
+
+/\(  ( (?>[^()]+) | (?R) )* \) /xg
+Capturing subpattern count = 1
+Partial matching not supported
+Options: extended
+First char = '('
+Need char = ')'
+    (ab(xy)cd)pqr 
+ 0: (ab(xy)cd)
+ 1: cd
+    1(abcd)(x(y)z)pqr
+ 0: (abcd)
+ 1: abcd
+ 0: (x(y)z)
+ 1: z
+
+/\(  (?: (?>[^()]+) | (?R) ) \) /x
+Capturing subpattern count = 0
+Partial matching not supported
+Options: extended
+First char = '('
+Need char = ')'
+    (abcd)
+ 0: (abcd)
+    (ab(xy)cd)
+ 0: (xy)
+    (a(b(c)d)e) 
+ 0: (c)
+    ((ab)) 
+ 0: ((ab))
+    *** Failers
+No match
+    ()   
+No match
+
+/\(  (?: (?>[^()]+) | (?R) )? \) /x
+Capturing subpattern count = 0
+Partial matching not supported
+Options: extended
+First char = '('
+Need char = ')'
+    ()
+ 0: ()
+    12(abcde(fsh)xyz(foo(bar))lmno)89
+ 0: (fsh)
+
+/\(  ( (?>[^()]+) | (?R) )* \) /x
+Capturing subpattern count = 1
+Partial matching not supported
+Options: extended
+First char = '('
+Need char = ')'
+    (ab(xy)cd)
+ 0: (ab(xy)cd)
+ 1: cd
+
+/\( ( ( (?>[^()]+) | (?R) )* ) \) /x
+Capturing subpattern count = 2
+Partial matching not supported
+Options: extended
+First char = '('
+Need char = ')'
+    (ab(xy)cd)
+ 0: (ab(xy)cd)
+ 1: ab(xy)cd
+ 2: cd
+
+/\( (123)? ( ( (?>[^()]+) | (?R) )* ) \) /x
+Capturing subpattern count = 3
+Partial matching not supported
+Options: extended
+First char = '('
+Need char = ')'
+    (ab(xy)cd)
+ 0: (ab(xy)cd)
+ 1: <unset>
+ 2: ab(xy)cd
+ 3: cd
+    (123ab(xy)cd)
+ 0: (123ab(xy)cd)
+ 1: 123
+ 2: ab(xy)cd
+ 3: cd
+
+/\( ( (123)? ( (?>[^()]+) | (?R) )* ) \) /x
+Capturing subpattern count = 3
+Partial matching not supported
+Options: extended
+First char = '('
+Need char = ')'
+    (ab(xy)cd)
+ 0: (ab(xy)cd)
+ 1: ab(xy)cd
+ 2: <unset>
+ 3: cd
+    (123ab(xy)cd)
+ 0: (123ab(xy)cd)
+ 1: 123ab(xy)cd
+ 2: 123
+ 3: cd
+
+/\( (((((((((( ( (?>[^()]+) | (?R) )* )))))))))) \) /x
+Capturing subpattern count = 11
+Partial matching not supported
+Options: extended
+First char = '('
+Need char = ')'
+    (ab(xy)cd)
+ 0: (ab(xy)cd)
+ 1: ab(xy)cd
+ 2: ab(xy)cd
+ 3: ab(xy)cd
+ 4: ab(xy)cd
+ 5: ab(xy)cd
+ 6: ab(xy)cd
+ 7: ab(xy)cd
+ 8: ab(xy)cd
+ 9: ab(xy)cd
+10: ab(xy)cd
+11: cd
+
+/\( ( ( (?>[^()<>]+) | ((?>[^()]+)) | (?R) )* ) \) /x
+Capturing subpattern count = 3
+Partial matching not supported
+Options: extended
+First char = '('
+Need char = ')'
+    (abcd(xyz<p>qrs)123)
+ 0: (abcd(xyz<p>qrs)123)
+ 1: abcd(xyz<p>qrs)123
+ 2: 123
+ 3: <unset>
+
+/\( ( ( (?>[^()]+) | ((?R)) )* ) \) /x
+Capturing subpattern count = 3
+Partial matching not supported
+Options: extended
+First char = '('
+Need char = ')'
+    (ab(cd)ef)
+ 0: (ab(cd)ef)
+ 1: ab(cd)ef
+ 2: ef
+ 3: (cd)
+    (ab(cd(ef)gh)ij)
+ 0: (ab(cd(ef)gh)ij)
+ 1: ab(cd(ef)gh)ij
+ 2: ij
+ 3: (cd(ef)gh)
+
+/^[[:alnum:]]/D
+------------------------------------------------------------------
+  0  37 Bra 0
+  3     ^
+  4     [0-9A-Za-z]
+ 37  37 Ket
+ 40     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: anchored
+No first char
+No need char
+
+/^[[:^alnum:]]/D
+------------------------------------------------------------------
+  0  37 Bra 0
+  3     ^
+  4     [\x00-/:-@[-`{-\xff]
+ 37  37 Ket
+ 40     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: anchored
+No first char
+No need char
+
+/^[[:alpha:]]/D
+------------------------------------------------------------------
+  0  37 Bra 0
+  3     ^
+  4     [A-Za-z]
+ 37  37 Ket
+ 40     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: anchored
+No first char
+No need char
+
+/^[[:^alpha:]]/D
+------------------------------------------------------------------
+  0  37 Bra 0
+  3     ^
+  4     [\x00-@[-`{-\xff]
+ 37  37 Ket
+ 40     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: anchored
+No first char
+No need char
+             
+/^[[:ascii:]]/D
+------------------------------------------------------------------
+  0  37 Bra 0
+  3     ^
+  4     [\x00-\x7f]
+ 37  37 Ket
+ 40     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: anchored
+No first char
+No need char
+
+/^[[:^ascii:]]/D
+------------------------------------------------------------------
+  0  37 Bra 0
+  3     ^
+  4     [\x80-\xff]
+ 37  37 Ket
+ 40     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: anchored
+No first char
+No need char
+
+/^[[:blank:]]/D
+------------------------------------------------------------------
+  0  37 Bra 0
+  3     ^
+  4     [\x09 ]
+ 37  37 Ket
+ 40     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: anchored
+No first char
+No need char
+
+/^[[:cntrl:]]/D
+------------------------------------------------------------------
+  0  37 Bra 0
+  3     ^
+  4     [\x00-\x1f\x7f]
+ 37  37 Ket
+ 40     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: anchored
+No first char
+No need char
+
+/^[[:digit:]]/D
+------------------------------------------------------------------
+  0  37 Bra 0
+  3     ^
+  4     [0-9]
+ 37  37 Ket
+ 40     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: anchored
+No first char
+No need char
+
+/^[[:graph:]]/D
+------------------------------------------------------------------
+  0  37 Bra 0
+  3     ^
+  4     [!-~]
+ 37  37 Ket
+ 40     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: anchored
+No first char
+No need char
+
+/^[[:lower:]]/D
+------------------------------------------------------------------
+  0  37 Bra 0
+  3     ^
+  4     [a-z]
+ 37  37 Ket
+ 40     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: anchored
+No first char
+No need char
+
+/^[[:print:]]/D
+------------------------------------------------------------------
+  0  37 Bra 0
+  3     ^
+  4     [ -~]
+ 37  37 Ket
+ 40     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: anchored
+No first char
+No need char
+
+/^[[:punct:]]/D
+------------------------------------------------------------------
+  0  37 Bra 0
+  3     ^
+  4     [!-/:-@[-`{-~]
+ 37  37 Ket
+ 40     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: anchored
+No first char
+No need char
+
+/^[[:space:]]/D
+------------------------------------------------------------------
+  0  37 Bra 0
+  3     ^
+  4     [\x09-\x0d ]
+ 37  37 Ket
+ 40     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: anchored
+No first char
+No need char
+
+/^[[:upper:]]/D
+------------------------------------------------------------------
+  0  37 Bra 0
+  3     ^
+  4     [A-Z]
+ 37  37 Ket
+ 40     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: anchored
+No first char
+No need char
+
+/^[[:xdigit:]]/D
+------------------------------------------------------------------
+  0  37 Bra 0
+  3     ^
+  4     [0-9A-Fa-f]
+ 37  37 Ket
+ 40     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: anchored
+No first char
+No need char
+
+/^[[:word:]]/D
+------------------------------------------------------------------
+  0  37 Bra 0
+  3     ^
+  4     [0-9A-Z_a-z]
+ 37  37 Ket
+ 40     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: anchored
+No first char
+No need char
+
+/^[[:^cntrl:]]/D
+------------------------------------------------------------------
+  0  37 Bra 0
+  3     ^
+  4     [ -~\x80-\xff]
+ 37  37 Ket
+ 40     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: anchored
+No first char
+No need char
+
+/^[12[:^digit:]]/D
+------------------------------------------------------------------
+  0  37 Bra 0
+  3     ^
+  4     [\x00-/12:-\xff]
+ 37  37 Ket
+ 40     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: anchored
+No first char
+No need char
+
+/^[[:^blank:]]/D
+------------------------------------------------------------------
+  0  37 Bra 0
+  3     ^
+  4     [\x00-\x08\x0a-\x1f!-\xff]
+ 37  37 Ket
+ 40     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: anchored
+No first char
+No need char
+
+/[01[:alpha:]%]/D
+------------------------------------------------------------------
+  0  36 Bra 0
+  3     [%01A-Za-z]
+ 36  36 Ket
+ 39     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+
+/[[.ch.]]/
+Failed: POSIX collating elements are not supported at offset 1
+
+/[[=ch=]]/
+Failed: POSIX collating elements are not supported at offset 1
+
+/[[:rhubarb:]]/
+Failed: unknown POSIX class name at offset 3
+
+/[[:upper:]]/i
+Capturing subpattern count = 0
+Options: caseless
+No first char
+No need char
+    A
+ 0: A
+    a 
+ 0: a
+    
+/[[:lower:]]/i
+Capturing subpattern count = 0
+Options: caseless
+No first char
+No need char
+    A
+ 0: A
+    a 
+ 0: a
+
+/((?-i)[[:lower:]])[[:lower:]]/i
+Capturing subpattern count = 1
+Options: caseless
+Case state changes
+No first char
+No need char
+    ab
+ 0: ab
+ 1: a
+    aB
+ 0: aB
+ 1: a
+    *** Failers
+ 0: ai
+ 1: a
+    Ab
+No match
+    AB        
+No match
+
+/[\200-\410]/
+Failed: range out of order in character class at offset 9
+
+/^(?(0)f|b)oo/
+Failed: invalid condition (?(0) at offset 5
+
+/This one's here because of the large output vector needed/
+Capturing subpattern count = 0
+No options
+First char = 'T'
+Need char = 'd'
+
+/(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\d+(?:\s|$))(\w+)\s+(\270)/
+Capturing subpattern count = 271
+Max back reference = 270
+Partial matching not supported
+No options
+No first char
+No need char
+    \O900 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 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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 ABC ABC
+ 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 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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 ABC ABC
+ 1: 1 
+ 2: 2 
+ 3: 3 
+ 4: 4 
+ 5: 5 
+ 6: 6 
+ 7: 7 
+ 8: 8 
+ 9: 9 
+10: 10 
+11: 11 
+12: 12 
+13: 13 
+14: 14 
+15: 15 
+16: 16 
+17: 17 
+18: 18 
+19: 19 
+20: 20 
+21: 21 
+22: 22 
+23: 23 
+24: 24 
+25: 25 
+26: 26 
+27: 27 
+28: 28 
+29: 29 
+30: 30 
+31: 31 
+32: 32 
+33: 33 
+34: 34 
+35: 35 
+36: 36 
+37: 37 
+38: 38 
+39: 39 
+40: 40 
+41: 41 
+42: 42 
+43: 43 
+44: 44 
+45: 45 
+46: 46 
+47: 47 
+48: 48 
+49: 49 
+50: 50 
+51: 51 
+52: 52 
+53: 53 
+54: 54 
+55: 55 
+56: 56 
+57: 57 
+58: 58 
+59: 59 
+60: 60 
+61: 61 
+62: 62 
+63: 63 
+64: 64 
+65: 65 
+66: 66 
+67: 67 
+68: 68 
+69: 69 
+70: 70 
+71: 71 
+72: 72 
+73: 73 
+74: 74 
+75: 75 
+76: 76 
+77: 77 
+78: 78 
+79: 79 
+80: 80 
+81: 81 
+82: 82 
+83: 83 
+84: 84 
+85: 85 
+86: 86 
+87: 87 
+88: 88 
+89: 89 
+90: 90 
+91: 91 
+92: 92 
+93: 93 
+94: 94 
+95: 95 
+96: 96 
+97: 97 
+98: 98 
+99: 99 
+100: 100 
+101: 101 
+102: 102 
+103: 103 
+104: 104 
+105: 105 
+106: 106 
+107: 107 
+108: 108 
+109: 109 
+110: 110 
+111: 111 
+112: 112 
+113: 113 
+114: 114 
+115: 115 
+116: 116 
+117: 117 
+118: 118 
+119: 119 
+120: 120 
+121: 121 
+122: 122 
+123: 123 
+124: 124 
+125: 125 
+126: 126 
+127: 127 
+128: 128 
+129: 129 
+130: 130 
+131: 131 
+132: 132 
+133: 133 
+134: 134 
+135: 135 
+136: 136 
+137: 137 
+138: 138 
+139: 139 
+140: 140 
+141: 141 
+142: 142 
+143: 143 
+144: 144 
+145: 145 
+146: 146 
+147: 147 
+148: 148 
+149: 149 
+150: 150 
+151: 151 
+152: 152 
+153: 153 
+154: 154 
+155: 155 
+156: 156 
+157: 157 
+158: 158 
+159: 159 
+160: 160 
+161: 161 
+162: 162 
+163: 163 
+164: 164 
+165: 165 
+166: 166 
+167: 167 
+168: 168 
+169: 169 
+170: 170 
+171: 171 
+172: 172 
+173: 173 
+174: 174 
+175: 175 
+176: 176 
+177: 177 
+178: 178 
+179: 179 
+180: 180 
+181: 181 
+182: 182 
+183: 183 
+184: 184 
+185: 185 
+186: 186 
+187: 187 
+188: 188 
+189: 189 
+190: 190 
+191: 191 
+192: 192 
+193: 193 
+194: 194 
+195: 195 
+196: 196 
+197: 197 
+198: 198 
+199: 199 
+200: 200 
+201: 201 
+202: 202 
+203: 203 
+204: 204 
+205: 205 
+206: 206 
+207: 207 
+208: 208 
+209: 209 
+210: 210 
+211: 211 
+212: 212 
+213: 213 
+214: 214 
+215: 215 
+216: 216 
+217: 217 
+218: 218 
+219: 219 
+220: 220 
+221: 221 
+222: 222 
+223: 223 
+224: 224 
+225: 225 
+226: 226 
+227: 227 
+228: 228 
+229: 229 
+230: 230 
+231: 231 
+232: 232 
+233: 233 
+234: 234 
+235: 235 
+236: 236 
+237: 237 
+238: 238 
+239: 239 
+240: 240 
+241: 241 
+242: 242 
+243: 243 
+244: 244 
+245: 245 
+246: 246 
+247: 247 
+248: 248 
+249: 249 
+250: 250 
+251: 251 
+252: 252 
+253: 253 
+254: 254 
+255: 255 
+256: 256 
+257: 257 
+258: 258 
+259: 259 
+260: 260 
+261: 261 
+262: 262 
+263: 263 
+264: 264 
+265: 265 
+266: 266 
+267: 267 
+268: 268 
+269: 269 
+270: ABC
+271: ABC
+
+/This one's here because Perl does this differently and PCRE can't at present/
+Capturing subpattern count = 0
+No options
+First char = 'T'
+Need char = 't'
+
+/(main(O)?)+/
+Capturing subpattern count = 2
+No options
+First char = 'm'
+Need char = 'n'
+    mainmain
+ 0: mainmain
+ 1: main
+    mainOmain
+ 0: mainOmain
+ 1: main
+ 2: O
+    
+/These are all cases where Perl does it differently (nested captures)/
+Capturing subpattern count = 1
+No options
+First char = 'T'
+Need char = 's'
+
+/^(a(b)?)+$/
+Capturing subpattern count = 2
+Options: anchored
+No first char
+No need char
+    aba
+ 0: aba
+ 1: a
+ 2: b
+   
+/^(aa(bb)?)+$/
+Capturing subpattern count = 2
+Options: anchored
+No first char
+No need char
+    aabbaa    
+ 0: aabbaa
+ 1: aa
+ 2: bb
+    
+/^(aa|aa(bb))+$/
+Capturing subpattern count = 2
+Options: anchored
+No first char
+No need char
+    aabbaa 
+ 0: aabbaa
+ 1: aa
+ 2: bb
+    
+/^(aa(bb)??)+$/
+Capturing subpattern count = 2
+Options: anchored
+No first char
+No need char
+    aabbaa    
+ 0: aabbaa
+ 1: aa
+ 2: bb
+    
+/^(?:aa(bb)?)+$/
+Capturing subpattern count = 1
+Options: anchored
+No first char
+No need char
+    aabbaa    
+ 0: aabbaa
+ 1: bb
+    
+/^(aa(b(b))?)+$/
+Capturing subpattern count = 3
+Options: anchored
+No first char
+No need char
+    aabbaa    
+ 0: aabbaa
+ 1: aa
+ 2: bb
+ 3: b
+
+/^(?:aa(b(b))?)+$/
+Capturing subpattern count = 2
+Options: anchored
+No first char
+No need char
+    aabbaa    
+ 0: aabbaa
+ 1: bb
+ 2: b
+
+/^(?:aa(b(?:b))?)+$/
+Capturing subpattern count = 1
+Options: anchored
+No first char
+No need char
+    aabbaa    
+ 0: aabbaa
+ 1: bb
+
+/^(?:aa(bb(?:b))?)+$/
+Capturing subpattern count = 1
+Options: anchored
+No first char
+No need char
+    aabbbaa    
+ 0: aabbbaa
+ 1: bbb
+    
+/^(?:aa(b(?:bb))?)+$/
+Capturing subpattern count = 1
+Options: anchored
+No first char
+No need char
+    aabbbaa    
+ 0: aabbbaa
+ 1: bbb
+
+/^(?:aa(?:b(b))?)+$/
+Capturing subpattern count = 1
+Options: anchored
+No first char
+No need char
+    aabbaa    
+ 0: aabbaa
+ 1: b
+
+/^(?:aa(?:b(bb))?)+$/
+Capturing subpattern count = 1
+Options: anchored
+No first char
+No need char
+    aabbbaa    
+ 0: aabbbaa
+ 1: bb
+
+/^(aa(b(bb))?)+$/
+Capturing subpattern count = 3
+Options: anchored
+No first char
+No need char
+    aabbbaa    
+ 0: aabbbaa
+ 1: aa
+ 2: bbb
+ 3: bb
+
+/^(aa(bb(bb))?)+$/
+Capturing subpattern count = 3
+Options: anchored
+No first char
+No need char
+    aabbbbaa    
+ 0: aabbbbaa
+ 1: aa
+ 2: bbbb
+ 3: bb
+
+/--------------------------------------------------------------------/ 
+Capturing subpattern count = 0
+No options
+First char = '-'
+Need char = '-'
+    
+/#/xMD
+Memory allocation (code space): 7
+------------------------------------------------------------------
+  0   3 Bra 0
+  3   3 Ket
+  6     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: extended
+No first char
+No need char
+
+/a#/xMD
+Memory allocation (code space): 9
+------------------------------------------------------------------
+  0   5 Bra 0
+  3     a
+  5   5 Ket
+  8     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: extended
+First char = 'a'
+No need char
+
+/[\s]/D
+------------------------------------------------------------------
+  0  36 Bra 0
+  3     [\x09\x0a\x0c\x0d ]
+ 36  36 Ket
+ 39     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+
+/[\S]/D
+------------------------------------------------------------------
+  0  36 Bra 0
+  3     [\x00-\x08\x0b\x0e-\x1f!-\xff]
+ 36  36 Ket
+ 39     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+
+/a(?i)b/D
+------------------------------------------------------------------
+  0   9 Bra 0
+  3     a
+  5  01 Opt
+  7  NC b
+  9   9 Ket
+ 12     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+No options
+Case state changes
+First char = 'a'
+Need char = 'b' (caseless)
+    ab
+ 0: ab
+    aB
+ 0: aB
+    *** Failers 
+No match
+    AB  
+No match
+
+/(a(?i)b)/D
+------------------------------------------------------------------
+  0  17 Bra 0
+  3   9 Bra 1
+  6     a
+  8  01 Opt
+ 10  NC b
+ 12   9 Ket
+ 15  00 Opt
+ 17  17 Ket
+ 20     End
+------------------------------------------------------------------
+Capturing subpattern count = 1
+No options
+Case state changes
+First char = 'a'
+Need char = 'b' (caseless)
+    ab
+ 0: ab
+ 1: ab
+    aB
+ 0: aB
+ 1: aB
+    *** Failers 
+No match
+    AB  
+No match
+    
+/   (?i)abc/xD
+------------------------------------------------------------------
+  0   9 Bra 0
+  3  NC abc
+  9   9 Ket
+ 12     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: caseless extended
+First char = 'a' (caseless)
+Need char = 'c' (caseless)
+
+/#this is a comment
+  (?i)abc/xD
+------------------------------------------------------------------
+  0   9 Bra 0
+  3  NC abc
+  9   9 Ket
+ 12     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: caseless extended
+First char = 'a' (caseless)
+Need char = 'c' (caseless)
+
+/123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890/D
+------------------------------------------------------------------
+  0 603 Bra 0
+  3     123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
+603 603 Ket
+606     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+No options
+First char = '1'
+Need char = '0'
+
+/\Q123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890/D
+------------------------------------------------------------------
+  0 603 Bra 0
+  3     123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
+603 603 Ket
+606     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+No options
+First char = '1'
+Need char = '0'
+
+/\Q\E/D
+------------------------------------------------------------------
+  0   3 Bra 0
+  3   3 Ket
+  6     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+    \
+ 0: 
+
+/\Q\Ex/D
+------------------------------------------------------------------
+  0   5 Bra 0
+  3     x
+  5   5 Ket
+  8     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+No options
+First char = 'x'
+No need char
+
+/ \Q\E/D
+------------------------------------------------------------------
+  0   5 Bra 0
+  3      
+  5   5 Ket
+  8     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+No options
+First char = ' '
+No need char
+
+/a\Q\E/D
+------------------------------------------------------------------
+  0   5 Bra 0
+  3     a
+  5   5 Ket
+  8     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+No options
+First char = 'a'
+No need char
+  abc
+ 0: a
+  bca
+ 0: a
+  bac  
+ 0: a
+
+/a\Q\Eb/D
+------------------------------------------------------------------
+  0   7 Bra 0
+  3     ab
+  7   7 Ket
+ 10     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+No options
+First char = 'a'
+Need char = 'b'
+  abc
+ 0: ab
+
+/\Q\Eabc/D
+------------------------------------------------------------------
+  0   9 Bra 0
+  3     abc
+  9   9 Ket
+ 12     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+No options
+First char = 'a'
+Need char = 'c'
+
+/x*+\w/D
+------------------------------------------------------------------
+  0  12 Bra 0
+  3   5 Once
+  6     x*
+  8   5 Ket
+ 11     \w
+ 12  12 Ket
+ 15     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+No first char
+No need char
+    *** Failers
+ 0: F
+    xxxxx
+No match
+    
+/x?+/D
+------------------------------------------------------------------
+  0  11 Bra 0
+  3   5 Once
+  6     x?
+  8   5 Ket
+ 11  11 Ket
+ 14     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+
+/x++/D
+------------------------------------------------------------------
+  0  11 Bra 0
+  3   5 Once
+  6     x+
+  8   5 Ket
+ 11  11 Ket
+ 14     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+First char = 'x'
+No need char
+
+/x{1,3}+/D 
+------------------------------------------------------------------
+  0  15 Bra 0
+  3   9 Once
+  6     x
+  8     x{,2}
+ 12   9 Ket
+ 15  15 Ket
+ 18     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+First char = 'x'
+No need char
+
+/(x)*+/D
+------------------------------------------------------------------
+  0  18 Bra 0
+  3  12 Once
+  6     Brazero
+  7   5 Bra 1
+ 10     x
+ 12   5 KetRmax
+ 15  12 Ket
+ 18  18 Ket
+ 21     End
+------------------------------------------------------------------
+Capturing subpattern count = 1
+No options
+No first char
+No need char
+
+/^(\w++|\s++)*$/
+Capturing subpattern count = 1
+Partial matching not supported
+Options: anchored
+No first char
+No need char
+    now is the time for all good men to come to the aid of the party
+ 0: now is the time for all good men to come to the aid of the party
+ 1: party
+    *** Failers
+No match
+    this is not a line with only words and spaces!
+No match
+    
+/(\d++)(\w)/
+Capturing subpattern count = 2
+Partial matching not supported
+No options
+No first char
+No need char
+    12345a
+ 0: 12345a
+ 1: 12345
+ 2: a
+    *** Failers
+No match
+    12345+ 
+No match
+
+/a++b/
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+First char = 'a'
+Need char = 'b'
+    aaab
+ 0: aaab
+
+/(a++b)/
+Capturing subpattern count = 1
+Partial matching not supported
+No options
+First char = 'a'
+Need char = 'b'
+    aaab
+ 0: aaab
+ 1: aaab
+
+/(a++)b/
+Capturing subpattern count = 1
+Partial matching not supported
+No options
+First char = 'a'
+Need char = 'b'
+    aaab
+ 0: aaab
+ 1: aaa
+
+/([^()]++|\([^()]*\))+/
+Capturing subpattern count = 1
+Partial matching not supported
+No options
+No first char
+No need char
+    ((abc(ade)ufh()()x
+ 0: abc(ade)ufh()()x
+ 1: x
+    
+/\(([^()]++|\([^()]+\))+\)/ 
+Capturing subpattern count = 1
+Partial matching not supported
+No options
+First char = '('
+Need char = ')'
+    (abc)
+ 0: (abc)
+ 1: abc
+    (abc(def)xyz)
+ 0: (abc(def)xyz)
+ 1: xyz
+    *** Failers
+No match
+    ((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa   
+No match
+
+/(abc){1,3}+/D
+------------------------------------------------------------------
+  0  53 Bra 0
+  3  47 Once
+  6   9 Bra 1
+  9     abc
+ 15   9 Ket
+ 18     Brazero
+ 19  28 Bra 0
+ 22   9 Bra 1
+ 25     abc
+ 31   9 Ket
+ 34     Brazero
+ 35   9 Bra 1
+ 38     abc
+ 44   9 Ket
+ 47  28 Ket
+ 50  47 Ket
+ 53  53 Ket
+ 56     End
+------------------------------------------------------------------
+Capturing subpattern count = 1
+No options
+First char = 'a'
+Need char = 'c'
+
+/a+?+/
+Failed: nothing to repeat at offset 3
+
+/a{2,3}?+b/
+Failed: nothing to repeat at offset 7
+
+/(?U)a+?+/
+Failed: nothing to repeat at offset 7
+
+/a{2,3}?+b/U
+Failed: nothing to repeat at offset 7
+
+/x(?U)a++b/D
+------------------------------------------------------------------
+  0  15 Bra 0
+  3     x
+  5   5 Once
+  8     a+
+ 10   5 Ket
+ 13     b
+ 15  15 Ket
+ 18     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+First char = 'x'
+Need char = 'b'
+    xaaaab
+ 0: xaaaab
+
+/(?U)xa++b/D
+------------------------------------------------------------------
+  0  15 Bra 0
+  3     x
+  5   5 Once
+  8     a+
+ 10   5 Ket
+ 13     b
+ 15  15 Ket
+ 18     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Partial matching not supported
+Options: ungreedy
+First char = 'x'
+Need char = 'b'
+    xaaaab
+ 0: xaaaab
+
+/^((a+)(?U)([ab]+)(?-U)([bc]+)(\w*))/D
+------------------------------------------------------------------
+  0 106 Bra 0
+  3     ^
+  4  99 Bra 1
+  7   5 Bra 2
+ 10     a+
+ 12   5 Ket
+ 15  37 Bra 3
+ 18     [ab]+?
+ 52  37 Ket
+ 55  37 Bra 4
+ 58     [bc]+
+ 92  37 Ket
+ 95   5 Bra 5
+ 98     \w*
+100   5 Ket
+103  99 Ket
+106 106 Ket
+109     End
+------------------------------------------------------------------
+Capturing subpattern count = 5
+Partial matching not supported
+Options: anchored
+No first char
+No need char
+
+/^x(?U)a+b/D
+------------------------------------------------------------------
+  0  10 Bra 0
+  3     ^
+  4     x
+  6     a+?
+  8     b
+ 10  10 Ket
+ 13     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Partial matching not supported
+Options: anchored
+No first char
+Need char = 'b'
+
+/^x(?U)(a+)b/D
+------------------------------------------------------------------
+  0  16 Bra 0
+  3     ^
+  4     x
+  6   5 Bra 1
+  9     a+?
+ 11   5 Ket
+ 14     b
+ 16  16 Ket
+ 19     End
+------------------------------------------------------------------
+Capturing subpattern count = 1
+Partial matching not supported
+Options: anchored
+No first char
+Need char = 'b'
+
+/[.x.]/
+Failed: POSIX collating elements are not supported at offset 0
+
+/[=x=]/
+Failed: POSIX collating elements are not supported at offset 0
+
+/[:x:]/
+Failed: POSIX named classes are supported only within a class at offset 0
+
+/\l/
+Failed: PCRE does not support \L, \l, \N, \U, or \u at offset 1
+
+/\L/
+Failed: PCRE does not support \L, \l, \N, \U, or \u at offset 1
+
+/\N{name}/
+Failed: PCRE does not support \L, \l, \N, \U, or \u at offset 1
+
+/\u/
+Failed: PCRE does not support \L, \l, \N, \U, or \u at offset 1
+
+/\U/
+Failed: PCRE does not support \L, \l, \N, \U, or \u at offset 1
+
+/[/
+Failed: missing terminating ] for character class at offset 1
+
+/[a-/
+Failed: missing terminating ] for character class at offset 3
+
+/[[:space:]/
+Failed: missing terminating ] for character class at offset 10
+
+/[\s]/DM
+Memory allocation (code space): 40
+------------------------------------------------------------------
+  0  36 Bra 0
+  3     [\x09\x0a\x0c\x0d ]
+ 36  36 Ket
+ 39     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+
+/[[:space:]]/DM
+Memory allocation (code space): 40
+------------------------------------------------------------------
+  0  36 Bra 0
+  3     [\x09-\x0d ]
+ 36  36 Ket
+ 39     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+
+/[[:space:]abcde]/DM
+Memory allocation (code space): 40
+------------------------------------------------------------------
+  0  36 Bra 0
+  3     [\x09-\x0d a-e]
+ 36  36 Ket
+ 39     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+
+/< (?: (?(R) \d++  | [^<>]*+) | (?R)) * >/x
+Capturing subpattern count = 0
+Partial matching not supported
+Options: extended
+First char = '<'
+Need char = '>'
+    <>
+ 0: <>
+    <abcd>
+ 0: <abcd>
+    <abc <123> hij>
+ 0: <abc <123> hij>
+    <abc <def> hij>
+ 0: <def>
+    <abc<>def> 
+ 0: <abc<>def>
+    <abc<>      
+ 0: <>
+    *** Failers
+No match
+    <abc
+No match
+
+|8J\$WE\<\.rX\+ix\[d1b\!H\#\?vV0vrK\:ZH1\=2M\>iV\;\?aPhFB\<\*vW\@QW\@sO9\}cfZA\-i\'w\%hKd6gt1UJP\,15_\#QY\$M\^Mss_U\/\]\&LK9\[5vQub\^w\[KDD\<EjmhUZ\?\.akp2dF\>qmj\;2\}YWFdYx\.Ap\]hjCPTP\(n28k\+3\;o\&WXqs\/gOXdr\$\:r\'do0\;b4c\(f_Gr\=\"\\4\)\[01T7ajQJvL\$W\~mL_sS\/4h\:x\*\[ZN\=KLs\&L5zX\/\/\>it\,o\:aU\(\;Z\>pW\&T7oP\'2K\^E\:x9\'c\[\%z\-\,64JQ5AeH_G\#KijUKghQw\^\\vea3a\?kka_G\$8\#\`\*kynsxzBLru\'\]k_\[7FrVx\}\^\=\$blx\>s\-N\%j\;D\*aZDnsw\:YKZ\%Q\.Kne9\#hP\?\+b3\(SOvL\,\^\;\&u5\@\?5C5Bhb\=m\-vEh_L15Jl\]U\)0RP6\{q\%L\^_z5E\'Dw6X\b|DM
+Memory allocation (code space): 826
+------------------------------------------------------------------
+  0 822 Bra 0
+  3     8J$WE<.rX+ix[d1b!H#?vV0vrK:ZH1=2M>iV;?aPhFB<*vW@QW@sO9}cfZA-i'w%hKd6gt1UJP,15_#QY$M^Mss_U/]&LK9[5vQub^w[KDD<EjmhUZ?.akp2dF>qmj;2}YWFdYx.Ap]hjCPTP(n28k+3;o&WXqs/gOXdr$:r'do0;b4c(f_Gr="\4)[01T7ajQJvL$W~mL_sS/4h:x*[ZN=KLs&L5zX//>it,o:aU(;Z>pW&T7oP'2K^E:x9'c[%z-,64JQ5AeH_G#KijUKghQw^\vea3a?kka_G$8#`*kynsxzBLru']k_[7FrVx}^=$blx>s-N%j;D*aZDnsw:YKZ%Q.Kne9#hP?+b3(SOvL,^;&u5@?5C5Bhb=m-vEh_L15Jl]U)0RP6{q%L^_z5E'Dw6X
+821     \b
+822 822 Ket
+825     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+No options
+First char = '8'
+Need char = 'X'
+
+|\$\<\.X\+ix\[d1b\!H\#\?vV0vrK\:ZH1\=2M\>iV\;\?aPhFB\<\*vW\@QW\@sO9\}cfZA\-i\'w\%hKd6gt1UJP\,15_\#QY\$M\^Mss_U\/\]\&LK9\[5vQub\^w\[KDD\<EjmhUZ\?\.akp2dF\>qmj\;2\}YWFdYx\.Ap\]hjCPTP\(n28k\+3\;o\&WXqs\/gOXdr\$\:r\'do0\;b4c\(f_Gr\=\"\\4\)\[01T7ajQJvL\$W\~mL_sS\/4h\:x\*\[ZN\=KLs\&L5zX\/\/\>it\,o\:aU\(\;Z\>pW\&T7oP\'2K\^E\:x9\'c\[\%z\-\,64JQ5AeH_G\#KijUKghQw\^\\vea3a\?kka_G\$8\#\`\*kynsxzBLru\'\]k_\[7FrVx\}\^\=\$blx\>s\-N\%j\;D\*aZDnsw\:YKZ\%Q\.Kne9\#hP\?\+b3\(SOvL\,\^\;\&u5\@\?5C5Bhb\=m\-vEh_L15Jl\]U\)0RP6\{q\%L\^_z5E\'Dw6X\b|DM
+Memory allocation (code space): 816
+------------------------------------------------------------------
+  0 812 Bra 0
+  3     $<.X+ix[d1b!H#?vV0vrK:ZH1=2M>iV;?aPhFB<*vW@QW@sO9}cfZA-i'w%hKd6gt1UJP,15_#QY$M^Mss_U/]&LK9[5vQub^w[KDD<EjmhUZ?.akp2dF>qmj;2}YWFdYx.Ap]hjCPTP(n28k+3;o&WXqs/gOXdr$:r'do0;b4c(f_Gr="\4)[01T7ajQJvL$W~mL_sS/4h:x*[ZN=KLs&L5zX//>it,o:aU(;Z>pW&T7oP'2K^E:x9'c[%z-,64JQ5AeH_G#KijUKghQw^\vea3a?kka_G$8#`*kynsxzBLru']k_[7FrVx}^=$blx>s-N%j;D*aZDnsw:YKZ%Q.Kne9#hP?+b3(SOvL,^;&u5@?5C5Bhb=m-vEh_L15Jl]U)0RP6{q%L^_z5E'Dw6X
+811     \b
+812 812 Ket
+815     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+No options
+First char = '$'
+Need char = 'X'
+
+/(.*)\d+\1/I
+Capturing subpattern count = 1
+Max back reference = 1
+Partial matching not supported
+No options
+No first char
+No need char
+
+/(.*)\d+/I
+Capturing subpattern count = 1
+Partial matching not supported
+No options
+First char at start or follows \n
+No need char
+    
+/(.*)\d+\1/Is
+Capturing subpattern count = 1
+Max back reference = 1
+Partial matching not supported
+Options: dotall
+No first char
+No need char
+
+/(.*)\d+/Is
+Capturing subpattern count = 1
+Partial matching not supported
+Options: anchored dotall
+No first char
+No need char
+
+/(.*(xyz))\d+\2/I
+Capturing subpattern count = 2
+Max back reference = 2
+Partial matching not supported
+No options
+First char at start or follows \n
+Need char = 'z'
+
+/((.*))\d+\1/I
+Capturing subpattern count = 2
+Max back reference = 1
+Partial matching not supported
+No options
+No first char
+No need char
+    abc123bc
+ 0: bc123bc
+ 1: bc
+ 2: bc
+    
+/a[b]/I
+Capturing subpattern count = 0
+No options
+First char = 'a'
+Need char = 'b'
+
+/(?=a).*/I
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+First char = 'a'
+No need char
+
+/(?=abc).xyz/iI
+Capturing subpattern count = 0
+Options: caseless
+First char = 'a' (caseless)
+Need char = 'z' (caseless)
+
+/(?=abc)(?i).xyz/I
+Capturing subpattern count = 0
+No options
+Case state changes
+First char = 'a'
+Need char = 'z' (caseless)
+
+/(?=a)(?=b)/I
+Capturing subpattern count = 0
+No options
+First char = 'a'
+No need char
+
+/(?=.)a/I
+Capturing subpattern count = 0
+No options
+First char = 'a'
+No need char
+
+/((?=abcda)a)/I
+Capturing subpattern count = 1
+No options
+First char = 'a'
+Need char = 'a'
+
+/((?=abcda)ab)/I
+Capturing subpattern count = 1
+No options
+First char = 'a'
+Need char = 'b'
+
+/()a/I
+Capturing subpattern count = 1
+No options
+No first char
+Need char = 'a'
+
+/(?(1)ab|ac)/I
+Capturing subpattern count = 0
+No options
+First char = 'a'
+No need char
+
+/(?(1)abz|acz)/I
+Capturing subpattern count = 0
+No options
+First char = 'a'
+Need char = 'z'
+
+/(?(1)abz)/I
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+
+/(?(1)abz)123/I
+Capturing subpattern count = 0
+No options
+No first char
+Need char = '3'
+
+/(a)+/I
+Capturing subpattern count = 1
+No options
+First char = 'a'
+No need char
+
+/(a){2,3}/I
+Capturing subpattern count = 1
+No options
+First char = 'a'
+Need char = 'a'
+
+/(a)*/I
+Capturing subpattern count = 1
+No options
+No first char
+No need char
+
+/[a]/I
+Capturing subpattern count = 0
+No options
+First char = 'a'
+No need char
+
+/[ab]/I
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+
+/[ab]/IS
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+Starting byte set: a b 
+
+/[^a]/I
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+
+/\d456/I
+Capturing subpattern count = 0
+No options
+No first char
+Need char = '6'
+
+/\d456/IS
+Capturing subpattern count = 0
+No options
+No first char
+Need char = '6'
+Starting byte set: 0 1 2 3 4 5 6 7 8 9 
+
+/a^b/I
+Capturing subpattern count = 0
+No options
+First char = 'a'
+Need char = 'b'
+
+/^a/mI
+Capturing subpattern count = 0
+Options: multiline
+First char at start or follows \n
+Need char = 'a'
+  abcde
+ 0: a
+  xy\nabc 
+ 0: a
+  *** Failers 
+No match
+  xyabc 
+No match
+
+/c|abc/I
+Capturing subpattern count = 0
+No options
+No first char
+Need char = 'c'
+
+/(?i)[ab]/IS
+Capturing subpattern count = 0
+Options: caseless
+No first char
+No need char
+Starting byte set: A B a b 
+
+/[ab](?i)cd/IS
+Capturing subpattern count = 0
+No options
+Case state changes
+No first char
+Need char = 'd' (caseless)
+Starting byte set: a b 
+
+/abc(?C)def/
+Capturing subpattern count = 0
+No options
+First char = 'a'
+Need char = 'f'
+    abcdef
+--->abcdef
+  0 ^  ^       d
+ 0: abcdef
+    1234abcdef 
+--->1234abcdef
+  0     ^  ^       d
+ 0: abcdef
+    *** Failers
+No match
+    abcxyz
+No match
+    abcxyzf   
+--->abcxyzf
+  0 ^  ^        d
+No match
+
+/abc(?C)de(?C1)f/
+Capturing subpattern count = 0
+No options
+First char = 'a'
+Need char = 'f'
+    123abcdef
+--->123abcdef
+  0    ^  ^       d
+  1    ^    ^     f
+ 0: abcdef
+    
+/(?C1)\dabc(?C2)def/ 
+Capturing subpattern count = 0
+No options
+No first char
+Need char = 'f'
+    1234abcdef
+--->1234abcdef
+  1 ^              \d
+  1  ^             \d
+  1   ^            \d
+  1    ^           \d
+  2    ^   ^       d
+ 0: 4abcdef
+    *** Failers
+No match
+    abcdef 
+--->abcdef
+  1 ^          \d
+  1  ^         \d
+  1   ^        \d
+  1    ^       \d
+  1     ^      \d
+  1      ^     \d
+No match
+    
+/(?C255)ab/
+Capturing subpattern count = 0
+No options
+First char = 'a'
+Need char = 'b'
+
+/(?C256)ab/
+Failed: number after (?C is > 255 at offset 6
+
+/(?Cab)xx/ 
+Failed: closing ) for (?C expected at offset 3
+
+/(?C12vr)x/
+Failed: closing ) for (?C expected at offset 5
+
+/abc(?C)def/
+Capturing subpattern count = 0
+No options
+First char = 'a'
+Need char = 'f'
+    *** Failers
+No match
+    \x83\x0\x61bcdef
+--->\x83\x00abcdef
+  0         ^  ^       d
+ 0: abcdef
+
+/(abc)(?C)de(?C1)f/
+Capturing subpattern count = 1
+No options
+First char = 'a'
+Need char = 'f'
+    123abcdef
+--->123abcdef
+  0    ^  ^       d
+  1    ^    ^     f
+ 0: abcdef
+ 1: abc
+    123abcdef\C+ 
+Callout 0: last capture = 1
+ 0: <unset>
+ 1: abc
+--->123abcdef
+       ^  ^       d
+Callout 1: last capture = 1
+ 0: <unset>
+ 1: abc
+--->123abcdef
+       ^    ^     f
+ 0: abcdef
+ 1: abc
+    123abcdef\C- 
+ 0: abcdef
+ 1: abc
+    *** Failers
+No match
+    123abcdef\C!1 
+--->123abcdef
+  0    ^  ^       d
+  1    ^    ^     f
+No match
+    
+/(?C0)(abc(?C1))*/
+Capturing subpattern count = 1
+No options
+No first char
+No need char
+    abcabcabc
+--->abcabcabc
+  0 ^             (abc(?C1))*
+  1 ^  ^          )
+  1 ^     ^       )
+  1 ^        ^    )
+ 0: abcabcabc
+ 1: abc
+    abcabc\C!1!3   
+--->abcabc
+  0 ^          (abc(?C1))*
+  1 ^  ^       )
+  1 ^     ^    )
+ 0: abcabc
+ 1: abc
+    *** Failers
+--->*** Failers
+  0 ^               (abc(?C1))*
+ 0: 
+    abcabcabc\C!1!3   
+--->abcabcabc
+  0 ^             (abc(?C1))*
+  1 ^  ^          )
+  1 ^     ^       )
+  1 ^        ^    )
+ 0: abcabc
+ 1: abc
+
+/(\d{3}(?C))*/
+Capturing subpattern count = 1
+Partial matching not supported
+No options
+No first char
+No need char
+    123\C+
+Callout 0: last capture = -1
+ 0: <unset>
+--->123
+    ^  ^    )
+ 0: 123
+ 1: 123
+    123456\C+
+Callout 0: last capture = -1
+ 0: <unset>
+--->123456
+    ^  ^       )
+Callout 0: last capture = 1
+ 0: <unset>
+ 1: 123
+--->123456
+    ^     ^    )
+ 0: 123456
+ 1: 456
+    123456789\C+  
+Callout 0: last capture = -1
+ 0: <unset>
+--->123456789
+    ^  ^          )
+Callout 0: last capture = 1
+ 0: <unset>
+ 1: 123
+--->123456789
+    ^     ^       )
+Callout 0: last capture = 1
+ 0: <unset>
+ 1: 456
+--->123456789
+    ^        ^    )
+ 0: 123456789
+ 1: 789
+
+/((xyz)(?C)p|(?C1)xyzabc)/
+Capturing subpattern count = 2
+No options
+First char = 'x'
+No need char
+    xyzabc\C+
+Callout 0: last capture = 2
+ 0: <unset>
+ 1: <unset>
+ 2: xyz
+--->xyzabc
+    ^  ^       p
+Callout 1: last capture = -1
+ 0: <unset>
+--->xyzabc
+    ^          x
+ 0: xyzabc
+ 1: xyzabc
+
+/(X)((xyz)(?C)p|(?C1)xyzabc)/
+Capturing subpattern count = 3
+No options
+First char = 'X'
+Need char = 'x'
+    Xxyzabc\C+
+Callout 0: last capture = 3
+ 0: <unset>
+ 1: X
+ 2: <unset>
+ 3: xyz
+--->Xxyzabc
+    ^   ^       p
+Callout 1: last capture = 1
+ 0: <unset>
+ 1: X
+--->Xxyzabc
+    ^^          x
+ 0: Xxyzabc
+ 1: X
+ 2: xyzabc
+
+/(?=(abc))(?C)abcdef/
+Capturing subpattern count = 1
+No options
+First char = 'a'
+Need char = 'f'
+    abcdef\C+
+Callout 0: last capture = 1
+ 0: <unset>
+ 1: abc
+--->abcdef
+    ^          a
+ 0: abcdef
+ 1: abc
+    
+/(?!(abc)(?C1)d)(?C2)abcxyz/
+Capturing subpattern count = 1
+No options
+First char = 'a'
+Need char = 'z'
+    abcxyz\C+ 
+Callout 1: last capture = 1
+ 0: <unset>
+ 1: abc
+--->abcxyz
+    ^  ^       d
+Callout 2: last capture = -1
+ 0: <unset>
+--->abcxyz
+    ^          a
+ 0: abcxyz
+
+/(?<=(abc)(?C))xyz/
+Capturing subpattern count = 1
+No options
+First char = 'x'
+Need char = 'z'
+   abcxyz\C+
+Callout 0: last capture = 1
+ 0: <unset>
+ 1: abc
+--->abcxyz
+       ^       )
+ 0: xyz
+ 1: abc
+   
+/(?C)abc/ 
+Capturing subpattern count = 0
+No options
+First char = 'a'
+Need char = 'c'
+
+/(?C)^abc/
+Capturing subpattern count = 0
+Options: anchored
+No first char
+No need char
+
+/(?C)a|b/S
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+Starting byte set: a b 
+
+/(?R)/
+Failed: recursive call could loop indefinitely at offset 3
+
+/(a|(?R))/
+Failed: recursive call could loop indefinitely at offset 6
+
+/(ab|(bc|(de|(?R))))/
+Failed: recursive call could loop indefinitely at offset 15
+
+/x(ab|(bc|(de|(?R))))/
+Capturing subpattern count = 3
+No options
+First char = 'x'
+No need char
+    xab
+ 0: xab
+ 1: ab
+    xbc
+ 0: xbc
+ 1: bc
+ 2: bc
+    xde
+ 0: xde
+ 1: de
+ 2: de
+ 3: de
+    xxab
+ 0: xxab
+ 1: xab
+ 2: xab
+ 3: xab
+    xxxab
+ 0: xxxab
+ 1: xxab
+ 2: xxab
+ 3: xxab
+    *** Failers
+No match
+    xyab   
+No match
+
+/(ab|(bc|(de|(?1))))/
+Failed: recursive call could loop indefinitely at offset 15
+
+/x(ab|(bc|(de|(?1)x)x)x)/
+Failed: recursive call could loop indefinitely at offset 16
+
+/^([^()]|\((?1)*\))*$/
+Capturing subpattern count = 1
+Options: anchored
+No first char
+No need char
+    abc
+ 0: abc
+ 1: c
+    a(b)c
+ 0: a(b)c
+ 1: c
+    a(b(c))d  
+ 0: a(b(c))d
+ 1: d
+    *** Failers)
+No match
+    a(b(c)d  
+No match
+
+/^>abc>([^()]|\((?1)*\))*<xyz<$/
+Capturing subpattern count = 1
+Options: anchored
+No first char
+Need char = '<'
+   >abc>123<xyz<
+ 0: >abc>123<xyz<
+ 1: 3
+   >abc>1(2)3<xyz<
+ 0: >abc>1(2)3<xyz<
+ 1: 3
+   >abc>(1(2)3)<xyz<
+ 0: >abc>(1(2)3)<xyz<
+ 1: (1(2)3)
+
+/(a(?1)b)/D
+------------------------------------------------------------------
+  0  16 Bra 0
+  3  10 Bra 1
+  6     a
+  8   3 Recurse
+ 11     b
+ 13  10 Ket
+ 16  16 Ket
+ 19     End
+------------------------------------------------------------------
+Capturing subpattern count = 1
+No options
+First char = 'a'
+Need char = 'b'
+
+/(a(?1)+b)/D
+------------------------------------------------------------------
+  0  22 Bra 0
+  3  16 Bra 1
+  6     a
+  8   6 Bra 0
+ 11   3 Recurse
+ 14   6 KetRmax
+ 17     b
+ 19  16 Ket
+ 22  22 Ket
+ 25     End
+------------------------------------------------------------------
+Capturing subpattern count = 1
+No options
+First char = 'a'
+Need char = 'b'
+
+/^\W*(?:((.)\W*(?1)\W*\2|)|((.)\W*(?3)\W*\4|\W*.\W*))\W*$/i
+Capturing subpattern count = 4
+Max back reference = 4
+Partial matching not supported
+Options: anchored caseless
+No first char
+No need char
+    1221
+ 0: 1221
+ 1: 1221
+ 2: 1
+    Satan, oscillate my metallic sonatas!
+ 0: Satan, oscillate my metallic sonatas!
+ 1: <unset>
+ 2: <unset>
+ 3: Satan, oscillate my metallic sonatas
+ 4: S
+    A man, a plan, a canal: Panama!
+ 0: A man, a plan, a canal: Panama!
+ 1: <unset>
+ 2: <unset>
+ 3: A man, a plan, a canal: Panama
+ 4: A
+    Able was I ere I saw Elba. 
+ 0: Able was I ere I saw Elba.
+ 1: <unset>
+ 2: <unset>
+ 3: Able was I ere I saw Elba
+ 4: A
+    *** Failers
+No match
+    The quick brown fox  
+No match
+    
+/^(\d+|\((?1)([+*-])(?1)\)|-(?1))$/
+Capturing subpattern count = 2
+Partial matching not supported
+Options: anchored
+No first char
+No need char
+    12
+ 0: 12
+ 1: 12
+    (((2+2)*-3)-7)
+ 0: (((2+2)*-3)-7)
+ 1: (((2+2)*-3)-7)
+ 2: -
+    -12
+ 0: -12
+ 1: -12
+    *** Failers
+No match
+    ((2+2)*-3)-7)
+No match
+         
+/^(x(y|(?1){2})z)/
+Capturing subpattern count = 2
+Options: anchored
+No first char
+No need char
+    xyz
+ 0: xyz
+ 1: xyz
+ 2: y
+    xxyzxyzz 
+ 0: xxyzxyzz
+ 1: xxyzxyzz
+ 2: xyzxyz
+    *** Failers
+No match
+    xxyzz
+No match
+    xxyzxyzxyzz   
+No match
+
+/((< (?: (?(R) \d++  | [^<>]*+) | (?2)) * >))/x
+Capturing subpattern count = 2
+Partial matching not supported
+Options: extended
+First char = '<'
+Need char = '>'
+    <>
+ 0: <>
+ 1: <>
+ 2: <>
+    <abcd>
+ 0: <abcd>
+ 1: <abcd>
+ 2: <abcd>
+    <abc <123> hij>
+ 0: <abc <123> hij>
+ 1: <abc <123> hij>
+ 2: <abc <123> hij>
+    <abc <def> hij>
+ 0: <def>
+ 1: <def>
+ 2: <def>
+    <abc<>def> 
+ 0: <abc<>def>
+ 1: <abc<>def>
+ 2: <abc<>def>
+    <abc<>      
+ 0: <>
+ 1: <>
+ 2: <>
+    *** Failers
+No match
+    <abc
+No match
+
+/(?1)/
+Failed: reference to non-existent subpattern at offset 3
+
+/((?2)(abc)/
+Failed: reference to non-existent subpattern at offset 4
+
+/^(abc)def(?1)/
+Capturing subpattern count = 1
+Options: anchored
+No first char
+No need char
+    abcdefabc
+ 0: abcdefabc
+ 1: abc
+
+/^(a|b|c)=(?1)+/
+Capturing subpattern count = 1
+Options: anchored
+No first char
+No need char
+    a=a
+ 0: a=a
+ 1: a
+    a=b
+ 0: a=b
+ 1: a
+    a=bc  
+ 0: a=bc
+ 1: a
+
+/^(a|b|c)=((?1))+/
+Capturing subpattern count = 2
+Options: anchored
+No first char
+No need char
+    a=a
+ 0: a=a
+ 1: a
+ 2: a
+    a=b
+ 0: a=b
+ 1: a
+ 2: b
+    a=bc  
+ 0: a=bc
+ 1: a
+ 2: c
+
+/a(?P<name1>b|c)d(?P<longername2>e)/D
+------------------------------------------------------------------
+  0  28 Bra 0
+  3     a
+  5   5 Bra 1
+  8     b
+ 10   5 Alt
+ 13     c
+ 15  10 Ket
+ 18     d
+ 20   5 Bra 2
+ 23     e
+ 25   5 Ket
+ 28  28 Ket
+ 31     End
+------------------------------------------------------------------
+Capturing subpattern count = 2
+Named capturing subpatterns:
+  longername2   2
+  name1         1
+No options
+First char = 'a'
+Need char = 'e'
+    abde
+ 0: abde
+ 1: b
+ 2: e
+    acde 
+ 0: acde
+ 1: c
+ 2: e
+
+/(?:a(?P<c>c(?P<d>d)))(?P<a>a)/D
+------------------------------------------------------------------
+  0  35 Bra 0
+  3  21 Bra 0
+  6     a
+  8  13 Bra 1
+ 11     c
+ 13   5 Bra 2
+ 16     d
+ 18   5 Ket
+ 21  13 Ket
+ 24  21 Ket
+ 27   5 Bra 3
+ 30     a
+ 32   5 Ket
+ 35  35 Ket
+ 38     End
+------------------------------------------------------------------
+Capturing subpattern count = 3
+Named capturing subpatterns:
+  a   3
+  c   1
+  d   2
+No options
+First char = 'a'
+Need char = 'a'
+
+/(?P<a>a)...(?P=a)bbb(?P>a)d/D
+------------------------------------------------------------------
+  0  28 Bra 0
+  3   5 Bra 1
+  6     a
+  8   5 Ket
+ 11     Any
+ 12     Any
+ 13     Any
+ 14     \1
+ 17     bbb
+ 23   3 Recurse
+ 26     d
+ 28  28 Ket
+ 31     End
+------------------------------------------------------------------
+Capturing subpattern count = 1
+Max back reference = 1
+Named capturing subpatterns:
+  a   1
+No options
+First char = 'a'
+Need char = 'd'
+
+/^\W*(?:(?P<one>(?P<two>.)\W*(?P>one)\W*(?P=two)|)|(?P<three>(?P<four>.)\W*(?P>three)\W*(?P=four)|\W*.\W*))\W*$/i
+Capturing subpattern count = 4
+Max back reference = 4
+Named capturing subpatterns:
+  four    4
+  one     1
+  three   3
+  two     2
+Partial matching not supported
+Options: anchored caseless
+No first char
+No need char
+    1221
+ 0: 1221
+ 1: 1221
+ 2: 1
+    Satan, oscillate my metallic sonatas!
+ 0: Satan, oscillate my metallic sonatas!
+ 1: <unset>
+ 2: <unset>
+ 3: Satan, oscillate my metallic sonatas
+ 4: S
+    A man, a plan, a canal: Panama!
+ 0: A man, a plan, a canal: Panama!
+ 1: <unset>
+ 2: <unset>
+ 3: A man, a plan, a canal: Panama
+ 4: A
+    Able was I ere I saw Elba. 
+ 0: Able was I ere I saw Elba.
+ 1: <unset>
+ 2: <unset>
+ 3: Able was I ere I saw Elba
+ 4: A
+    *** Failers
+No match
+    The quick brown fox  
+No match
+    
+/((?(R)a|b))\1(?1)?/
+Capturing subpattern count = 1
+Max back reference = 1
+No options
+No first char
+No need char
+  bb
+ 0: bb
+ 1: b
+  bbaa 
+ 0: bba
+ 1: b
+
+/(.*)a/sI
+Capturing subpattern count = 1
+Partial matching not supported
+Options: anchored dotall
+No first char
+Need char = 'a'
+
+/(.*)a\1/sI
+Capturing subpattern count = 1
+Max back reference = 1
+Partial matching not supported
+Options: dotall
+No first char
+Need char = 'a'
+
+/(.*)a(b)\2/sI
+Capturing subpattern count = 2
+Max back reference = 2
+Partial matching not supported
+Options: anchored dotall
+No first char
+Need char = 'b'
+
+/((.*)a|(.*)b)z/sI
+Capturing subpattern count = 3
+Partial matching not supported
+Options: anchored dotall
+No first char
+Need char = 'z'
+
+/((.*)a|(.*)b)z\1/sI
+Capturing subpattern count = 3
+Max back reference = 1
+Partial matching not supported
+Options: dotall
+No first char
+Need char = 'z'
+
+/((.*)a|(.*)b)z\2/sI
+Capturing subpattern count = 3
+Max back reference = 2
+Partial matching not supported
+Options: dotall
+No first char
+Need char = 'z'
+
+/((.*)a|(.*)b)z\3/sI
+Capturing subpattern count = 3
+Max back reference = 3
+Partial matching not supported
+Options: dotall
+No first char
+Need char = 'z'
+
+/((.*)a|^(.*)b)z\3/sI
+Capturing subpattern count = 3
+Max back reference = 3
+Partial matching not supported
+Options: anchored dotall
+No first char
+Need char = 'z'
+
+/(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)a/sI
+Capturing subpattern count = 31
+Partial matching not supported
+Options: anchored dotall
+No first char
+No need char
+
+/(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)a\31/sI
+Capturing subpattern count = 31
+Max back reference = 31
+Partial matching not supported
+Options: dotall
+No first char
+No need char
+
+/(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)|(.*)a\32/sI
+Capturing subpattern count = 32
+Max back reference = 32
+Partial matching not supported
+Options: dotall
+No first char
+No need char
+
+/(a)(bc)/ND
+------------------------------------------------------------------
+  0  21 Bra 0
+  3   5 Bra 0
+  6     a
+  8   5 Ket
+ 11   7 Bra 0
+ 14     bc
+ 18   7 Ket
+ 21  21 Ket
+ 24     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options:
+First char = 'a'
+Need char = 'c'
+  abc
+ 0: abc
+
+/(?P<one>a)(bc)/ND
+------------------------------------------------------------------
+  0  21 Bra 0
+  3   5 Bra 1
+  6     a
+  8   5 Ket
+ 11   7 Bra 0
+ 14     bc
+ 18   7 Ket
+ 21  21 Ket
+ 24     End
+------------------------------------------------------------------
+Capturing subpattern count = 1
+Named capturing subpatterns:
+  one   1
+Options:
+First char = 'a'
+Need char = 'c'
+  abc
+ 0: abc
+ 1: a
+
+/(a)(?P<named>bc)/ND
+------------------------------------------------------------------
+  0  21 Bra 0
+  3   5 Bra 0
+  6     a
+  8   5 Ket
+ 11   7 Bra 1
+ 14     bc
+ 18   7 Ket
+ 21  21 Ket
+ 24     End
+------------------------------------------------------------------
+Capturing subpattern count = 1
+Named capturing subpatterns:
+  named   1
+Options:
+First char = 'a'
+Need char = 'c'
+
+/(a+)*zz/
+Capturing subpattern count = 1
+Partial matching not supported
+No options
+No first char
+Need char = 'z'
+  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaazzbbbbbb\M
+Minimum match limit = 8
+ 0: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaazz
+ 1: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+  aaaaaaaaaaaaaz\M
+Minimum match limit = 32768
+No match
+
+/(aaa(?C1)bbb|ab)/
+Capturing subpattern count = 1
+No options
+First char = 'a'
+Need char = 'b'
+   aaabbb
+--->aaabbb
+  1 ^  ^       b
+ 0: aaabbb
+ 1: aaabbb
+   aaabbb\C*0
+--->aaabbb
+  1 ^  ^       b
+ 0: aaabbb
+ 1: aaabbb
+   aaabbb\C*1
+--->aaabbb
+  1 ^  ^       b
+Callout data = 1
+ 0: ab
+ 1: ab
+   aaabbb\C*-1
+--->aaabbb
+  1 ^  ^       b
+Callout data = -1
+No match
+
+/ab(?P<one>cd)ef(?P<two>gh)/
+Capturing subpattern count = 2
+Named capturing subpatterns:
+  one   1
+  two   2
+No options
+First char = 'a'
+Need char = 'h'
+    abcdefgh
+ 0: abcdefgh
+ 1: cd
+ 2: gh
+    abcdefgh\C1\Gtwo
+ 0: abcdefgh
+ 1: cd
+ 2: gh
+ 1C cd (2)
+ 2G gh (2)
+    abcdefgh\Cone\Ctwo
+ 0: abcdefgh
+ 1: cd
+ 2: gh
+ 1C cd (2)
+ 2C gh (2)
+    abcdefgh\Cthree  
+no parentheses with name "three"
+ 0: abcdefgh
+ 1: cd
+ 2: gh
+
+/(?P<Tes>)(?P<Test>)/D
+------------------------------------------------------------------
+  0  15 Bra 0
+  3   3 Bra 1
+  6   3 Ket
+  9   3 Bra 2
+ 12   3 Ket
+ 15  15 Ket
+ 18     End
+------------------------------------------------------------------
+Capturing subpattern count = 2
+Named capturing subpatterns:
+  Tes    1
+  Test   2
+No options
+No first char
+No need char
+
+/(?P<Test>)(?P<Tes>)/D
+------------------------------------------------------------------
+  0  15 Bra 0
+  3   3 Bra 1
+  6   3 Ket
+  9   3 Bra 2
+ 12   3 Ket
+ 15  15 Ket
+ 18     End
+------------------------------------------------------------------
+Capturing subpattern count = 2
+Named capturing subpatterns:
+  Tes    2
+  Test   1
+No options
+No first char
+No need char
+
+/(?P<Z>zz)(?P<A>aa)/
+Capturing subpattern count = 2
+Named capturing subpatterns:
+  A   2
+  Z   1
+No options
+First char = 'z'
+Need char = 'a'
+    zzaa\CZ
+ 0: zzaa
+ 1: zz
+ 2: aa
+ 1C zz (2)
+    zzaa\CA
+ 0: zzaa
+ 1: zz
+ 2: aa
+ 2C aa (2)
+
+/(?P<x>eks)(?P<x>eccs)/
+Failed: two named groups have the same name at offset 16
+
+/(?P<abc>abc(?P<def>def)(?P<abc>xyz))/
+Failed: two named groups have the same name at offset 31
+
+"\[((?P<elem>\d+)(,(?P>elem))*)\]"
+Capturing subpattern count = 3
+Named capturing subpatterns:
+  elem   2
+Partial matching not supported
+No options
+First char = '['
+Need char = ']'
+    [10,20,30,5,5,4,4,2,43,23,4234]
+ 0: [10,20,30,5,5,4,4,2,43,23,4234]
+ 1: 10,20,30,5,5,4,4,2,43,23,4234
+ 2: 10
+ 3: ,4234
+    *** Failers
+No match
+    []  
+No match
+
+"\[((?P<elem>\d+)(,(?P>elem))*)?\]"
+Capturing subpattern count = 3
+Named capturing subpatterns:
+  elem   2
+Partial matching not supported
+No options
+First char = '['
+Need char = ']'
+    [10,20,30,5,5,4,4,2,43,23,4234]
+ 0: [10,20,30,5,5,4,4,2,43,23,4234]
+ 1: 10,20,30,5,5,4,4,2,43,23,4234
+ 2: 10
+ 3: ,4234
+    [] 
+ 0: []
+
+/(a(b(?2)c))?/D
+------------------------------------------------------------------
+  0  25 Bra 0
+  3     Brazero
+  4  18 Bra 1
+  7     a
+  9  10 Bra 2
+ 12     b
+ 14   9 Recurse
+ 17     c
+ 19  10 Ket
+ 22  18 Ket
+ 25  25 Ket
+ 28     End
+------------------------------------------------------------------
+Capturing subpattern count = 2
+No options
+No first char
+No need char
+
+/(a(b(?2)c))*/D
+------------------------------------------------------------------
+  0  25 Bra 0
+  3     Brazero
+  4  18 Bra 1
+  7     a
+  9  10 Bra 2
+ 12     b
+ 14   9 Recurse
+ 17     c
+ 19  10 Ket
+ 22  18 KetRmax
+ 25  25 Ket
+ 28     End
+------------------------------------------------------------------
+Capturing subpattern count = 2
+No options
+No first char
+No need char
+
+/(a(b(?2)c)){0,2}/D
+------------------------------------------------------------------
+  0  53 Bra 0
+  3     Brazero
+  4  46 Bra 0
+  7  18 Bra 1
+ 10     a
+ 12  10 Bra 2
+ 15     b
+ 17  12 Recurse
+ 20     c
+ 22  10 Ket
+ 25  18 Ket
+ 28     Brazero
+ 29  18 Bra 1
+ 32     a
+ 34  10 Bra 2
+ 37     b
+ 39  12 Recurse
+ 42     c
+ 44  10 Ket
+ 47  18 Ket
+ 50  46 Ket
+ 53  53 Ket
+ 56     End
+------------------------------------------------------------------
+Capturing subpattern count = 2
+No options
+No first char
+No need char
+
+/[ab]{1}+/D
+------------------------------------------------------------------
+  0  47 Bra 0
+  3  41 Once
+  6     [ab]{1,1}
+ 44  41 Ket
+ 47  47 Ket
+ 50     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+
+/((w\/|-|with)*(free|immediate)*.*?shipping\s*[!.-]*)/i
+Capturing subpattern count = 3
+Partial matching not supported
+Options: caseless
+No first char
+Need char = 'g' (caseless)
+     Baby Bjorn Active Carrier - With free SHIPPING!!
+ 0: Baby Bjorn Active Carrier - With free SHIPPING!!
+ 1: Baby Bjorn Active Carrier - With free SHIPPING!!
+
+/((w\/|-|with)*(free|immediate)*.*?shipping\s*[!.-]*)/iS
+Capturing subpattern count = 3
+Partial matching not supported
+Options: caseless
+No first char
+Need char = 'g' (caseless)
+Study returned NULL
+     Baby Bjorn Active Carrier - With free SHIPPING!!
+ 0: Baby Bjorn Active Carrier - With free SHIPPING!!
+ 1: Baby Bjorn Active Carrier - With free SHIPPING!!
+     
+/a*.*b/SD
+------------------------------------------------------------------
+  0   9 Bra 0
+  3     a*
+  5     Any*
+  7     b
+  9   9 Ket
+ 12     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+No first char
+Need char = 'b'
+Study returned NULL
+
+/(a|b)*.?c/SD 
+------------------------------------------------------------------
+  0  21 Bra 0
+  3     Brazero
+  4   5 Bra 1
+  7     a
+  9   5 Alt
+ 12     b
+ 14  10 KetRmax
+ 17     Any?
+ 19     c
+ 21  21 Ket
+ 24     End
+------------------------------------------------------------------
+Capturing subpattern count = 1
+No options
+No first char
+Need char = 'c'
+Study returned NULL
+
+/abc(?C255)de(?C)f/D
+------------------------------------------------------------------
+  0  27 Bra 0
+  3     abc
+  9     Callout 255 10 1
+ 15     de
+ 19     Callout 0 16 1
+ 25     f
+ 27  27 Ket
+ 30     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+No options
+First char = 'a'
+Need char = 'f'
+
+/abcde/CD
+------------------------------------------------------------------
+  0  49 Bra 0
+  3     Callout 255 0 1
+  9     a
+ 11     Callout 255 1 1
+ 17     b
+ 19     Callout 255 2 1
+ 25     c
+ 27     Callout 255 3 1
+ 33     d
+ 35     Callout 255 4 1
+ 41     e
+ 43     Callout 255 5 0
+ 49  49 Ket
+ 52     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options:
+First char = 'a'
+Need char = 'e'
+  abcde
+--->abcde
+ +0 ^         a
+ +1 ^^        b
+ +2 ^ ^       c
+ +3 ^  ^      d
+ +4 ^   ^     e
+ +5 ^    ^    
+ 0: abcde
+  abcdfe 
+--->abcdfe
+ +0 ^          a
+ +1 ^^         b
+ +2 ^ ^        c
+ +3 ^  ^       d
+ +4 ^   ^      e
+No match
+  
+/a*b/CD
+------------------------------------------------------------------
+  0  25 Bra 0
+  3     Callout 255 0 2
+  9     a*
+ 11     Callout 255 2 1
+ 17     b
+ 19     Callout 255 3 0
+ 25  25 Ket
+ 28     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Partial matching not supported
+Options:
+No first char
+Need char = 'b'
+  ab
+--->ab
+ +0 ^      a*
+ +2 ^^     b
+ +3 ^ ^    
+ 0: ab
+  aaaab
+--->aaaab
+ +0 ^         a*
+ +2 ^   ^     b
+ +3 ^    ^    
+ 0: aaaab
+  aaaacb   
+--->aaaacb
+ +0 ^          a*
+ +2 ^   ^      b
+ +2 ^  ^       b
+ +2 ^ ^        b
+ +2 ^^         b
+ +2 ^          b
+ +0  ^         a*
+ +2  ^  ^      b
+ +2  ^ ^       b
+ +2  ^^        b
+ +2  ^         b
+ +0   ^        a*
+ +2   ^ ^      b
+ +2   ^^       b
+ +2   ^        b
+ +0    ^       a*
+ +2    ^^      b
+ +2    ^       b
+ +0     ^      a*
+ +2     ^      b
+ +0      ^     a*
+ +2      ^     b
+ +3      ^^    
+ 0: b
+
+/a+b/CD
+------------------------------------------------------------------
+  0  25 Bra 0
+  3     Callout 255 0 2
+  9     a+
+ 11     Callout 255 2 1
+ 17     b
+ 19     Callout 255 3 0
+ 25  25 Ket
+ 28     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Partial matching not supported
+Options:
+First char = 'a'
+Need char = 'b'
+  ab
+--->ab
+ +0 ^      a+
+ +2 ^^     b
+ +3 ^ ^    
+ 0: ab
+  aaaab
+--->aaaab
+ +0 ^         a+
+ +2 ^   ^     b
+ +3 ^    ^    
+ 0: aaaab
+  aaaacb   
+--->aaaacb
+ +0 ^          a+
+ +2 ^   ^      b
+ +2 ^  ^       b
+ +2 ^ ^        b
+ +2 ^^         b
+ +0  ^         a+
+ +2  ^  ^      b
+ +2  ^ ^       b
+ +2  ^^        b
+ +0   ^        a+
+ +2   ^ ^      b
+ +2   ^^       b
+ +0    ^       a+
+ +2    ^^      b
+No match
+
+/(abc|def)x/CD
+------------------------------------------------------------------
+  0  92 Bra 0
+  3     Callout 255 0 9
+  9  33 Bra 1
+ 12     Callout 255 1 1
+ 18     a
+ 20     Callout 255 2 1
+ 26     b
+ 28     Callout 255 3 1
+ 34     c
+ 36     Callout 255 4 0
+ 42  33 Alt
+ 45     Callout 255 5 1
+ 51     d
+ 53     Callout 255 6 1
+ 59     e
+ 61     Callout 255 7 1
+ 67     f
+ 69     Callout 255 8 0
+ 75  66 Ket
+ 78     Callout 255 9 1
+ 84     x
+ 86     Callout 255 10 0
+ 92  92 Ket
+ 95     End
+------------------------------------------------------------------
+Capturing subpattern count = 1
+Options:
+No first char
+Need char = 'x'
+  abcx
+--->abcx
+ +0 ^        (abc|def)
+ +1 ^        a
+ +2 ^^       b
+ +3 ^ ^      c
+ +4 ^  ^     |
+ +9 ^  ^     x
++10 ^   ^    
+ 0: abcx
+ 1: abc
+  defx
+--->defx
+ +0 ^        (abc|def)
+ +1 ^        a
+ +5 ^        d
+ +6 ^^       e
+ +7 ^ ^      f
+ +8 ^  ^     )
+ +9 ^  ^     x
++10 ^   ^    
+ 0: defx
+ 1: def
+  abcdefzx
+--->abcdefzx
+ +0 ^            (abc|def)
+ +1 ^            a
+ +2 ^^           b
+ +3 ^ ^          c
+ +4 ^  ^         |
+ +9 ^  ^         x
+ +5 ^            d
+ +0  ^           (abc|def)
+ +1  ^           a
+ +5  ^           d
+ +0   ^          (abc|def)
+ +1   ^          a
+ +5   ^          d
+ +0    ^         (abc|def)
+ +1    ^         a
+ +5    ^         d
+ +6    ^^        e
+ +7    ^ ^       f
+ +8    ^  ^      )
+ +9    ^  ^      x
+ +0     ^        (abc|def)
+ +1     ^        a
+ +5     ^        d
+ +0      ^       (abc|def)
+ +1      ^       a
+ +5      ^       d
+ +0       ^      (abc|def)
+ +1       ^      a
+ +5       ^      d
+ +0        ^     (abc|def)
+ +1        ^     a
+ +5        ^     d
+No match
+
+/(ab|cd){3,4}/C
+Capturing subpattern count = 1
+Options:
+No first char
+No need char
+  ababab
+--->ababab
+ +0 ^          (ab|cd){3,4}
+ +1 ^          a
+ +2 ^^         b
+ +3 ^ ^        |
+ +1 ^ ^        a
+ +2 ^  ^       b
+ +3 ^   ^      |
+ +1 ^   ^      a
+ +2 ^    ^     b
+ +3 ^     ^    |
+ +1 ^     ^    a
+ +4 ^     ^    c
++12 ^     ^    
+ 0: ababab
+ 1: ab
+  abcdabcd
+--->abcdabcd
+ +0 ^            (ab|cd){3,4}
+ +1 ^            a
+ +2 ^^           b
+ +3 ^ ^          |
+ +1 ^ ^          a
+ +4 ^ ^          c
+ +5 ^  ^         d
+ +6 ^   ^        )
+ +1 ^   ^        a
+ +2 ^    ^       b
+ +3 ^     ^      |
+ +1 ^     ^      a
+ +4 ^     ^      c
+ +5 ^      ^     d
+ +6 ^       ^    )
++12 ^       ^    
+ 0: abcdabcd
+ 1: cd
+  abcdcdcdcdcd  
+--->abcdcdcdcdcd
+ +0 ^                (ab|cd){3,4}
+ +1 ^                a
+ +2 ^^               b
+ +3 ^ ^              |
+ +1 ^ ^              a
+ +4 ^ ^              c
+ +5 ^  ^             d
+ +6 ^   ^            )
+ +1 ^   ^            a
+ +4 ^   ^            c
+ +5 ^    ^           d
+ +6 ^     ^          )
+ +1 ^     ^          a
+ +4 ^     ^          c
+ +5 ^      ^         d
+ +6 ^       ^        )
++12 ^       ^        
+ 0: abcdcdcd
+ 1: cd
+
+/([ab]{,4}c|xy)/CD
+------------------------------------------------------------------
+  0 131 Bra 0
+  3     Callout 255 0 14
+  9  88 Bra 1
+ 12     Callout 255 1 4
+ 18     [ab]
+ 51     Callout 255 5 1
+ 57     {
+ 59     Callout 255 6 1
+ 65     ,
+ 67     Callout 255 7 1
+ 73     4
+ 75     Callout 255 8 1
+ 81     }
+ 83     Callout 255 9 1
+ 89     c
+ 91     Callout 255 10 0
+ 97  25 Alt
+100     Callout 255 11 1
+106     x
+108     Callout 255 12 1
+114     y
+116     Callout 255 13 0
+122 113 Ket
+125     Callout 255 14 0
+131 131 Ket
+134     End
+------------------------------------------------------------------
+Capturing subpattern count = 1
+Options:
+No first char
+No need char
+    Note: that { does NOT introduce a quantifier
+--->Note: that { does NOT introduce a quantifier
+ +0 ^                                                ([ab]{,4}c|xy)
+ +1 ^                                                [ab]
++11 ^                                                x
+ +0  ^                                               ([ab]{,4}c|xy)
+ +1  ^                                               [ab]
++11  ^                                               x
+ +0   ^                                              ([ab]{,4}c|xy)
+ +1   ^                                              [ab]
++11   ^                                              x
+ +0    ^                                             ([ab]{,4}c|xy)
+ +1    ^                                             [ab]
++11    ^                                             x
+ +0     ^                                            ([ab]{,4}c|xy)
+ +1     ^                                            [ab]
++11     ^                                            x
+ +0      ^                                           ([ab]{,4}c|xy)
+ +1      ^                                           [ab]
++11      ^                                           x
+ +0       ^                                          ([ab]{,4}c|xy)
+ +1       ^                                          [ab]
++11       ^                                          x
+ +0        ^                                         ([ab]{,4}c|xy)
+ +1        ^                                         [ab]
++11        ^                                         x
+ +0         ^                                        ([ab]{,4}c|xy)
+ +1         ^                                        [ab]
+ +5         ^^                                       {
++11         ^                                        x
+ +0          ^                                       ([ab]{,4}c|xy)
+ +1          ^                                       [ab]
++11          ^                                       x
+ +0           ^                                      ([ab]{,4}c|xy)
+ +1           ^                                      [ab]
++11           ^                                      x
+ +0            ^                                     ([ab]{,4}c|xy)
+ +1            ^                                     [ab]
++11            ^                                     x
+ +0             ^                                    ([ab]{,4}c|xy)
+ +1             ^                                    [ab]
++11             ^                                    x
+ +0              ^                                   ([ab]{,4}c|xy)
+ +1              ^                                   [ab]
++11              ^                                   x
+ +0               ^                                  ([ab]{,4}c|xy)
+ +1               ^                                  [ab]
++11               ^                                  x
+ +0                ^                                 ([ab]{,4}c|xy)
+ +1                ^                                 [ab]
++11                ^                                 x
+ +0                 ^                                ([ab]{,4}c|xy)
+ +1                 ^                                [ab]
++11                 ^                                x
+ +0                  ^                               ([ab]{,4}c|xy)
+ +1                  ^                               [ab]
++11                  ^                               x
+ +0                   ^                              ([ab]{,4}c|xy)
+ +1                   ^                              [ab]
++11                   ^                              x
+ +0                    ^                             ([ab]{,4}c|xy)
+ +1                    ^                             [ab]
++11                    ^                             x
+ +0                     ^                            ([ab]{,4}c|xy)
+ +1                     ^                            [ab]
++11                     ^                            x
+ +0                      ^                           ([ab]{,4}c|xy)
+ +1                      ^                           [ab]
++11                      ^                           x
+ +0                       ^                          ([ab]{,4}c|xy)
+ +1                       ^                          [ab]
++11                       ^                          x
+ +0                        ^                         ([ab]{,4}c|xy)
+ +1                        ^                         [ab]
++11                        ^                         x
+ +0                         ^                        ([ab]{,4}c|xy)
+ +1                         ^                        [ab]
++11                         ^                        x
+ +0                          ^                       ([ab]{,4}c|xy)
+ +1                          ^                       [ab]
++11                          ^                       x
+ +0                           ^                      ([ab]{,4}c|xy)
+ +1                           ^                      [ab]
++11                           ^                      x
+ +0                            ^                     ([ab]{,4}c|xy)
+ +1                            ^                     [ab]
++11                            ^                     x
+ +0                             ^                    ([ab]{,4}c|xy)
+ +1                             ^                    [ab]
++11                             ^                    x
+ +0                              ^                   ([ab]{,4}c|xy)
+ +1                              ^                   [ab]
++11                              ^                   x
+ +0                               ^                  ([ab]{,4}c|xy)
+ +1                               ^                  [ab]
++11                               ^                  x
+ +0                                ^                 ([ab]{,4}c|xy)
+ +1                                ^                 [ab]
++11                                ^                 x
+ +0                                 ^                ([ab]{,4}c|xy)
+ +1                                 ^                [ab]
+ +5                                 ^^               {
++11                                 ^                x
+ +0                                  ^               ([ab]{,4}c|xy)
+ +1                                  ^               [ab]
++11                                  ^               x
+ +0                                   ^              ([ab]{,4}c|xy)
+ +1                                   ^              [ab]
++11                                   ^              x
+ +0                                    ^             ([ab]{,4}c|xy)
+ +1                                    ^             [ab]
++11                                    ^             x
+ +0                                     ^            ([ab]{,4}c|xy)
+ +1                                     ^            [ab]
+ +5                                     ^^           {
++11                                     ^            x
+ +0                                      ^           ([ab]{,4}c|xy)
+ +1                                      ^           [ab]
++11                                      ^           x
+ +0                                       ^          ([ab]{,4}c|xy)
+ +1                                       ^          [ab]
++11                                       ^          x
+ +0                                        ^         ([ab]{,4}c|xy)
+ +1                                        ^         [ab]
++11                                        ^         x
+ +0                                         ^        ([ab]{,4}c|xy)
+ +1                                         ^        [ab]
++11                                         ^        x
+ +0                                          ^       ([ab]{,4}c|xy)
+ +1                                          ^       [ab]
++11                                          ^       x
+ +0                                           ^      ([ab]{,4}c|xy)
+ +1                                           ^      [ab]
++11                                           ^      x
+ +0                                            ^     ([ab]{,4}c|xy)
+ +1                                            ^     [ab]
++11                                            ^     x
+ +0                                             ^    ([ab]{,4}c|xy)
+ +1                                             ^    [ab]
++11                                             ^    x
+No match
+
+/([ab]{1,4}c|xy){4,5}?123/CD
+------------------------------------------------------------------
+  0 485 Bra 0
+  3     Callout 255 0 21
+  9  61 Bra 1
+ 12     Callout 255 1 9
+ 18     [ab]{1,4}
+ 56     Callout 255 10 1
+ 62     c
+ 64     Callout 255 11 0
+ 70  25 Alt
+ 73     Callout 255 12 1
+ 79     x
+ 81     Callout 255 13 1
+ 87     y
+ 89     Callout 255 14 0
+ 95  86 Ket
+ 98  61 Bra 1
+101     Callout 255 1 9
+107     [ab]{1,4}
+145     Callout 255 10 1
+151     c
+153     Callout 255 11 0
+159  25 Alt
+162     Callout 255 12 1
+168     x
+170     Callout 255 13 1
+176     y
+178     Callout 255 14 0
+184  86 Ket
+187  61 Bra 1
+190     Callout 255 1 9
+196     [ab]{1,4}
+234     Callout 255 10 1
+240     c
+242     Callout 255 11 0
+248  25 Alt
+251     Callout 255 12 1
+257     x
+259     Callout 255 13 1
+265     y
+267     Callout 255 14 0
+273  86 Ket
+276  61 Bra 1
+279     Callout 255 1 9
+285     [ab]{1,4}
+323     Callout 255 10 1
+329     c
+331     Callout 255 11 0
+337  25 Alt
+340     Callout 255 12 1
+346     x
+348     Callout 255 13 1
+354     y
+356     Callout 255 14 0
+362  86 Ket
+365     Braminzero
+366  61 Bra 1
+369     Callout 255 1 9
+375     [ab]{1,4}
+413     Callout 255 10 1
+419     c
+421     Callout 255 11 0
+427  25 Alt
+430     Callout 255 12 1
+436     x
+438     Callout 255 13 1
+444     y
+446     Callout 255 14 0
+452  86 Ket
+455     Callout 255 21 1
+461     1
+463     Callout 255 22 1
+469     2
+471     Callout 255 23 1
+477     3
+479     Callout 255 24 0
+485 485 Ket
+488     End
+------------------------------------------------------------------
+Capturing subpattern count = 1
+Partial matching not supported
+Options:
+No first char
+Need char = '3'
+    aacaacaacaacaac123
+--->aacaacaacaacaac123
+ +0 ^                      ([ab]{1,4}c|xy){4,5}?
+ +1 ^                      [ab]{1,4}
++10 ^ ^                    c
++11 ^  ^                   |
+ +1 ^  ^                   [ab]{1,4}
++10 ^    ^                 c
++11 ^     ^                |
+ +1 ^     ^                [ab]{1,4}
++10 ^       ^              c
++11 ^        ^             |
+ +1 ^        ^             [ab]{1,4}
++10 ^          ^           c
++11 ^           ^          |
++21 ^           ^          1
+ +1 ^           ^          [ab]{1,4}
++10 ^             ^        c
++11 ^              ^       |
++21 ^              ^       1
++22 ^               ^      2
++23 ^                ^     3
++24 ^                 ^    
+ 0: aacaacaacaacaac123
+ 1: aac
+
+/\b.*/I
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+No first char
+No need char
+  ab cd\>1
+ 0:  cd
+  
+/\b.*/Is 
+Capturing subpattern count = 0
+Partial matching not supported
+Options: dotall
+No first char
+No need char
+  ab cd\>1
+ 0:  cd
+  
+/(?!.bcd).*/I
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+No first char
+No need char
+  Xbcd12345 
+ 0: bcd12345
+
+/abcde/
+Capturing subpattern count = 0
+No options
+First char = 'a'
+Need char = 'e'
+    ab\P
+Partial match
+    abc\P
+Partial match
+    abcd\P
+Partial match
+    abcde\P   
+ 0: abcde
+    the quick brown abc\P
+Partial match
+    ** Failers\P
+No match
+    the quick brown abxyz fox\P
+No match
+    
+"^(0?[1-9]|[12][0-9]|3[01])/(0?[1-9]|1[012])/(20)?\d\d$"
+Capturing subpattern count = 3
+Options: anchored
+No first char
+Need char = '/'
+    13/05/04\P
+ 0: 13/05/04
+ 1: 13
+ 2: 05
+    13/5/2004\P
+ 0: 13/5/2004
+ 1: 13
+ 2: 5
+ 3: 20
+    02/05/09\P 
+ 0: 02/05/09
+ 1: 02
+ 2: 05
+    1\P
+Partial match
+    1/2\P
+Partial match
+    1/2/0\P
+Partial match
+    1/2/04\P    
+ 0: 1/2/04
+ 1: 1
+ 2: 2
+    0\P
+Partial match
+    02/\P
+Partial match
+    02/0\P   
+Partial match
+    02/1\P
+Partial match
+    ** Failers\P
+No match
+    \P
+No match
+    123\P
+No match
+    33/4/04\P
+No match
+    3/13/04\P
+No match
+    0/1/2003\P
+No match
+    0/\P 
+No match
+    02/0/\P 
+No match
+    02/13\P  
+No match
+
+/0{0,2}ABC/I
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+No first char
+Need char = 'C'
+    
+/\d{3,}ABC/I
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+No first char
+Need char = 'C'
+    
+/\d*ABC/I
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+No first char
+Need char = 'C'
+
+/[abc]+DE/I
+Capturing subpattern count = 0
+Partial matching not supported
+No options
+No first char
+Need char = 'E'
+
+/[abc]?123/
+Capturing subpattern count = 0
+No options
+No first char
+Need char = '3'
+    123\P
+ 0: 123
+    a\P
+Partial match
+    b\P
+Partial match
+    c\P
+Partial match
+    c12\P
+Partial match
+    c123\P      
+ 0: c123
+
+/^(?:\d){3,5}X/
+Capturing subpattern count = 0
+Options: anchored
+No first char
+Need char = 'X'
+    1\P
+Partial match
+    123\P
+Partial match
+    123X
+ 0: 123X
+    1234\P
+Partial match
+    1234X
+ 0: 1234X
+    12345\P
+Partial match
+    12345X      
+ 0: 12345X
+    *** Failers 
+No match
+    1X 
+No match
+    123456\P 
+No match
+
+/abc/>testsavedregex
+Capturing subpattern count = 0
+No options
+First char = 'a'
+Need char = 'c'
+Compiled regex written to testsavedregex
+<testsavedregex
+Compiled regex loaded from testsavedregex
+No study data
+Capturing subpattern count = 0
+No options
+First char = 'a'
+Need char = 'c'
+    abc
+ 0: abc
+    ** Failers
+No match
+    bca
+No match
+    
+/abc/F>testsavedregex
+Capturing subpattern count = 0
+No options
+First char = 'a'
+Need char = 'c'
+Compiled regex written to testsavedregex
+<testsavedregex
+Compiled regex (byte-inverted) loaded from testsavedregex
+No study data
+Capturing subpattern count = 0
+No options
+First char = 'a'
+Need char = 'c'
+    abc
+ 0: abc
+    ** Failers
+No match
+    bca
+No match
+
+/(a|b)/S>testsavedregex
+Capturing subpattern count = 1
+No options
+No first char
+No need char
+Starting byte set: a b 
+Compiled regex written to testsavedregex
+Study data written to testsavedregex
+<testsavedregex
+Compiled regex loaded from testsavedregex
+Study data loaded from testsavedregex
+Capturing subpattern count = 1
+No options
+No first char
+No need char
+Starting byte set: a b 
+    abc
+ 0: a
+ 1: a
+    ** Failers
+ 0: a
+ 1: a
+    def  
+No match
+    
+/(a|b)/SF>testsavedregex
+Capturing subpattern count = 1
+No options
+No first char
+No need char
+Starting byte set: a b 
+Compiled regex written to testsavedregex
+Study data written to testsavedregex
+<testsavedregex
+Compiled regex (byte-inverted) loaded from testsavedregex
+Study data loaded from testsavedregex
+Capturing subpattern count = 1
+No options
+No first char
+No need char
+Starting byte set: a b 
+    abc
+ 0: a
+ 1: a
+    ** Failers
+ 0: a
+ 1: a
+    def  
+No match
+    
+~<(\w+)/?>(.)*</(\1)>~smg
+Capturing subpattern count = 3
+Max back reference = 1
+Partial matching not supported
+Options: multiline dotall
+First char = '<'
+Need char = '>'
+    <!DOCTYPE seite SYSTEM "http://www.lco.lineas.de/xmlCms.dtd">\n<seite>\n<dokumenteninformation>\n<seitentitel>Partner der LCO</seitentitel>\n<sprache>de</sprache>\n<seitenbeschreibung>Partner der LINEAS Consulting\nGmbH</seitenbeschreibung>\n<schluesselworte>LINEAS Consulting GmbH Hamburg\nPartnerfirmen</schluesselworte>\n<revisit>30 days</revisit>\n<robots>index,follow</robots>\n<menueinformation>\n<aktiv>ja</aktiv>\n<menueposition>3</menueposition>\n<menuetext>Partner</menuetext>\n</menueinformation>\n<lastedited>\n<autor>LCO</autor>\n<firma>LINEAS Consulting</firma>\n<datum>15.10.2003</datum>\n</lastedited>\n</dokumenteninformation>\n<inhalt>\n\n<absatzueberschrift>Die Partnerfirmen der LINEAS Consulting\nGmbH</absatzueberschrift>\n\n<absatz><link ziel="http://www.ca.com/" zielfenster="_blank">\n<bild name="logo_ca.gif" rahmen="no"/></link> <link\nziel="http://www.ey.com/" zielfenster="_blank"><bild\nname="logo_euy.gif" rahmen="no"/></link>\n</absatz>\n\n<absatz><link ziel="http://www.cisco.de/" zielfenster="_blank">\n<bild name="logo_cisco.gif" rahmen="ja"/></link></absatz>\n\n<absatz><link ziel="http://www.atelion.de/"\nzielfenster="_blank"><bild\nname="logo_atelion.gif" rahmen="no"/></link>\n</absatz>\n\n<absatz><link ziel="http://www.line-information.de/"\nzielfenster="_blank">\n<bild name="logo_line_information.gif" rahmen="no"/></link>\n</absatz>\n\n<absatz><bild name="logo_aw.gif" rahmen="no"/></absatz>\n\n<absatz><link ziel="http://www.incognis.de/"\nzielfenster="_blank"><bild\nname="logo_incognis.gif" rahmen="no"/></link></absatz>\n\n<absatz><link ziel="http://www.addcraft.com/"\nzielfenster="_blank"><bild\nname="logo_addcraft.gif" rahmen="no"/></link></absatz>\n\n<absatz><link ziel="http://www.comendo.com/"\nzielfenster="_blank"><bild\nname="logo_comendo.gif" rahmen="no"/></link></absatz>\n\n</inhalt>\n</seite>
+ 0: <seite>\x0a<dokumenteninformation>\x0a<seitentitel>Partner der LCO</seitentitel>\x0a<sprache>de</sprache>\x0a<seitenbeschreibung>Partner der LINEAS Consulting\x0aGmbH</seitenbeschreibung>\x0a<schluesselworte>LINEAS Consulting GmbH Hamburg\x0aPartnerfirmen</schluesselworte>\x0a<revisit>30 days</revisit>\x0a<robots>index,follow</robots>\x0a<menueinformation>\x0a<aktiv>ja</aktiv>\x0a<menueposition>3</menueposition>\x0a<menuetext>Partner</menuetext>\x0a</menueinformation>\x0a<lastedited>\x0a<autor>LCO</autor>\x0a<firma>LINEAS Consulting</firma>\x0a<datum>15.10.2003</datum>\x0a</lastedited>\x0a</dokumenteninformation>\x0a<inhalt>\x0a\x0a<absatzueberschrift>Die Partnerfirmen der LINEAS Consulting\x0aGmbH</absatzueberschrift>\x0a\x0a<absatz><link ziel="http://www.ca.com/" zielfenster="_blank">\x0a<bild name="logo_ca.gif" rahmen="no"/></link> <link\x0aziel="http://www.ey.com/" zielfenster="_blank"><bild\x0aname="logo_euy.gif" rahmen="no"/></link>\x0a</absatz>\x0a\x0a<absatz><link ziel="http://www.cisco.de/" zielfenster="_blank">\x0a<bild name="logo_cisco.gif" rahmen="ja"/></link></absatz>\x0a\x0a<absatz><link ziel="http://www.atelion.de/"\x0azielfenster="_blank"><bild\x0aname="logo_atelion.gif" rahmen="no"/></link>\x0a</absatz>\x0a\x0a<absatz><link ziel="http://www.line-information.de/"\x0azielfenster="_blank">\x0a<bild name="logo_line_information.gif" rahmen="no"/></link>\x0a</absatz>\x0a\x0a<absatz><bild name="logo_aw.gif" rahmen="no"/></absatz>\x0a\x0a<absatz><link ziel="http://www.incognis.de/"\x0azielfenster="_blank"><bild\x0aname="logo_incognis.gif" rahmen="no"/></link></absatz>\x0a\x0a<absatz><link ziel="http://www.addcraft.com/"\x0azielfenster="_blank"><bild\x0aname="logo_addcraft.gif" rahmen="no"/></link></absatz>\x0a\x0a<absatz><link ziel="http://www.comendo.com/"\x0azielfenster="_blank"><bild\x0aname="logo_comendo.gif" rahmen="no"/></link></absatz>\x0a\x0a</inhalt>\x0a</seite>
+ 1: seite
+ 2: \x0a
+ 3: seite
+
+/^a/IF
+Capturing subpattern count = 0
+Options: anchored
+No first char
+No need char
+
+/ End of testinput2 /
+Capturing subpattern count = 0
+No options
+First char = ' '
+Need char = ' '
diff --git a/connectors/jk/native/iis/pcre/testdata/testoutput3 b/connectors/jk/native/iis/pcre/testdata/testoutput3
new file mode 100644
index 0000000..225bd5b
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/testdata/testoutput3
@@ -0,0 +1,115 @@
+PCRE version 5.0 13-Sep-2004
+
+/^[\w]+/
+    *** Failers
+No match
+    École
+No match
+
+/^[\w]+/Lfr_FR
+    École
+ 0: École
+
+/^[\w]+/
+    *** Failers
+No match
+    École
+No match
+
+/^[\W]+/
+    École
+ 0: \xc9
+
+/^[\W]+/Lfr_FR
+    *** Failers
+ 0: *** 
+    École
+No match
+
+/[\b]/
+    \b
+ 0: \x08
+    *** Failers
+No match
+    a
+No match
+
+/[\b]/Lfr_FR
+    \b
+ 0: \x08
+    *** Failers
+No match
+    a
+No match
+
+/^\w+/
+    *** Failers
+No match
+    École
+No match
+
+/^\w+/Lfr_FR
+    École
+ 0: École
+
+/(.+)\b(.+)/
+    École
+ 0: \xc9cole
+ 1: \xc9
+ 2: cole
+
+/(.+)\b(.+)/Lfr_FR
+    *** Failers
+ 0: *** Failers
+ 1: *** 
+ 2: Failers
+    École
+No match
+
+/École/i
+    École
+ 0: \xc9cole
+    *** Failers
+No match
+    école
+No match
+
+/École/iLfr_FR
+    École
+ 0: École
+    école
+ 0: école
+
+/\w/IS
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+Starting byte set: 0 1 2 3 4 5 6 7 8 9 A B C D E F G H I J K L M N O P 
+  Q R S T U V W X Y Z _ a b c d e f g h i j k l m n o p q r s t u v w x y z 
+
+/\w/ISLfr_FR
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+Starting byte set: 0 1 2 3 4 5 6 7 8 9 A B C D E F G H I J K L M N O P 
+  Q R S T U V W X Y Z _ a b c d e f g h i j k l m n o p q r s t u v w x y z 
+  µ À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö Ø Ù Ú Û Ü Ý Þ ß à á â ã ä 
+  å æ ç è é ê ë ì í î ï ð ñ ò ó ô õ ö ø ù ú û ü ý þ ÿ 
+
+/^[\xc8-\xc9]/iLfr_FR
+    École
+ 0: É
+    école
+ 0: é
+
+/^[\xc8-\xc9]/Lfr_FR
+    École
+ 0: É
+    *** Failers 
+No match
+    école
+No match
+
+/ End of testinput3 /
diff --git a/connectors/jk/native/iis/pcre/testdata/testoutput4 b/connectors/jk/native/iis/pcre/testdata/testoutput4
new file mode 100644
index 0000000..e8d2603
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/testdata/testoutput4
@@ -0,0 +1,903 @@
+PCRE version 5.0 13-Sep-2004
+
+/-- Do not use the \x{} construct except with patterns that have the --/
+/-- /8 option set, because PCRE doesn't recognize them as UTF-8 unless --/
+No match
+/-- that option is set. However, the latest Perls recognize them always. --/
+No match
+
+/a.b/8
+    acb
+ 0: acb
+    a\x7fb
+ 0: a\x{7f}b
+    a\x{100}b 
+ 0: a\x{100}b
+    *** Failers
+No match
+    a\nb  
+No match
+
+/a(.{3})b/8
+    a\x{4000}xyb 
+ 0: a\x{4000}xyb
+ 1: \x{4000}xy
+    a\x{4000}\x7fyb 
+ 0: a\x{4000}\x{7f}yb
+ 1: \x{4000}\x{7f}y
+    a\x{4000}\x{100}yb 
+ 0: a\x{4000}\x{100}yb
+ 1: \x{4000}\x{100}y
+    *** Failers
+No match
+    a\x{4000}b 
+No match
+    ac\ncb 
+No match
+
+/a(.*?)(.)/
+    a\xc0\x88b
+ 0: a\xc0
+ 1: 
+ 2: \xc0
+
+/a(.*?)(.)/8
+    a\x{100}b
+ 0: a\x{100}
+ 1: 
+ 2: \x{100}
+
+/a(.*)(.)/
+    a\xc0\x88b
+ 0: a\xc0\x88b
+ 1: \xc0\x88
+ 2: b
+
+/a(.*)(.)/8
+    a\x{100}b
+ 0: a\x{100}b
+ 1: \x{100}
+ 2: b
+
+/a(.)(.)/
+    a\xc0\x92bcd
+ 0: a\xc0\x92
+ 1: \xc0
+ 2: \x92
+
+/a(.)(.)/8
+    a\x{240}bcd
+ 0: a\x{240}b
+ 1: \x{240}
+ 2: b
+
+/a(.?)(.)/
+    a\xc0\x92bcd
+ 0: a\xc0\x92
+ 1: \xc0
+ 2: \x92
+
+/a(.?)(.)/8
+    a\x{240}bcd
+ 0: a\x{240}b
+ 1: \x{240}
+ 2: b
+
+/a(.??)(.)/
+    a\xc0\x92bcd
+ 0: a\xc0
+ 1: 
+ 2: \xc0
+
+/a(.??)(.)/8
+    a\x{240}bcd
+ 0: a\x{240}
+ 1: 
+ 2: \x{240}
+
+/a(.{3})b/8
+    a\x{1234}xyb 
+ 0: a\x{1234}xyb
+ 1: \x{1234}xy
+    a\x{1234}\x{4321}yb 
+ 0: a\x{1234}\x{4321}yb
+ 1: \x{1234}\x{4321}y
+    a\x{1234}\x{4321}\x{3412}b 
+ 0: a\x{1234}\x{4321}\x{3412}b
+ 1: \x{1234}\x{4321}\x{3412}
+    *** Failers
+No match
+    a\x{1234}b 
+No match
+    ac\ncb 
+No match
+
+/a(.{3,})b/8
+    a\x{1234}xyb 
+ 0: a\x{1234}xyb
+ 1: \x{1234}xy
+    a\x{1234}\x{4321}yb 
+ 0: a\x{1234}\x{4321}yb
+ 1: \x{1234}\x{4321}y
+    a\x{1234}\x{4321}\x{3412}b 
+ 0: a\x{1234}\x{4321}\x{3412}b
+ 1: \x{1234}\x{4321}\x{3412}
+    axxxxbcdefghijb 
+ 0: axxxxbcdefghijb
+ 1: xxxxbcdefghij
+    a\x{1234}\x{4321}\x{3412}\x{3421}b 
+ 0: a\x{1234}\x{4321}\x{3412}\x{3421}b
+ 1: \x{1234}\x{4321}\x{3412}\x{3421}
+    *** Failers
+No match
+    a\x{1234}b 
+No match
+
+/a(.{3,}?)b/8
+    a\x{1234}xyb 
+ 0: a\x{1234}xyb
+ 1: \x{1234}xy
+    a\x{1234}\x{4321}yb 
+ 0: a\x{1234}\x{4321}yb
+ 1: \x{1234}\x{4321}y
+    a\x{1234}\x{4321}\x{3412}b 
+ 0: a\x{1234}\x{4321}\x{3412}b
+ 1: \x{1234}\x{4321}\x{3412}
+    axxxxbcdefghijb 
+ 0: axxxxb
+ 1: xxxx
+    a\x{1234}\x{4321}\x{3412}\x{3421}b 
+ 0: a\x{1234}\x{4321}\x{3412}\x{3421}b
+ 1: \x{1234}\x{4321}\x{3412}\x{3421}
+    *** Failers
+No match
+    a\x{1234}b 
+No match
+
+/a(.{3,5})b/8
+    a\x{1234}xyb 
+ 0: a\x{1234}xyb
+ 1: \x{1234}xy
+    a\x{1234}\x{4321}yb 
+ 0: a\x{1234}\x{4321}yb
+ 1: \x{1234}\x{4321}y
+    a\x{1234}\x{4321}\x{3412}b 
+ 0: a\x{1234}\x{4321}\x{3412}b
+ 1: \x{1234}\x{4321}\x{3412}
+    axxxxbcdefghijb 
+ 0: axxxxb
+ 1: xxxx
+    a\x{1234}\x{4321}\x{3412}\x{3421}b 
+ 0: a\x{1234}\x{4321}\x{3412}\x{3421}b
+ 1: \x{1234}\x{4321}\x{3412}\x{3421}
+    axbxxbcdefghijb 
+ 0: axbxxb
+ 1: xbxx
+    axxxxxbcdefghijb 
+ 0: axxxxxb
+ 1: xxxxx
+    *** Failers
+No match
+    a\x{1234}b 
+No match
+    axxxxxxbcdefghijb 
+No match
+
+/a(.{3,5}?)b/8
+    a\x{1234}xyb 
+ 0: a\x{1234}xyb
+ 1: \x{1234}xy
+    a\x{1234}\x{4321}yb 
+ 0: a\x{1234}\x{4321}yb
+ 1: \x{1234}\x{4321}y
+    a\x{1234}\x{4321}\x{3412}b 
+ 0: a\x{1234}\x{4321}\x{3412}b
+ 1: \x{1234}\x{4321}\x{3412}
+    axxxxbcdefghijb 
+ 0: axxxxb
+ 1: xxxx
+    a\x{1234}\x{4321}\x{3412}\x{3421}b 
+ 0: a\x{1234}\x{4321}\x{3412}\x{3421}b
+ 1: \x{1234}\x{4321}\x{3412}\x{3421}
+    axbxxbcdefghijb 
+ 0: axbxxb
+ 1: xbxx
+    axxxxxbcdefghijb 
+ 0: axxxxxb
+ 1: xxxxx
+    *** Failers
+No match
+    a\x{1234}b 
+No match
+    axxxxxxbcdefghijb 
+No match
+
+/^[a\x{c0}]/8
+    *** Failers
+No match
+    \x{100}
+No match
+
+/(?<=aXb)cd/8
+    aXbcd
+ 0: cd
+
+/(?<=a\x{100}b)cd/8
+    a\x{100}bcd
+ 0: cd
+
+/(?<=a\x{100000}b)cd/8
+    a\x{100000}bcd
+ 0: cd
+    
+/(?:\x{100}){3}b/8
+    \x{100}\x{100}\x{100}b
+ 0: \x{100}\x{100}\x{100}b
+    *** Failers 
+No match
+    \x{100}\x{100}b
+No match
+
+/\x{ab}/8
+    \x{ab} 
+ 0: \x{ab}
+    \xc2\xab
+ 0: \x{ab}
+    *** Failers 
+No match
+    \x00{ab}
+No match
+
+/(?<=(.))X/8
+    WXYZ
+ 0: X
+ 1: W
+    \x{256}XYZ 
+ 0: X
+ 1: \x{256}
+    *** Failers
+No match
+    XYZ 
+No match
+
+/X(\C{3})/8
+    X\x{1234}
+ 0: X\x{1234}
+ 1: \x{1234}
+
+/X(\C{4})/8
+    X\x{1234}YZ
+ 0: X\x{1234}Y
+ 1: \x{1234}Y
+    
+/X\C*/8
+    XYZabcdce
+ 0: XYZabcdce
+    
+/X\C*?/8
+    XYZabcde
+ 0: X
+    
+/X\C{3,5}/8
+    Xabcdefg   
+ 0: Xabcde
+    X\x{1234} 
+ 0: X\x{1234}
+    X\x{1234}YZ
+ 0: X\x{1234}YZ
+    X\x{1234}\x{512}  
+ 0: X\x{1234}\x{512}
+    X\x{1234}\x{512}YZ
+ 0: X\x{1234}\x{512}
+
+/X\C{3,5}?/8
+    Xabcdefg   
+ 0: Xabc
+    X\x{1234} 
+ 0: X\x{1234}
+    X\x{1234}YZ
+ 0: X\x{1234}
+    X\x{1234}\x{512}  
+ 0: X\x{1234}
+
+/[^a]+/8g
+    bcd
+ 0: bcd
+    \x{100}aY\x{256}Z 
+ 0: \x{100}
+ 0: Y\x{256}Z
+    
+/^[^a]{2}/8
+    \x{100}bc
+ 0: \x{100}b
+ 
+/^[^a]{2,}/8
+    \x{100}bcAa
+ 0: \x{100}bcA
+
+/^[^a]{2,}?/8
+    \x{100}bca
+ 0: \x{100}b
+
+/[^a]+/8ig
+    bcd
+ 0: bcd
+    \x{100}aY\x{256}Z 
+ 0: \x{100}
+ 0: Y\x{256}Z
+    
+/^[^a]{2}/8i
+    \x{100}bc
+ 0: \x{100}b
+ 
+/^[^a]{2,}/8i
+    \x{100}bcAa
+ 0: \x{100}bc
+
+/^[^a]{2,}?/8i
+    \x{100}bca
+ 0: \x{100}b
+
+/\x{100}{0,0}/8
+    abcd
+ 0: 
+ 
+/\x{100}?/8
+    abcd
+ 0: 
+    \x{100}\x{100} 
+ 0: \x{100}
+
+/\x{100}{0,3}/8 
+    \x{100}\x{100} 
+ 0: \x{100}\x{100}
+    \x{100}\x{100}\x{100}\x{100} 
+ 0: \x{100}\x{100}\x{100}
+    
+/\x{100}*/8
+    abce
+ 0: 
+    \x{100}\x{100}\x{100}\x{100} 
+ 0: \x{100}\x{100}\x{100}\x{100}
+
+/\x{100}{1,1}/8
+    abcd\x{100}\x{100}\x{100}\x{100} 
+ 0: \x{100}
+
+/\x{100}{1,3}/8
+    abcd\x{100}\x{100}\x{100}\x{100} 
+ 0: \x{100}\x{100}\x{100}
+
+/\x{100}+/8
+    abcd\x{100}\x{100}\x{100}\x{100} 
+ 0: \x{100}\x{100}\x{100}\x{100}
+
+/\x{100}{3}/8
+    abcd\x{100}\x{100}\x{100}XX
+ 0: \x{100}\x{100}\x{100}
+
+/\x{100}{3,5}/8
+    abcd\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}XX
+ 0: \x{100}\x{100}\x{100}\x{100}\x{100}
+
+/\x{100}{3,}/8
+    abcd\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}XX
+ 0: \x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}
+
+/(?<=a\x{100}{2}b)X/8+
+    Xyyya\x{100}\x{100}bXzzz
+ 0: X
+ 0+ zzz
+
+/\D*/8
+  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+ 0: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+
+/\D*/8
+  \x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}
+ 0: \x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}\x{100}
+
+/\D/8
+    1X2
+ 0: X
+    1\x{100}2 
+ 0: \x{100}
+  
+/>\S/8
+    > >X Y
+ 0: >X
+    > >\x{100} Y
+ 0: >\x{100}
+  
+/\d/8
+    \x{100}3
+ 0: 3
+    
+/\s/8
+    \x{100} X
+ 0:  
+    
+/\D+/8
+    12abcd34
+ 0: abcd
+    *** Failers
+ 0: *** Failers
+    1234  
+No match
+
+/\D{2,3}/8
+    12abcd34
+ 0: abc
+    12ab34
+ 0: ab
+    *** Failers  
+ 0: ***
+    1234
+No match
+    12a34  
+No match
+
+/\D{2,3}?/8
+    12abcd34
+ 0: ab
+    12ab34
+ 0: ab
+    *** Failers  
+ 0: **
+    1234
+No match
+    12a34  
+No match
+
+/\d+/8
+    12abcd34
+ 0: 12
+    *** Failers
+No match
+
+/\d{2,3}/8
+    12abcd34
+ 0: 12
+    1234abcd
+ 0: 123
+    *** Failers  
+No match
+    1.4 
+No match
+
+/\d{2,3}?/8
+    12abcd34
+ 0: 12
+    1234abcd
+ 0: 12
+    *** Failers  
+No match
+    1.4 
+No match
+
+/\S+/8
+    12abcd34
+ 0: 12abcd34
+    *** Failers
+ 0: ***
+    \    \ 
+No match
+
+/\S{2,3}/8
+    12abcd34
+ 0: 12a
+    1234abcd
+ 0: 123
+    *** Failers
+ 0: ***
+    \     \  
+No match
+
+/\S{2,3}?/8
+    12abcd34
+ 0: 12
+    1234abcd
+ 0: 12
+    *** Failers
+ 0: **
+    \     \  
+No match
+
+/>\s+</8+
+    12>      <34
+ 0: >      <
+ 0+ 34
+    *** Failers
+No match
+
+/>\s{2,3}</8+
+    ab>  <cd
+ 0: >  <
+ 0+ cd
+    ab>   <ce
+ 0: >   <
+ 0+ ce
+    *** Failers
+No match
+    ab>    <cd 
+No match
+
+/>\s{2,3}?</8+
+    ab>  <cd
+ 0: >  <
+ 0+ cd
+    ab>   <ce
+ 0: >   <
+ 0+ ce
+    *** Failers
+No match
+    ab>    <cd 
+No match
+
+/\w+/8
+    12      34
+ 0: 12
+    *** Failers
+ 0: Failers
+    +++=*! 
+No match
+
+/\w{2,3}/8
+    ab  cd
+ 0: ab
+    abcd ce
+ 0: abc
+    *** Failers
+ 0: Fai
+    a.b.c
+No match
+
+/\w{2,3}?/8
+    ab  cd
+ 0: ab
+    abcd ce
+ 0: ab
+    *** Failers
+ 0: Fa
+    a.b.c
+No match
+
+/\W+/8
+    12====34
+ 0: ====
+    *** Failers
+ 0: *** 
+    abcd 
+No match
+
+/\W{2,3}/8
+    ab====cd
+ 0: ===
+    ab==cd
+ 0: ==
+    *** Failers
+ 0: ***
+    a.b.c
+No match
+
+/\W{2,3}?/8
+    ab====cd
+ 0: ==
+    ab==cd
+ 0: ==
+    *** Failers
+ 0: **
+    a.b.c
+No match
+
+/[\x{100}]/8
+    \x{100}
+ 0: \x{100}
+    Z\x{100}
+ 0: \x{100}
+    \x{100}Z
+ 0: \x{100}
+    *** Failers 
+No match
+
+/[Z\x{100}]/8
+    Z\x{100}
+ 0: Z
+    \x{100}
+ 0: \x{100}
+    \x{100}Z
+ 0: \x{100}
+    *** Failers 
+No match
+
+/[\x{100}\x{200}]/8
+   ab\x{100}cd
+ 0: \x{100}
+   ab\x{200}cd
+ 0: \x{200}
+   *** Failers  
+No match
+
+/[\x{100}-\x{200}]/8
+   ab\x{100}cd
+ 0: \x{100}
+   ab\x{200}cd
+ 0: \x{200}
+   ab\x{111}cd 
+ 0: \x{111}
+   *** Failers  
+No match
+
+/[z-\x{200}]/8
+   ab\x{100}cd
+ 0: \x{100}
+   ab\x{200}cd
+ 0: \x{200}
+   ab\x{111}cd 
+ 0: \x{111}
+   abzcd
+ 0: z
+   ab|cd  
+ 0: |
+   *** Failers  
+No match
+
+/[Q\x{100}\x{200}]/8
+   ab\x{100}cd
+ 0: \x{100}
+   ab\x{200}cd
+ 0: \x{200}
+   Q? 
+ 0: Q
+   *** Failers  
+No match
+
+/[Q\x{100}-\x{200}]/8
+   ab\x{100}cd
+ 0: \x{100}
+   ab\x{200}cd
+ 0: \x{200}
+   ab\x{111}cd 
+ 0: \x{111}
+   Q? 
+ 0: Q
+   *** Failers  
+No match
+
+/[Qz-\x{200}]/8
+   ab\x{100}cd
+ 0: \x{100}
+   ab\x{200}cd
+ 0: \x{200}
+   ab\x{111}cd 
+ 0: \x{111}
+   abzcd
+ 0: z
+   ab|cd  
+ 0: |
+   Q? 
+ 0: Q
+   *** Failers  
+No match
+
+/[\x{100}\x{200}]{1,3}/8
+   ab\x{100}cd
+ 0: \x{100}
+   ab\x{200}cd
+ 0: \x{200}
+   ab\x{200}\x{100}\x{200}\x{100}cd
+ 0: \x{200}\x{100}\x{200}
+   *** Failers  
+No match
+
+/[\x{100}\x{200}]{1,3}?/8
+   ab\x{100}cd
+ 0: \x{100}
+   ab\x{200}cd
+ 0: \x{200}
+   ab\x{200}\x{100}\x{200}\x{100}cd
+ 0: \x{200}
+   *** Failers  
+No match
+
+/[Q\x{100}\x{200}]{1,3}/8
+   ab\x{100}cd
+ 0: \x{100}
+   ab\x{200}cd
+ 0: \x{200}
+   ab\x{200}\x{100}\x{200}\x{100}cd
+ 0: \x{200}\x{100}\x{200}
+   *** Failers  
+No match
+
+/[Q\x{100}\x{200}]{1,3}?/8
+   ab\x{100}cd
+ 0: \x{100}
+   ab\x{200}cd
+ 0: \x{200}
+   ab\x{200}\x{100}\x{200}\x{100}cd
+ 0: \x{200}
+   *** Failers  
+No match
+
+/(?<=[\x{100}\x{200}])X/8
+    abc\x{200}X
+ 0: X
+    abc\x{100}X 
+ 0: X
+    *** Failers
+No match
+    X  
+No match
+
+/(?<=[Q\x{100}\x{200}])X/8
+    abc\x{200}X
+ 0: X
+    abc\x{100}X 
+ 0: X
+    abQX 
+ 0: X
+    *** Failers
+No match
+    X  
+No match
+
+/(?<=[\x{100}\x{200}]{3})X/8
+    abc\x{100}\x{200}\x{100}X
+ 0: X
+    *** Failers
+No match
+    abc\x{200}X
+No match
+    X  
+No match
+
+/[^\x{100}\x{200}]X/8
+    AX
+ 0: AX
+    \x{150}X
+ 0: \x{150}X
+    \x{500}X 
+ 0: \x{500}X
+    *** Failers
+No match
+    \x{100}X
+No match
+    \x{200}X   
+No match
+
+/[^Q\x{100}\x{200}]X/8
+    AX
+ 0: AX
+    \x{150}X
+ 0: \x{150}X
+    \x{500}X 
+ 0: \x{500}X
+    *** Failers
+No match
+    \x{100}X
+No match
+    \x{200}X   
+No match
+    QX 
+No match
+
+/[^\x{100}-\x{200}]X/8
+    AX
+ 0: AX
+    \x{500}X 
+ 0: \x{500}X
+    *** Failers
+No match
+    \x{100}X
+No match
+    \x{150}X
+No match
+    \x{200}X   
+No match
+
+/a\Cb/
+    aXb
+ 0: aXb
+    a\nb
+ 0: a\x0ab
+  
+/a\Cb/8
+    aXb
+ 0: aXb
+    a\nb
+ 0: a\x{0a}b
+    *** Failers 
+No match
+    a\x{100}b 
+No match
+
+/[z-\x{100}]/8i
+    z
+ 0: z
+    Z 
+ 0: Z
+    \x{100}
+ 0: \x{100}
+    *** Failers
+No match
+    \x{102}
+No match
+    y    
+No match
+
+/[\xFF]/
+    >\xff<
+ 0: \xff
+
+/[\xff]/8
+    >\x{ff}<
+ 0: \x{ff}
+
+/[^\xFF]/
+    XYZ
+ 0: X
+
+/[^\xff]/8
+    XYZ
+ 0: X
+    \x{123} 
+ 0: \x{123}
+
+/^[ac]*b/8
+  xb
+No match
+
+/^[ac\x{100}]*b/8
+  xb
+No match
+
+/^[^x]*b/8i
+  xb
+No match
+
+/^[^x]*b/8
+  xb
+No match
+  
+/^\d*b/8
+  xb 
+No match
+
+/(|a)/g8
+    catac
+ 0: 
+ 1: 
+ 0: 
+ 1: 
+ 0: a
+ 1: a
+ 0: 
+ 1: 
+ 0: 
+ 1: 
+ 0: a
+ 1: a
+ 0: 
+ 1: 
+ 0: 
+ 1: 
+    a\x{256}a 
+ 0: 
+ 1: 
+ 0: a
+ 1: a
+ 0: 
+ 1: 
+ 0: 
+ 1: 
+ 0: a
+ 1: a
+ 0: 
+ 1: 
+
+/^\x{85}$/8i
+    \x{85}
+ 0: \x{85}
+
+/ End of testinput4 /
diff --git a/connectors/jk/native/iis/pcre/testdata/testoutput5 b/connectors/jk/native/iis/pcre/testdata/testoutput5
new file mode 100644
index 0000000..b010957
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/testdata/testoutput5
@@ -0,0 +1,1075 @@
+PCRE version 5.0 13-Sep-2004
+
+/\x{100}/8DM
+Memory allocation (code space): 10
+------------------------------------------------------------------
+  0   6 Bra 0
+  3     \x{100}
+  6   6 Ket
+  9     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+First char = 196
+Need char = 128
+
+/\x{1000}/8DM
+Memory allocation (code space): 11
+------------------------------------------------------------------
+  0   7 Bra 0
+  3     \x{1000}
+  7   7 Ket
+ 10     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+First char = 225
+Need char = 128
+
+/\x{10000}/8DM
+Memory allocation (code space): 12
+------------------------------------------------------------------
+  0   8 Bra 0
+  3     \x{10000}
+  8   8 Ket
+ 11     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+First char = 240
+Need char = 128
+
+/\x{100000}/8DM
+Memory allocation (code space): 12
+------------------------------------------------------------------
+  0   8 Bra 0
+  3     \x{100000}
+  8   8 Ket
+ 11     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+First char = 244
+Need char = 128
+
+/\x{1000000}/8DM
+Memory allocation (code space): 13
+------------------------------------------------------------------
+  0   9 Bra 0
+  3     \x{1000000}
+  9   9 Ket
+ 12     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+First char = 249
+Need char = 128
+
+/\x{4000000}/8DM
+Memory allocation (code space): 14
+------------------------------------------------------------------
+  0  10 Bra 0
+  3     \x{4000000}
+ 10  10 Ket
+ 13     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+First char = 252
+Need char = 128
+
+/\x{7fffFFFF}/8DM
+Memory allocation (code space): 14
+------------------------------------------------------------------
+  0  10 Bra 0
+  3     \x{7fffffff}
+ 10  10 Ket
+ 13     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+First char = 253
+Need char = 191
+
+/[\x{ff}]/8DM
+Memory allocation (code space): 10
+------------------------------------------------------------------
+  0   6 Bra 0
+  3     \x{ff}
+  6   6 Ket
+  9     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+First char = 195
+Need char = 191
+
+/[\x{100}]/8DM
+Memory allocation (code space): 47
+------------------------------------------------------------------
+  0  11 Bra 0
+  3     [\x{100}]
+ 11  11 Ket
+ 14     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+No first char
+No need char
+
+/\x{ffffffff}/8
+Failed: character value in \x{...} sequence is too large at offset 11
+
+/\x{100000000}/8
+Failed: character value in \x{...} sequence is too large at offset 12
+
+/^\x{100}a\x{1234}/8
+    \x{100}a\x{1234}bcd
+ 0: \x{100}a\x{1234}
+
+/\x80/8D
+------------------------------------------------------------------
+  0   6 Bra 0
+  3     \x{80}
+  6   6 Ket
+  9     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+First char = 194
+Need char = 128
+
+/\xff/8D
+------------------------------------------------------------------
+  0   6 Bra 0
+  3     \x{ff}
+  6   6 Ket
+  9     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+First char = 195
+Need char = 191
+
+/\x{0041}\x{2262}\x{0391}\x{002e}/D8
+------------------------------------------------------------------
+  0  14 Bra 0
+  3     A\x{2262}\x{391}.
+ 14  14 Ket
+ 17     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+First char = 'A'
+Need char = '.'
+    \x{0041}\x{2262}\x{0391}\x{002e}
+ 0: A\x{2262}\x{391}.
+    
+/\x{D55c}\x{ad6d}\x{C5B4}/D8 
+------------------------------------------------------------------
+  0  15 Bra 0
+  3     \x{d55c}\x{ad6d}\x{c5b4}
+ 15  15 Ket
+ 18     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+First char = 237
+Need char = 180
+    \x{D55c}\x{ad6d}\x{C5B4} 
+ 0: \x{d55c}\x{ad6d}\x{c5b4}
+
+/\x{65e5}\x{672c}\x{8a9e}/D8
+------------------------------------------------------------------
+  0  15 Bra 0
+  3     \x{65e5}\x{672c}\x{8a9e}
+ 15  15 Ket
+ 18     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+First char = 230
+Need char = 158
+    \x{65e5}\x{672c}\x{8a9e}
+ 0: \x{65e5}\x{672c}\x{8a9e}
+
+/\x{80}/D8
+------------------------------------------------------------------
+  0   6 Bra 0
+  3     \x{80}
+  6   6 Ket
+  9     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+First char = 194
+Need char = 128
+
+/\x{084}/D8
+------------------------------------------------------------------
+  0   6 Bra 0
+  3     \x{84}
+  6   6 Ket
+  9     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+First char = 194
+Need char = 132
+
+/\x{104}/D8
+------------------------------------------------------------------
+  0   6 Bra 0
+  3     \x{104}
+  6   6 Ket
+  9     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+First char = 196
+Need char = 132
+
+/\x{861}/D8
+------------------------------------------------------------------
+  0   7 Bra 0
+  3     \x{861}
+  7   7 Ket
+ 10     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+First char = 224
+Need char = 161
+
+/\x{212ab}/D8
+------------------------------------------------------------------
+  0   8 Bra 0
+  3     \x{212ab}
+  8   8 Ket
+ 11     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+First char = 240
+Need char = 171
+
+/.{3,5}X/D8
+------------------------------------------------------------------
+  0  13 Bra 0
+  3     Any{3}
+  7     Any{0,2}
+ 11     X
+ 13  13 Ket
+ 16     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Partial matching not supported
+Options: utf8
+No first char
+Need char = 'X'
+    \x{212ab}\x{212ab}\x{212ab}\x{861}X
+ 0: \x{212ab}\x{212ab}\x{212ab}\x{861}X
+
+
+/.{3,5}?/D8
+------------------------------------------------------------------
+  0  11 Bra 0
+  3     Any{3}
+  7     Any{0,2}?
+ 11  11 Ket
+ 14     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Partial matching not supported
+Options: utf8
+No first char
+No need char
+    \x{212ab}\x{212ab}\x{212ab}\x{861}
+ 0: \x{212ab}\x{212ab}\x{212ab}
+
+/-- These tests are here rather than in testinput4 because Perl 5.6 has --/
+/-- some problems with UTF-8 support, in the area of \x{..} where the   --/
+No match
+/-- value is < 255. It grumbles about invalid UTF-8 strings.            --/
+No match
+
+/^[a\x{c0}]b/8
+    \x{c0}b
+ 0: \x{c0}b
+    
+/^([a\x{c0}]*?)aa/8
+    a\x{c0}aaaa/ 
+ 0: a\x{c0}aa
+ 1: a\x{c0}
+
+/^([a\x{c0}]*?)aa/8
+    a\x{c0}aaaa/ 
+ 0: a\x{c0}aa
+ 1: a\x{c0}
+    a\x{c0}a\x{c0}aaa/ 
+ 0: a\x{c0}a\x{c0}aa
+ 1: a\x{c0}a\x{c0}
+
+/^([a\x{c0}]*)aa/8
+    a\x{c0}aaaa/ 
+ 0: a\x{c0}aaaa
+ 1: a\x{c0}aa
+    a\x{c0}a\x{c0}aaa/ 
+ 0: a\x{c0}a\x{c0}aaa
+ 1: a\x{c0}a\x{c0}a
+
+/^([a\x{c0}]*)a\x{c0}/8
+    a\x{c0}aaaa/ 
+ 0: a\x{c0}
+ 1: 
+    a\x{c0}a\x{c0}aaa/ 
+ 0: a\x{c0}a\x{c0}
+ 1: a\x{c0}
+    
+/-- --/ 
+    
+/(?<=\C)X/8
+Failed: \C not allowed in lookbehind assertion at offset 6
+
+/-- This one is here not because it's different to Perl, but because the --/
+/-- way the captured single-byte is displayed. (In Perl it becomes a --/
+No match
+/-- character, and you can't tell the difference.) --/
+No match
+    
+/X(\C)(.*)/8
+    X\x{1234}
+ 0: X\x{1234}
+ 1: \xe1
+ 2: \x88\xb4
+    X\nabc 
+ 0: X\x{0a}abc
+ 1: \x{0a}
+ 2: abc
+    
+/^[ab]/8D
+------------------------------------------------------------------
+  0  37 Bra 0
+  3     ^
+  4     [ab]
+ 37  37 Ket
+ 40     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: anchored utf8
+No first char
+No need char
+    bar
+ 0: b
+    *** Failers
+No match
+    c
+No match
+    \x{ff}
+No match
+    \x{100}  
+No match
+
+/^[^ab]/8D
+------------------------------------------------------------------
+  0  37 Bra 0
+  3     ^
+  4     [\x00-`c-\xff] (neg)
+ 37  37 Ket
+ 40     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: anchored utf8
+No first char
+No need char
+    c
+ 0: c
+    \x{ff}
+ 0: \x{ff}
+    \x{100}  
+ 0: \x{100}
+    *** Failers 
+ 0: *
+    aaa
+No match
+  
+/[^ab\xC0-\xF0]/8SD
+------------------------------------------------------------------
+  0  36 Bra 0
+  3     [\x00-`c-\xbf\xf1-\xff] (neg)
+ 36  36 Ket
+ 39     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+No first char
+No need char
+Starting byte set: \x00 \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \x09 \x0a 
+  \x0b \x0c \x0d \x0e \x0f \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 
+  \x1a \x1b \x1c \x1d \x1e \x1f \x20 ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 
+  5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y 
+  Z [ \ ] ^ _ ` c d e f g h i j k l m n o p q r s t u v w x y z { | } ~ \x7f 
+  \xc2 \xc3 \xc4 \xc5 \xc6 \xc7 \xc8 \xc9 \xca \xcb \xcc \xcd \xce \xcf \xd0 
+  \xd1 \xd2 \xd3 \xd4 \xd5 \xd6 \xd7 \xd8 \xd9 \xda \xdb \xdc \xdd \xde \xdf 
+  \xe0 \xe1 \xe2 \xe3 \xe4 \xe5 \xe6 \xe7 \xe8 \xe9 \xea \xeb \xec \xed \xee 
+  \xef \xf0 \xf1 \xf2 \xf3 \xf4 \xf5 \xf6 \xf7 \xf8 \xf9 \xfa \xfb \xfc \xfd 
+  \xfe \xff 
+    \x{f1}
+ 0: \x{f1}
+    \x{bf}
+ 0: \x{bf}
+    \x{100}
+ 0: \x{100}
+    \x{1000}   
+ 0: \x{1000}
+    *** Failers
+ 0: *
+    \x{c0} 
+No match
+    \x{f0} 
+No match
+
+/Ä€{3,4}/8SD
+------------------------------------------------------------------
+  0  13 Bra 0
+  3     \x{100}{3}
+  8     \x{100}{,1}
+ 13  13 Ket
+ 16     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Partial matching not supported
+Options: utf8
+First char = 196
+Need char = 128
+Study returned NULL
+  \x{100}\x{100}\x{100}\x{100\x{100}
+ 0: \x{100}\x{100}\x{100}
+
+/(\x{100}+|x)/8SD
+------------------------------------------------------------------
+  0  17 Bra 0
+  3   6 Bra 1
+  6     \x{100}+
+  9   5 Alt
+ 12     x
+ 14  11 Ket
+ 17  17 Ket
+ 20     End
+------------------------------------------------------------------
+Capturing subpattern count = 1
+Partial matching not supported
+Options: utf8
+No first char
+No need char
+Starting byte set: x \xc4 
+
+/(\x{100}*a|x)/8SD
+------------------------------------------------------------------
+  0  19 Bra 0
+  3   8 Bra 1
+  6     \x{100}*
+  9     a
+ 11   5 Alt
+ 14     x
+ 16  13 Ket
+ 19  19 Ket
+ 22     End
+------------------------------------------------------------------
+Capturing subpattern count = 1
+Partial matching not supported
+Options: utf8
+No first char
+No need char
+Starting byte set: a x \xc4 
+
+/(\x{100}{0,2}a|x)/8SD
+------------------------------------------------------------------
+  0  21 Bra 0
+  3  10 Bra 1
+  6     \x{100}{,2}
+ 11     a
+ 13   5 Alt
+ 16     x
+ 18  15 Ket
+ 21  21 Ket
+ 24     End
+------------------------------------------------------------------
+Capturing subpattern count = 1
+Partial matching not supported
+Options: utf8
+No first char
+No need char
+Starting byte set: a x \xc4 
+
+/(\x{100}{1,2}a|x)/8SD
+------------------------------------------------------------------
+  0  24 Bra 0
+  3  13 Bra 1
+  6     \x{100}
+  9     \x{100}{,1}
+ 14     a
+ 16   5 Alt
+ 19     x
+ 21  18 Ket
+ 24  24 Ket
+ 27     End
+------------------------------------------------------------------
+Capturing subpattern count = 1
+Partial matching not supported
+Options: utf8
+No first char
+No need char
+Starting byte set: x \xc4 
+
+/\x{100}*(\d+|"(?1)")/8
+    1234
+ 0: 1234
+ 1: 1234
+    "1234" 
+ 0: "1234"
+ 1: "1234"
+    \x{100}1234
+ 0: \x{100}1234
+ 1: 1234
+    "\x{100}1234"  
+ 0: \x{100}1234
+ 1: 1234
+    \x{100}\x{100}12ab 
+ 0: \x{100}\x{100}12
+ 1: 12
+    \x{100}\x{100}"12" 
+ 0: \x{100}\x{100}"12"
+ 1: "12"
+    *** Failers 
+No match
+    \x{100}\x{100}abcd
+No match
+
+/\x{100}/8D
+------------------------------------------------------------------
+  0   6 Bra 0
+  3     \x{100}
+  6   6 Ket
+  9     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+First char = 196
+Need char = 128
+
+/\x{100}*/8D
+------------------------------------------------------------------
+  0   6 Bra 0
+  3     \x{100}*
+  6   6 Ket
+  9     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Partial matching not supported
+Options: utf8
+No first char
+No need char
+
+/a\x{100}*/8D
+------------------------------------------------------------------
+  0   8 Bra 0
+  3     a
+  5     \x{100}*
+  8   8 Ket
+ 11     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Partial matching not supported
+Options: utf8
+First char = 'a'
+No need char
+
+/ab\x{100}*/8D
+------------------------------------------------------------------
+  0  10 Bra 0
+  3     ab
+  7     \x{100}*
+ 10  10 Ket
+ 13     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Partial matching not supported
+Options: utf8
+First char = 'a'
+Need char = 'b'
+
+/a\x{100}\x{101}*/8D
+------------------------------------------------------------------
+  0  11 Bra 0
+  3     a\x{100}
+  8     \x{101}*
+ 11  11 Ket
+ 14     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Partial matching not supported
+Options: utf8
+First char = 'a'
+Need char = 128
+
+/a\x{100}\x{101}+/8D
+------------------------------------------------------------------
+  0  11 Bra 0
+  3     a\x{100}
+  8     \x{101}+
+ 11  11 Ket
+ 14     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Partial matching not supported
+Options: utf8
+First char = 'a'
+Need char = 129
+
+/\x{100}*A/8D
+------------------------------------------------------------------
+  0   8 Bra 0
+  3     \x{100}*
+  6     A
+  8   8 Ket
+ 11     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Partial matching not supported
+Options: utf8
+No first char
+Need char = 'A'
+    A
+ 0: A
+
+/\x{100}*\d(?R)/8D
+------------------------------------------------------------------
+  0  10 Bra 0
+  3     \x{100}*
+  6     \d
+  7   0 Recurse
+ 10  10 Ket
+ 13     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Partial matching not supported
+Options: utf8
+No first char
+No need char
+
+/[^\x{c4}]/D
+------------------------------------------------------------------
+  0  36 Bra 0
+  3     [\x01-35-bd-z|~-\xff] (neg)
+ 36  36 Ket
+ 39     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+
+/[^\x{c4}]/8D
+------------------------------------------------------------------
+  0  36 Bra 0
+  3     [\x00-\xc3\xc5-\xff] (neg)
+ 36  36 Ket
+ 39     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+No first char
+No need char
+
+/[\x{100}]/8DM
+Memory allocation (code space): 47
+------------------------------------------------------------------
+  0  11 Bra 0
+  3     [\x{100}]
+ 11  11 Ket
+ 14     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+No first char
+No need char
+    \x{100}
+ 0: \x{100}
+    Z\x{100}
+ 0: \x{100}
+    \x{100}Z
+ 0: \x{100}
+    *** Failers 
+No match
+
+/[Z\x{100}]/8DM
+Memory allocation (code space): 47
+------------------------------------------------------------------
+  0  43 Bra 0
+  3     [Z\x{100}]
+ 43  43 Ket
+ 46     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+No first char
+No need char
+    Z\x{100}
+ 0: Z
+    \x{100}
+ 0: \x{100}
+    \x{100}Z
+ 0: \x{100}
+    *** Failers 
+No match
+
+/[\x{200}-\x{100}]/8
+Failed: range out of order in character class at offset 15
+
+/[Ä€-Ä„]/8
+    \x{100}
+ 0: \x{100}
+    \x{104}
+ 0: \x{104}
+    *** Failers
+No match
+    \x{105}
+No match
+    \x{ff}    
+No match
+
+/[z-\x{100}]/8D
+------------------------------------------------------------------
+  0  12 Bra 0
+  3     [z-\x{100}]
+ 12  12 Ket
+ 15     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+No first char
+No need char
+
+/[z\Qa-d]Ä€\E]/8D
+------------------------------------------------------------------
+  0  43 Bra 0
+  3     [\-\]adz\x{100}]
+ 43  43 Ket
+ 46     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+No first char
+No need char
+    \x{100}
+ 0: \x{100}
+    Ā 
+ 0: \x{100}
+
+/[\xFF]/D
+------------------------------------------------------------------
+  0   5 Bra 0
+  3     \xff
+  5   5 Ket
+  8     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+No options
+First char = 255
+No need char
+    >\xff<
+ 0: \xff
+
+/[\xff]/D8
+------------------------------------------------------------------
+  0   6 Bra 0
+  3     \x{ff}
+  6   6 Ket
+  9     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+First char = 195
+Need char = 191
+    >\x{ff}<
+ 0: \x{ff}
+
+/[^\xFF]/D
+------------------------------------------------------------------
+  0   5 Bra 0
+  3     [^\xff]
+  5   5 Ket
+  8     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+
+/[^\xff]/8D
+------------------------------------------------------------------
+  0  36 Bra 0
+  3     [\x00-\xfe] (neg)
+ 36  36 Ket
+ 39     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+No first char
+No need char
+
+/[Ä-Ü]/8
+    Ö # Matches without Study
+ 0: \x{d6}
+    \x{d6}
+ 0: \x{d6}
+    
+/[Ä-Ü]/8S
+    Ö <-- Same with Study
+ 0: \x{d6}
+    \x{d6}
+ 0: \x{d6}
+    
+/[\x{c4}-\x{dc}]/8 
+    Ö # Matches without Study
+ 0: \x{d6}
+    \x{d6} 
+ 0: \x{d6}
+
+/[\x{c4}-\x{dc}]/8S
+    Ö <-- Same with Study
+ 0: \x{d6}
+    \x{d6} 
+ 0: \x{d6}
+
+/[Ã]/8
+Failed: invalid UTF-8 string at offset 2
+
+/Ã/8
+Failed: invalid UTF-8 string at offset 0
+
+/ÃÃÃxxx/8
+Failed: invalid UTF-8 string at offset 1
+
+/ÃÃÃxxx/8?D
+------------------------------------------------------------------
+  0  15 Bra 0
+  3     \X{c0}\X{c0}\X{c0}xxx
+ 15  15 Ket
+ 18     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8 no_utf8_check
+First char = 195
+Need char = 'x'
+
+/abc/8
+    Ã]
+Error -10
+    Ã
+Error -10
+    ÃÃÃ
+Error -10
+    ÃÃÃ\?
+No match
+
+/anything/8
+    \xc0\x80
+Error -10
+    \xc1\x8f 
+Error -10
+    \xe0\x9f\x80
+Error -10
+    \xf0\x8f\x80\x80 
+Error -10
+    \xf8\x87\x80\x80\x80  
+Error -10
+    \xfc\x83\x80\x80\x80\x80
+Error -10
+    \xfe\x80\x80\x80\x80\x80  
+Error -10
+    \xff\x80\x80\x80\x80\x80  
+Error -10
+    \xc3\x8f
+No match
+    \xe0\xaf\x80
+No match
+    \xe1\x80\x80
+No match
+    \xf0\x9f\x80\x80 
+No match
+    \xf1\x8f\x80\x80 
+No match
+    \xf8\x88\x80\x80\x80  
+No match
+    \xf9\x87\x80\x80\x80  
+No match
+    \xfc\x84\x80\x80\x80\x80
+No match
+    \xfd\x83\x80\x80\x80\x80
+No match
+
+/\x{100}abc(xyz(?1))/8D
+------------------------------------------------------------------
+  0  27 Bra 0
+  3     \x{100}abc
+ 12  12 Bra 1
+ 15     xyz
+ 21  12 Recurse
+ 24  12 Ket
+ 27  27 Ket
+ 30     End
+------------------------------------------------------------------
+Capturing subpattern count = 1
+Options: utf8
+First char = 196
+Need char = 'z'
+
+/[^\x{100}]abc(xyz(?1))/8D
+------------------------------------------------------------------
+  0  32 Bra 0
+  3     [^\x{100}]
+ 11     abc
+ 17  12 Bra 1
+ 20     xyz
+ 26  17 Recurse
+ 29  12 Ket
+ 32  32 Ket
+ 35     End
+------------------------------------------------------------------
+Capturing subpattern count = 1
+Options: utf8
+No first char
+Need char = 'z'
+
+/[ab\x{100}]abc(xyz(?1))/8D
+------------------------------------------------------------------
+  0  64 Bra 0
+  3     [ab\x{100}]
+ 43     abc
+ 49  12 Bra 1
+ 52     xyz
+ 58  49 Recurse
+ 61  12 Ket
+ 64  64 Ket
+ 67     End
+------------------------------------------------------------------
+Capturing subpattern count = 1
+Options: utf8
+No first char
+Need char = 'z'
+
+/(\x{100}(b(?2)c))?/D8
+------------------------------------------------------------------
+  0  26 Bra 0
+  3     Brazero
+  4  19 Bra 1
+  7     \x{100}
+ 10  10 Bra 2
+ 13     b
+ 15  10 Recurse
+ 18     c
+ 20  10 Ket
+ 23  19 Ket
+ 26  26 Ket
+ 29     End
+------------------------------------------------------------------
+Capturing subpattern count = 2
+Options: utf8
+No first char
+No need char
+
+/(\x{100}(b(?2)c)){0,2}/D8
+------------------------------------------------------------------
+  0  55 Bra 0
+  3     Brazero
+  4  48 Bra 0
+  7  19 Bra 1
+ 10     \x{100}
+ 13  10 Bra 2
+ 16     b
+ 18  13 Recurse
+ 21     c
+ 23  10 Ket
+ 26  19 Ket
+ 29     Brazero
+ 30  19 Bra 1
+ 33     \x{100}
+ 36  10 Bra 2
+ 39     b
+ 41  13 Recurse
+ 44     c
+ 46  10 Ket
+ 49  19 Ket
+ 52  48 Ket
+ 55  55 Ket
+ 58     End
+------------------------------------------------------------------
+Capturing subpattern count = 2
+Options: utf8
+No first char
+No need char
+
+/(\x{100}(b(?1)c))?/D8
+------------------------------------------------------------------
+  0  26 Bra 0
+  3     Brazero
+  4  19 Bra 1
+  7     \x{100}
+ 10  10 Bra 2
+ 13     b
+ 15   4 Recurse
+ 18     c
+ 20  10 Ket
+ 23  19 Ket
+ 26  26 Ket
+ 29     End
+------------------------------------------------------------------
+Capturing subpattern count = 2
+Options: utf8
+No first char
+No need char
+
+/(\x{100}(b(?1)c)){0,2}/D8
+------------------------------------------------------------------
+  0  55 Bra 0
+  3     Brazero
+  4  48 Bra 0
+  7  19 Bra 1
+ 10     \x{100}
+ 13  10 Bra 2
+ 16     b
+ 18   7 Recurse
+ 21     c
+ 23  10 Ket
+ 26  19 Ket
+ 29     Brazero
+ 30  19 Bra 1
+ 33     \x{100}
+ 36  10 Bra 2
+ 39     b
+ 41   7 Recurse
+ 44     c
+ 46  10 Ket
+ 49  19 Ket
+ 52  48 Ket
+ 55  55 Ket
+ 58     End
+------------------------------------------------------------------
+Capturing subpattern count = 2
+Options: utf8
+No first char
+No need char
+
+/\W/8
+    A.B
+ 0: .
+    A\x{100}B 
+ 0: \x{100}
+  
+/\w/8
+    \x{100}X   
+ 0: X
+
+/ End of testinput5 /
diff --git a/connectors/jk/native/iis/pcre/testdata/testoutput6 b/connectors/jk/native/iis/pcre/testdata/testoutput6
new file mode 100644
index 0000000..732e5aa
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/testdata/testoutput6
@@ -0,0 +1,1013 @@
+PCRE version 5.0 13-Sep-2004
+
+/^\pC\pL\pM\pN\pP\pS\pZ</8
+    \x7f\x{c0}\x{30f}\x{660}\x{66c}\x{f01}\x{1680}<
+ 0: \x{7f}\x{c0}\x{30f}\x{660}\x{66c}\x{f01}\x{1680}<
+    \np\x{300}9!\$ < 
+ 0: \x{0a}p\x{300}9!$ <
+    ** Failers 
+No match
+    ap\x{300}9!\$ < 
+No match
+  
+/^\PC/8
+    X
+ 0: X
+    ** Failers 
+ 0: *
+    \x7f
+No match
+  
+/^\PL/8
+    9
+ 0: 9
+    ** Failers 
+ 0: *
+    \x{c0}
+No match
+  
+/^\PM/8
+    X
+ 0: X
+    ** Failers 
+ 0: *
+    \x{30f}
+No match
+  
+/^\PN/8
+    X
+ 0: X
+    ** Failers 
+ 0: *
+    \x{660}
+No match
+  
+/^\PP/8
+    X
+ 0: X
+    ** Failers 
+No match
+    \x{66c}
+No match
+  
+/^\PS/8
+    X
+ 0: X
+    ** Failers 
+ 0: *
+    \x{f01}
+No match
+  
+/^\PZ/8
+    X
+ 0: X
+    ** Failers 
+ 0: *
+    \x{1680}
+No match
+    
+/^\p{Cc}/8
+    \x{017}
+ 0: \x{17}
+    \x{09f} 
+ 0: \x{9f}
+    ** Failers
+No match
+    \x{0600} 
+No match
+  
+/^\p{Cf}/8
+    \x{601}
+ 0: \x{601}
+    ** Failers
+No match
+    \x{09f} 
+No match
+  
+/^\p{Cn}/8
+    ** Failers
+No match
+    \x{09f} 
+No match
+  
+/^\p{Co}/8
+    \x{f8ff}
+ 0: \x{f8ff}
+    ** Failers
+No match
+    \x{09f} 
+No match
+  
+/^\p{Cs}/8
+    \x{dfff}
+ 0: \x{dfff}
+    ** Failers
+No match
+    \x{09f} 
+No match
+  
+/^\p{Ll}/8
+    a
+ 0: a
+    ** Failers 
+No match
+    Z
+No match
+    \x{dfff}  
+No match
+  
+/^\p{Lm}/8
+    \x{2b0}
+ 0: \x{2b0}
+    ** Failers
+No match
+    a 
+No match
+  
+/^\p{Lo}/8
+    \x{1bb}
+ 0: \x{1bb}
+    ** Failers
+No match
+    a 
+No match
+    \x{2b0}
+No match
+  
+/^\p{Lt}/8
+    \x{1c5}
+ 0: \x{1c5}
+    ** Failers
+No match
+    a 
+No match
+    \x{2b0}
+No match
+  
+/^\p{Lu}/8
+    A
+ 0: A
+    ** Failers
+No match
+    \x{2b0}
+No match
+  
+/^\p{Mc}/8
+    \x{903}
+ 0: \x{903}
+    ** Failers
+No match
+    X
+No match
+    \x{300}
+No match
+       
+/^\p{Me}/8
+    \x{488}
+ 0: \x{488}
+    ** Failers
+No match
+    X
+No match
+    \x{903}
+No match
+    \x{300}
+No match
+  
+/^\p{Mn}/8
+    \x{300}
+ 0: \x{300}
+    ** Failers
+No match
+    X
+No match
+    \x{903}
+No match
+  
+/^\p{Nd}+/8
+    0123456789\x{660}\x{661}\x{662}\x{663}\x{664}\x{665}\x{666}\x{667}\x{668}\x{669}\x{66a}
+ 0: 0123456789\x{660}\x{661}\x{662}\x{663}\x{664}\x{665}\x{666}\x{667}\x{668}\x{669}
+    \x{6f0}\x{6f1}\x{6f2}\x{6f3}\x{6f4}\x{6f5}\x{6f6}\x{6f7}\x{6f8}\x{6f9}\x{6fa}
+ 0: \x{6f0}\x{6f1}\x{6f2}\x{6f3}\x{6f4}\x{6f5}\x{6f6}\x{6f7}\x{6f8}\x{6f9}
+    \x{966}\x{967}\x{968}\x{969}\x{96a}\x{96b}\x{96c}\x{96d}\x{96e}\x{96f}\x{970}
+ 0: \x{966}\x{967}\x{968}\x{969}\x{96a}\x{96b}\x{96c}\x{96d}\x{96e}\x{96f}
+    ** Failers
+No match
+    X
+No match
+  
+/^\p{Nl}/8
+    \x{16ee}
+ 0: \x{16ee}
+    ** Failers
+No match
+    X
+No match
+    \x{966}
+No match
+  
+/^\p{No}/8
+    \x{b2}
+ 0: \x{b2}
+    \x{b3}
+ 0: \x{b3}
+    ** Failers
+No match
+    X
+No match
+    \x{16ee}
+No match
+  
+/^\p{Pc}/8
+    \x5f
+ 0: _
+    \x{203f}
+ 0: \x{203f}
+    ** Failers
+No match
+    X
+No match
+    -
+No match
+    \x{58a}
+No match
+  
+/^\p{Pd}/8
+    -
+ 0: -
+    \x{58a}
+ 0: \x{58a}
+    ** Failers
+No match
+    X
+No match
+    \x{203f}
+No match
+  
+/^\p{Pe}/8
+    )
+ 0: )
+    ]
+ 0: ]
+    }
+ 0: }
+    \x{f3b}
+ 0: \x{f3b}
+    ** Failers
+No match
+    X
+No match
+    \x{203f}
+No match
+    (
+No match
+    [
+No match
+    {
+No match
+    \x{f3c}
+No match
+  
+/^\p{Pf}/8
+    \x{bb}
+ 0: \x{bb}
+    \x{2019}
+ 0: \x{2019}
+    ** Failers
+No match
+    X
+No match
+    \x{203f}
+No match
+  
+/^\p{Pi}/8
+    \x{ab}
+ 0: \x{ab}
+    \x{2018}
+ 0: \x{2018}
+    ** Failers
+No match
+    X
+No match
+    \x{203f}
+No match
+  
+/^\p{Po}/8
+    !
+ 0: !
+    \x{37e}
+ 0: \x{37e}
+    ** Failers
+ 0: *
+    X
+No match
+    \x{203f}
+No match
+  
+/^\p{Ps}/8
+    (
+ 0: (
+    [
+ 0: [
+    {
+ 0: {
+    \x{f3c}
+ 0: \x{f3c}
+    ** Failers
+No match
+    X
+No match
+    )
+No match
+    ]
+No match
+    }
+No match
+    \x{f3b}
+No match
+  
+/^\p{Sc}+/8
+    $\x{a2}\x{a3}\x{a4}\x{a5}\x{a6}
+ 0: $\x{a2}\x{a3}\x{a4}\x{a5}
+    \x{9f2}
+ 0: \x{9f2}
+    ** Failers
+No match
+    X
+No match
+    \x{2c2}
+No match
+  
+/^\p{Sk}/8
+    \x{2c2}
+ 0: \x{2c2}
+    ** Failers
+No match
+    X
+No match
+    \x{9f2}
+No match
+  
+/^\p{Sm}+/8
+    +<|~\x{ac}\x{2044}
+ 0: +<|~\x{ac}\x{2044}
+    ** Failers
+No match
+    X
+No match
+    \x{9f2}
+No match
+  
+/^\p{So}/8
+    \x{a6}
+ 0: \x{a6}
+    \x{482} 
+ 0: \x{482}
+    ** Failers
+No match
+    X
+No match
+    \x{9f2}
+No match
+  
+/^\p{Zl}/8
+    \x{2028}
+ 0: \x{2028}
+    ** Failers
+No match
+    X
+No match
+    \x{2029}
+No match
+  
+/^\p{Zp}/8
+    \x{2029}
+ 0: \x{2029}
+    ** Failers
+No match
+    X
+No match
+    \x{2028}
+No match
+  
+/^\p{Zs}/8
+    \ \
+ 0:  
+    \x{a0}
+ 0: \x{a0}
+    \x{1680}
+ 0: \x{1680}
+    \x{180e}
+ 0: \x{180e}
+    \x{2000}
+ 0: \x{2000}
+    \x{2001}     
+ 0: \x{2001}
+    ** Failers
+No match
+    \x{2028}
+No match
+    \x{200d} 
+No match
+  
+/\p{Nd}+(..)/8
+      \x{660}\x{661}\x{662}ABC
+ 0: \x{660}\x{661}\x{662}AB
+ 1: AB
+  
+/\p{Nd}+?(..)/8
+      \x{660}\x{661}\x{662}ABC
+ 0: \x{660}\x{661}\x{662}
+ 1: \x{661}\x{662}
+  
+/\p{Nd}{2,}(..)/8
+      \x{660}\x{661}\x{662}ABC
+ 0: \x{660}\x{661}\x{662}AB
+ 1: AB
+  
+/\p{Nd}{2,}?(..)/8
+      \x{660}\x{661}\x{662}ABC
+ 0: \x{660}\x{661}\x{662}A
+ 1: \x{662}A
+  
+/\p{Nd}*(..)/8
+      \x{660}\x{661}\x{662}ABC
+ 0: \x{660}\x{661}\x{662}AB
+ 1: AB
+  
+/\p{Nd}*?(..)/8
+      \x{660}\x{661}\x{662}ABC
+ 0: \x{660}\x{661}
+ 1: \x{660}\x{661}
+  
+/\p{Nd}{2}(..)/8
+      \x{660}\x{661}\x{662}ABC
+ 0: \x{660}\x{661}\x{662}A
+ 1: \x{662}A
+  
+/\p{Nd}{2,3}(..)/8
+      \x{660}\x{661}\x{662}ABC
+ 0: \x{660}\x{661}\x{662}AB
+ 1: AB
+  
+/\p{Nd}{2,3}?(..)/8
+      \x{660}\x{661}\x{662}ABC
+ 0: \x{660}\x{661}\x{662}A
+ 1: \x{662}A
+  
+/\p{Nd}?(..)/8
+      \x{660}\x{661}\x{662}ABC
+ 0: \x{660}\x{661}\x{662}
+ 1: \x{661}\x{662}
+  
+/\p{Nd}??(..)/8
+      \x{660}\x{661}\x{662}ABC
+ 0: \x{660}\x{661}
+ 1: \x{660}\x{661}
+  
+/\p{Nd}*+(..)/8
+      \x{660}\x{661}\x{662}ABC
+ 0: \x{660}\x{661}\x{662}AB
+ 1: AB
+  
+/\p{Nd}*+(...)/8
+      \x{660}\x{661}\x{662}ABC
+ 0: \x{660}\x{661}\x{662}ABC
+ 1: ABC
+  
+/\p{Nd}*+(....)/8
+      ** Failers
+ 0: ** F
+ 1: ** F
+      \x{660}\x{661}\x{662}ABC
+No match
+  
+/\p{Lu}/8i
+    A
+ 0: A
+    a\x{10a0}B 
+ 0: \x{10a0}
+    ** Failers 
+ 0: F
+    a
+No match
+    \x{1d00}  
+No match
+
+/\p{^Lu}/8i
+    1234
+ 0: 1
+    ** Failers
+ 0: *
+    ABC 
+No match
+
+/\P{Lu}/8i
+    1234
+ 0: 1
+    ** Failers
+ 0: *
+    ABC 
+No match
+
+/(?<=A\p{Nd})XYZ/8
+    A2XYZ
+ 0: XYZ
+    123A5XYZPQR
+ 0: XYZ
+    ABA\x{660}XYZpqr
+ 0: XYZ
+    ** Failers
+No match
+    AXYZ
+No match
+    XYZ     
+No match
+    
+/(?<!\pL)XYZ/8
+    1XYZ
+ 0: XYZ
+    AB=XYZ.. 
+ 0: XYZ
+    XYZ 
+ 0: XYZ
+    ** Failers
+No match
+    WXYZ 
+No match
+
+/[\p{L}]/D
+------------------------------------------------------------------
+  0  10 Bra 0
+  3     [\p{L}]
+ 10  10 Ket
+ 13     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+
+/[\p{^L}]/D
+------------------------------------------------------------------
+  0  10 Bra 0
+  3     [\P{L}]
+ 10  10 Ket
+ 13     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+
+/[\P{L}]/D
+------------------------------------------------------------------
+  0  10 Bra 0
+  3     [\P{L}]
+ 10  10 Ket
+ 13     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+
+/[\P{^L}]/D
+------------------------------------------------------------------
+  0  10 Bra 0
+  3     [\p{L}]
+ 10  10 Ket
+ 13     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+No options
+No first char
+No need char
+
+/[abc\p{L}\x{0660}]/8D
+------------------------------------------------------------------
+  0  45 Bra 0
+  3     [a-c\p{L}\x{660}]
+ 45  45 Ket
+ 48     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+No first char
+No need char
+
+/[\p{Nd}]/8DM
+Memory allocation (code space): 46
+------------------------------------------------------------------
+  0  10 Bra 0
+  3     [\p{Nd}]
+ 10  10 Ket
+ 13     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+No first char
+No need char
+    1234
+ 0: 1
+
+/[\p{Nd}+-]+/8DM
+Memory allocation (code space): 47
+------------------------------------------------------------------
+  0  43 Bra 0
+  3     [+\-\p{Nd}]+
+ 43  43 Ket
+ 46     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Partial matching not supported
+Options: utf8
+No first char
+No need char
+    1234
+ 0: 1234
+    12-34
+ 0: 12-34
+    12+\x{661}-34  
+ 0: 12+\x{661}-34
+    ** Failers
+No match
+    abcd  
+No match
+
+/[\P{Nd}]+/8
+    abcd
+ 0: abcd
+    ** Failers
+ 0: ** Failers
+    1234
+No match
+
+/\D+/8
+    11111111111111111111111111111111111111111111111111111111111111111111111
+No match
+    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+ 0: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+     
+/\P{Nd}+/8
+    11111111111111111111111111111111111111111111111111111111111111111111111
+No match
+    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+ 0: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+
+/[\D]+/8
+    11111111111111111111111111111111111111111111111111111111111111111111111
+No match
+    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+ 0: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+
+/[\P{Nd}]+/8
+    11111111111111111111111111111111111111111111111111111111111111111111111
+No match
+    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+ 0: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+
+/[\D\P{Nd}]+/8
+    11111111111111111111111111111111111111111111111111111111111111111111111
+No match
+    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+ 0: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+
+/\pL/8
+    a
+ 0: a
+    A 
+ 0: A
+
+/\pL/8i
+    a
+ 0: a
+    A 
+ 0: A
+    
+/\p{Lu}/8 
+    A
+ 0: A
+    aZ
+ 0: Z
+    ** Failers
+ 0: F
+    abc   
+No match
+
+/\p{Lu}/8i
+    A
+ 0: A
+    aZ
+ 0: Z
+    ** Failers
+ 0: F
+    abc   
+No match
+
+/\p{Ll}/8 
+    a
+ 0: a
+    Az
+ 0: z
+    ** Failers
+ 0: a
+    ABC   
+No match
+
+/\p{Ll}/8i 
+    a
+ 0: a
+    Az
+ 0: z
+    ** Failers
+ 0: a
+    ABC   
+No match
+
+/^\x{c0}$/8i
+    \x{c0}
+ 0: \x{c0}
+    \x{e0} 
+ 0: \x{e0}
+
+/^\x{e0}$/8i
+    \x{c0}
+ 0: \x{c0}
+    \x{e0} 
+ 0: \x{e0}
+
+/A\x{391}\x{10427}\x{ff3a}\x{1fb0}/8
+    A\x{391}\x{10427}\x{ff3a}\x{1fb0}
+ 0: A\x{391}\x{10427}\x{ff3a}\x{1fb0}
+    ** Failers
+No match
+    a\x{391}\x{10427}\x{ff3a}\x{1fb0}   
+No match
+    A\x{3b1}\x{10427}\x{ff3a}\x{1fb0}
+No match
+    A\x{391}\x{1044F}\x{ff3a}\x{1fb0}
+No match
+    A\x{391}\x{10427}\x{ff5a}\x{1fb0}
+No match
+    A\x{391}\x{10427}\x{ff3a}\x{1fb8}
+No match
+
+/A\x{391}\x{10427}\x{ff3a}\x{1fb0}/8i
+    A\x{391}\x{10427}\x{ff3a}\x{1fb0}
+ 0: A\x{391}\x{10427}\x{ff3a}\x{1fb0}
+    a\x{391}\x{10427}\x{ff3a}\x{1fb0}   
+ 0: a\x{391}\x{10427}\x{ff3a}\x{1fb0}
+    A\x{3b1}\x{10427}\x{ff3a}\x{1fb0}
+ 0: A\x{3b1}\x{10427}\x{ff3a}\x{1fb0}
+    A\x{391}\x{1044F}\x{ff3a}\x{1fb0}
+ 0: A\x{391}\x{1044f}\x{ff3a}\x{1fb0}
+    A\x{391}\x{10427}\x{ff5a}\x{1fb0}
+ 0: A\x{391}\x{10427}\x{ff5a}\x{1fb0}
+    A\x{391}\x{10427}\x{ff3a}\x{1fb8}
+ 0: A\x{391}\x{10427}\x{ff3a}\x{1fb8}
+
+/A\x{391}\x{10427}\x{ff3a}\x{1fb0}/8iD
+------------------------------------------------------------------
+  0  21 Bra 0
+  3  NC A\x{391}\x{10427}\x{ff3a}\x{1fb0}
+ 21  21 Ket
+ 24     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: caseless utf8
+First char = 'A' (caseless)
+No need char
+
+/A\x{391}\x{10427}\x{ff3a}\x{1fb0}/8D
+------------------------------------------------------------------
+  0  21 Bra 0
+  3     A\x{391}\x{10427}\x{ff3a}\x{1fb0}
+ 21  21 Ket
+ 24     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+First char = 'A'
+Need char = 176
+
+/AB\x{1fb0}/8D
+------------------------------------------------------------------
+  0  11 Bra 0
+  3     AB\x{1fb0}
+ 11  11 Ket
+ 14     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: utf8
+First char = 'A'
+Need char = 176
+
+/AB\x{1fb0}/8Di
+------------------------------------------------------------------
+  0  11 Bra 0
+  3  NC AB\x{1fb0}
+ 11  11 Ket
+ 14     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: caseless utf8
+First char = 'A' (caseless)
+Need char = 'B' (caseless)
+
+/\x{391}+/8i
+    \x{391}\x{3b1}\x{3b1}\x{3b1}\x{391}
+ 0: \x{391}\x{3b1}\x{3b1}\x{3b1}\x{391}
+
+/\x{391}{3,5}(.)/8i
+    \x{391}\x{3b1}\x{3b1}\x{3b1}\x{391}X
+ 0: \x{391}\x{3b1}\x{3b1}\x{3b1}\x{391}X
+ 1: X
+
+/\x{391}{3,5}?(.)/8i
+    \x{391}\x{3b1}\x{3b1}\x{3b1}\x{391}X
+ 0: \x{391}\x{3b1}\x{3b1}\x{3b1}
+ 1: \x{3b1}
+
+/[\x{391}\x{ff3a}]/8i
+    \x{391}
+ 0: \x{391}
+    \x{ff3a}
+ 0: \x{ff3a}
+    \x{3b1}
+ 0: \x{3b1}
+    \x{ff5a}   
+ 0: \x{ff5a}
+    
+/[\x{c0}\x{391}]/8i
+    \x{c0}
+ 0: \x{c0}
+    \x{e0} 
+ 0: \x{e0}
+
+/[\x{105}-\x{109}]/8iD
+------------------------------------------------------------------
+  0  13 Bra 0
+  3     [\x{104}-\x{109}]
+ 13  13 Ket
+ 16     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: caseless utf8
+No first char
+No need char
+    \x{104}
+ 0: \x{104}
+    \x{105}
+ 0: \x{105}
+    \x{109}  
+ 0: \x{109}
+    ** Failers
+No match
+    \x{100}
+No match
+    \x{10a} 
+No match
+    
+/[z-\x{100}]/8iD 
+------------------------------------------------------------------
+  0  20 Bra 0
+  3     [Z\x{39c}\x{178}z-\x{101}]
+ 20  20 Ket
+ 23     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: caseless utf8
+No first char
+No need char
+    Z
+ 0: Z
+    z
+ 0: z
+    \x{39c}
+ 0: \x{39c}
+    \x{178}
+ 0: \x{178}
+    |
+ 0: |
+    \x{80}
+ 0: \x{80}
+    \x{ff}
+ 0: \x{ff}
+    \x{100}
+ 0: \x{100}
+    \x{101} 
+ 0: \x{101}
+    ** Failers
+No match
+    \x{102}
+No match
+    Y
+No match
+    y           
+No match
+
+/[z-\x{100}]/8Di
+------------------------------------------------------------------
+  0  20 Bra 0
+  3     [Z\x{39c}\x{178}z-\x{101}]
+ 20  20 Ket
+ 23     End
+------------------------------------------------------------------
+Capturing subpattern count = 0
+Options: caseless utf8
+No first char
+No need char
+
+/^\X/8
+    A
+ 0: A
+    A\x{300}BC 
+ 0: A\x{300}
+    A\x{300}\x{301}\x{302}BC 
+ 0: A\x{300}\x{301}\x{302}
+    *** Failers
+ 0: *
+    \x{300}  
+No match
+
+/^[\X]/8
+    X123
+ 0: X
+    *** Failers
+No match
+    AXYZ
+No match
+
+/^(\X*)C/8
+    A\x{300}\x{301}\x{302}BCA\x{300}\x{301} 
+ 0: A\x{300}\x{301}\x{302}BC
+ 1: A\x{300}\x{301}\x{302}B
+    A\x{300}\x{301}\x{302}BCA\x{300}\x{301}C 
+ 0: A\x{300}\x{301}\x{302}BCA\x{300}\x{301}C
+ 1: A\x{300}\x{301}\x{302}BCA\x{300}\x{301}
+
+/^(\X*?)C/8
+    A\x{300}\x{301}\x{302}BCA\x{300}\x{301} 
+ 0: A\x{300}\x{301}\x{302}BC
+ 1: A\x{300}\x{301}\x{302}B
+    A\x{300}\x{301}\x{302}BCA\x{300}\x{301}C 
+ 0: A\x{300}\x{301}\x{302}BC
+ 1: A\x{300}\x{301}\x{302}B
+
+/^(\X*)(.)/8
+    A\x{300}\x{301}\x{302}BCA\x{300}\x{301} 
+ 0: A\x{300}\x{301}\x{302}BCA
+ 1: A\x{300}\x{301}\x{302}BC
+ 2: A
+    A\x{300}\x{301}\x{302}BCA\x{300}\x{301}C 
+ 0: A\x{300}\x{301}\x{302}BCA\x{300}\x{301}C
+ 1: A\x{300}\x{301}\x{302}BCA\x{300}\x{301}
+ 2: C
+
+/^(\X*?)(.)/8
+    A\x{300}\x{301}\x{302}BCA\x{300}\x{301} 
+ 0: A
+ 1: 
+ 2: A
+    A\x{300}\x{301}\x{302}BCA\x{300}\x{301}C 
+ 0: A
+ 1: 
+ 2: A
+
+/^\X(.)/8
+    *** Failers
+ 0: **
+ 1: *
+    A\x{300}\x{301}\x{302}
+No match
+
+/^\X{2,3}(.)/8
+    A\x{300}\x{301}B\x{300}X
+ 0: A\x{300}\x{301}B\x{300}X
+ 1: X
+    A\x{300}\x{301}B\x{300}C\x{300}\x{301}
+ 0: A\x{300}\x{301}B\x{300}C
+ 1: C
+    A\x{300}\x{301}B\x{300}C\x{300}\x{301}X
+ 0: A\x{300}\x{301}B\x{300}C\x{300}\x{301}X
+ 1: X
+    A\x{300}\x{301}B\x{300}C\x{300}\x{301}DA\x{300}X
+ 0: A\x{300}\x{301}B\x{300}C\x{300}\x{301}D
+ 1: D
+    
+/^\X{2,3}?(.)/8
+    A\x{300}\x{301}B\x{300}X
+ 0: A\x{300}\x{301}B\x{300}X
+ 1: X
+    A\x{300}\x{301}B\x{300}C\x{300}\x{301}
+ 0: A\x{300}\x{301}B\x{300}C
+ 1: C
+    A\x{300}\x{301}B\x{300}C\x{300}\x{301}X
+ 0: A\x{300}\x{301}B\x{300}C
+ 1: C
+    A\x{300}\x{301}B\x{300}C\x{300}\x{301}DA\x{300}X
+ 0: A\x{300}\x{301}B\x{300}C
+ 1: C
+    
+/ End of testinput6 /
diff --git a/connectors/jk/native/iis/pcre/ucp.c b/connectors/jk/native/iis/pcre/ucp.c
new file mode 100644
index 0000000..3d69653
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/ucp.c
@@ -0,0 +1,151 @@
+/*************************************************
+*     libucp - Unicode Property Table handler    *
+*************************************************/
+
+/* This function provides a fast way of obtaining the basic Unicode properties
+of a character, using a compact binary tree that occupies less than 100K bytes.
+
+           Copyright (c) 2004 University of Cambridge
+
+-------------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+    * Neither the name of the University of Cambridge nor the names of its
+      contributors may be used to endorse or promote products derived from
+      this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-------------------------------------------------------------------------------
+*/
+
+
+#include "ucp.h"               /* Exported interface */
+#include "ucpinternal.h"       /* Internal table details */
+#include "ucptable.c"          /* The table itself */
+
+
+
+/*************************************************
+*         Search table and return data           *
+*************************************************/
+
+/* Two values are returned: the category is ucp_C, ucp_L, etc. The detailed
+character type is ucp_Lu, ucp_Nd, etc.
+
+Arguments:
+  c           the character value
+  type_ptr    the detailed character type is returned here
+  case_ptr    for letters, the opposite case is returned here, if there
+                is one, else zero
+
+Returns:      the character type category or -1 if not found
+*/
+
+static int
+ucp_findchar(const int c, int *type_ptr, int *case_ptr)
+{
+cnode *node = ucp_table;
+register int cc = c;
+int case_offset;
+
+for (;;)
+  {
+  register int d = node->f1 | ((node->f0 & f0_chhmask) << 16);
+  if (cc == d) break;
+  if (cc < d)
+    {
+    if ((node->f0 & f0_leftexists) == 0) return -1;
+    node ++;
+    }
+  else
+    {
+    register int roffset = (node->f2 & f2_rightmask) >> f2_rightshift;
+    if (roffset == 0) return -1;
+    node += 1 << (roffset - 1);
+    }
+  }
+
+switch ((*type_ptr = ((node->f0 & f0_typemask) >> f0_typeshift)))
+  {
+  case ucp_Cc:
+  case ucp_Cf:
+  case ucp_Cn:
+  case ucp_Co:
+  case ucp_Cs:
+  return ucp_C;
+  break;
+
+  case ucp_Ll:
+  case ucp_Lu:
+  case_offset = node->f2 & f2_casemask;
+  if ((case_offset & 0x0100) != 0) case_offset |= 0xfffff000;
+  *case_ptr = (case_offset == 0)? 0 : cc + case_offset;
+  return ucp_L;
+
+  case ucp_Lm:
+  case ucp_Lo:
+  case ucp_Lt:
+  *case_ptr = 0;
+  return ucp_L;
+  break;
+
+  case ucp_Mc:
+  case ucp_Me:
+  case ucp_Mn:
+  return ucp_M;
+  break;
+
+  case ucp_Nd:
+  case ucp_Nl:
+  case ucp_No:
+  return ucp_N;
+  break;
+
+  case ucp_Pc:
+  case ucp_Pd:
+  case ucp_Pe:
+  case ucp_Pf:
+  case ucp_Pi:
+  case ucp_Ps:
+  case ucp_Po:
+  return ucp_P;
+  break;
+
+  case ucp_Sc:
+  case ucp_Sk:
+  case ucp_Sm:
+  case ucp_So:
+  return ucp_S;
+  break;
+
+  case ucp_Zl:
+  case ucp_Zp:
+  case ucp_Zs:
+  return ucp_Z;
+  break;
+
+  default:         /* "Should never happen" */
+  return -1;
+  break;
+  }
+}
+
+/* End of ucp.c */
diff --git a/connectors/jk/native/iis/pcre/ucp.h b/connectors/jk/native/iis/pcre/ucp.h
new file mode 100644
index 0000000..c013978
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/ucp.h
@@ -0,0 +1,58 @@
+/*************************************************
+*     libucp - Unicode Property Table handler    *
+*************************************************/
+
+/* These are the character categories that are returned by ucp_findchar */
+
+enum {
+  ucp_C,     /* Other */
+  ucp_L,     /* Letter */
+  ucp_M,     /* Mark */
+  ucp_N,     /* Number */
+  ucp_P,     /* Punctuation */
+  ucp_S,     /* Symbol */
+  ucp_Z      /* Separator */
+};
+
+/* These are the detailed character types that are returned by ucp_findchar */
+
+enum {
+  ucp_Cc,    /* Control */
+  ucp_Cf,    /* Format */
+  ucp_Cn,    /* Unassigned */
+  ucp_Co,    /* Private use */
+  ucp_Cs,    /* Surrogate */
+  ucp_Ll,    /* Lower case letter */
+  ucp_Lm,    /* Modifier letter */
+  ucp_Lo,    /* Other letter */
+  ucp_Lt,    /* Title case letter */
+  ucp_Lu,    /* Upper case letter */
+  ucp_Mc,    /* Spacing mark */
+  ucp_Me,    /* Enclosing mark */
+  ucp_Mn,    /* Non-spacing mark */
+  ucp_Nd,    /* Decimal number */
+  ucp_Nl,    /* Letter number */
+  ucp_No,    /* Other number */
+  ucp_Pc,    /* Connector punctuation */
+  ucp_Pd,    /* Dash punctuation */
+  ucp_Pe,    /* Close punctuation */
+  ucp_Pf,    /* Final punctuation */
+  ucp_Pi,    /* Initial punctuation */
+  ucp_Po,    /* Other punctuation */
+  ucp_Ps,    /* Open punctuation */
+  ucp_Sc,    /* Currency symbol */
+  ucp_Sk,    /* Modifier symbol */
+  ucp_Sm,    /* Mathematical symbol */
+  ucp_So,    /* Other symbol */
+  ucp_Zl,    /* Line separator */
+  ucp_Zp,    /* Paragraph separator */
+  ucp_Zs     /* Space separator */
+};
+
+/* For use in PCRE we make this function static so that there is no conflict if
+PCRE is linked with an application that makes use of an external version -
+assuming an external version is ever released... */
+
+static int ucp_findchar(const int, int *, int *);
+
+/* End of ucp.h */
diff --git a/connectors/jk/native/iis/pcre/ucpinternal.h b/connectors/jk/native/iis/pcre/ucpinternal.h
new file mode 100644
index 0000000..faefb03
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/ucpinternal.h
@@ -0,0 +1,91 @@
+/*************************************************
+*     libucp - Unicode Property Table handler    *
+*************************************************/
+
+/* Internal header file defining the layout of compact nodes in the tree. */
+
+typedef struct cnode {
+  unsigned short int f0;
+  unsigned short int f1;
+  unsigned short int f2;
+} cnode;
+
+/* Things for the f0 field */
+
+#define f0_leftexists   0x8000    /* Left child exists */
+#define f0_typemask     0x3f00    /* Type bits */
+#define f0_typeshift         8    /* Type shift */
+#define f0_chhmask      0x00ff    /* Character high bits */
+
+/* Things for the f2 field */
+
+#define f2_rightmask    0xf000    /* Mask for right offset bits */
+#define f2_rightshift       12    /* Shift for right offset */
+#define f2_casemask     0x0fff    /* Mask for case offset */
+
+/* The tree consists of a vector of structures of type cnode, with the root
+node as the first element. The three short ints (16-bits) are used as follows:
+
+(f0) (1) The 0x8000 bit of f0 is set if a left child exists. The child's node
+         is the next node in the vector.
+     (2) The 0x4000 bits of f0 is spare.
+     (3) The 0x3f00 bits of f0 contain the character type; this is a number
+         defined by the enumeration in ucp.h (e.g. ucp_Lu).
+     (4) The bottom 8 bits of f0 contain the most significant byte of the
+         character's 24-bit codepoint.
+
+(f1) (1) The f1 field contains the two least significant bytes of the
+         codepoint.
+
+(f2) (1) The 0xf000 bits of f2 contain zero if there is no right child of this
+         node. Otherwise, they contain one plus the exponent of the power of
+         two of the offset to the right node (e.g. a value of 3 means 8). The
+         units of the offset are node items.
+
+     (2) The 0x0fff bits of f2 contain the signed offset from this character to
+         its alternate cased value. They are zero if there is no such
+         character.
+
+
+-----------------------------------------------------------------------------
+||.|.| type (6) | ms char (8) ||  ls char (16)  ||....|  case offset (12)  ||
+-----------------------------------------------------------------------------
+  | |                                              |
+  | |-> spare                                      |
+  |                                        exponent of right
+  |-> left child exists                       child offset
+
+
+The upper/lower casing information is set only for characters that come in
+pairs. There are (at present) four non-one-to-one mappings in the Unicode data.
+These are ignored. They are:
+
+  1FBE Greek Prosgegrammeni (lower, with upper -> capital iota)
+  2126 Ohm
+  212A Kelvin
+  212B Angstrom
+
+Certainly for the last three, having an alternate case would seem to be a
+mistake. I don't know any Greek, so cannot comment on the first one.
+
+
+When searching the tree, proceed as follows:
+
+(1) Start at the first node.
+
+(2) Extract the character value from f1 and the bottom 8 bits of f0;
+
+(3) Compare with the character being sought. If equal, we are done.
+
+(4) If the test character is smaller, inspect the f0_leftexists flag. If it is
+    not set, the character is not in the tree. If it is set, move to the next
+    node, and go to (2).
+
+(5) If the test character is bigger, extract the f2_rightmask bits from f2, and
+    shift them right by f2_rightshift. If the result is zero, the character is
+    not in the tree. Otherwise, calculate the number of nodes to skip by
+    shifting the value 1 left by this number minus one. Go to (2).
+*/
+
+
+/* End of internal.h */
diff --git a/connectors/jk/native/iis/pcre/ucptable.c b/connectors/jk/native/iis/pcre/ucptable.c
new file mode 100644
index 0000000..7fb3a12
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/ucptable.c
@@ -0,0 +1,15105 @@
+/* This source module is automatically generated from the Unicode
+property table. See internal.h for a description of the layout. */
+
+static cnode ucp_table[] = {
+  { 0x9a00, 0x2f1f, 0xe000 },
+  { 0x8700, 0x1558, 0xd000 },
+  { 0x8700, 0x0a99, 0xc000 },
+  { 0x8500, 0x0435, 0xbfe0 },
+  { 0x8500, 0x01ff, 0xafff },
+  { 0x8500, 0x00ff, 0x9079 },
+  { 0x8000, 0x007f, 0x8000 },
+  { 0x9500, 0x003f, 0x7000 },
+  { 0x8000, 0x001f, 0x6000 },
+  { 0x8000, 0x000f, 0x5000 },
+  { 0x8000, 0x0007, 0x4000 },
+  { 0x8000, 0x0003, 0x3000 },
+  { 0x8000, 0x0001, 0x2000 },
+  { 0x0000, 0x0000, 0x0000 },
+  { 0x0000, 0x0002, 0x0000 },
+  { 0x8000, 0x0005, 0x2000 },
+  { 0x0000, 0x0004, 0x0000 },
+  { 0x0000, 0x0006, 0x0000 },
+  { 0x8000, 0x000b, 0x3000 },
+  { 0x8000, 0x0009, 0x2000 },
+  { 0x0000, 0x0008, 0x0000 },
+  { 0x0000, 0x000a, 0x0000 },
+  { 0x8000, 0x000d, 0x2000 },
+  { 0x0000, 0x000c, 0x0000 },
+  { 0x0000, 0x000e, 0x0000 },
+  { 0x8000, 0x0017, 0x4000 },
+  { 0x8000, 0x0013, 0x3000 },
+  { 0x8000, 0x0011, 0x2000 },
+  { 0x0000, 0x0010, 0x0000 },
+  { 0x0000, 0x0012, 0x0000 },
+  { 0x8000, 0x0015, 0x2000 },
+  { 0x0000, 0x0014, 0x0000 },
+  { 0x0000, 0x0016, 0x0000 },
+  { 0x8000, 0x001b, 0x3000 },
+  { 0x8000, 0x0019, 0x2000 },
+  { 0x0000, 0x0018, 0x0000 },
+  { 0x0000, 0x001a, 0x0000 },
+  { 0x8000, 0x001d, 0x2000 },
+  { 0x0000, 0x001c, 0x0000 },
+  { 0x0000, 0x001e, 0x0000 },
+  { 0x9500, 0x002f, 0x5000 },
+  { 0x9500, 0x0027, 0x4000 },
+  { 0x9500, 0x0023, 0x3000 },
+  { 0x9500, 0x0021, 0x2000 },
+  { 0x1d00, 0x0020, 0x0000 },
+  { 0x1500, 0x0022, 0x0000 },
+  { 0x9500, 0x0025, 0x2000 },
+  { 0x1700, 0x0024, 0x0000 },
+  { 0x1500, 0x0026, 0x0000 },
+  { 0x9900, 0x002b, 0x3000 },
+  { 0x9200, 0x0029, 0x2000 },
+  { 0x1600, 0x0028, 0x0000 },
+  { 0x1500, 0x002a, 0x0000 },
+  { 0x9100, 0x002d, 0x2000 },
+  { 0x1500, 0x002c, 0x0000 },
+  { 0x1500, 0x002e, 0x0000 },
+  { 0x8d00, 0x0037, 0x4000 },
+  { 0x8d00, 0x0033, 0x3000 },
+  { 0x8d00, 0x0031, 0x2000 },
+  { 0x0d00, 0x0030, 0x0000 },
+  { 0x0d00, 0x0032, 0x0000 },
+  { 0x8d00, 0x0035, 0x2000 },
+  { 0x0d00, 0x0034, 0x0000 },
+  { 0x0d00, 0x0036, 0x0000 },
+  { 0x9500, 0x003b, 0x3000 },
+  { 0x8d00, 0x0039, 0x2000 },
+  { 0x0d00, 0x0038, 0x0000 },
+  { 0x1500, 0x003a, 0x0000 },
+  { 0x9900, 0x003d, 0x2000 },
+  { 0x1900, 0x003c, 0x0000 },
+  { 0x1900, 0x003e, 0x0000 },
+  { 0x9000, 0x005f, 0x6000 },
+  { 0x8900, 0x004f, 0x5020 },
+  { 0x8900, 0x0047, 0x4020 },
+  { 0x8900, 0x0043, 0x3020 },
+  { 0x8900, 0x0041, 0x2020 },
+  { 0x1500, 0x0040, 0x0000 },
+  { 0x0900, 0x0042, 0x0020 },
+  { 0x8900, 0x0045, 0x2020 },
+  { 0x0900, 0x0044, 0x0020 },
+  { 0x0900, 0x0046, 0x0020 },
+  { 0x8900, 0x004b, 0x3020 },
+  { 0x8900, 0x0049, 0x2020 },
+  { 0x0900, 0x0048, 0x0020 },
+  { 0x0900, 0x004a, 0x0020 },
+  { 0x8900, 0x004d, 0x2020 },
+  { 0x0900, 0x004c, 0x0020 },
+  { 0x0900, 0x004e, 0x0020 },
+  { 0x8900, 0x0057, 0x4020 },
+  { 0x8900, 0x0053, 0x3020 },
+  { 0x8900, 0x0051, 0x2020 },
+  { 0x0900, 0x0050, 0x0020 },
+  { 0x0900, 0x0052, 0x0020 },
+  { 0x8900, 0x0055, 0x2020 },
+  { 0x0900, 0x0054, 0x0020 },
+  { 0x0900, 0x0056, 0x0020 },
+  { 0x9600, 0x005b, 0x3000 },
+  { 0x8900, 0x0059, 0x2020 },
+  { 0x0900, 0x0058, 0x0020 },
+  { 0x0900, 0x005a, 0x0020 },
+  { 0x9200, 0x005d, 0x2000 },
+  { 0x1500, 0x005c, 0x0000 },
+  { 0x1800, 0x005e, 0x0000 },
+  { 0x8500, 0x006f, 0x5fe0 },
+  { 0x8500, 0x0067, 0x4fe0 },
+  { 0x8500, 0x0063, 0x3fe0 },
+  { 0x8500, 0x0061, 0x2fe0 },
+  { 0x1800, 0x0060, 0x0000 },
+  { 0x0500, 0x0062, 0x0fe0 },
+  { 0x8500, 0x0065, 0x2fe0 },
+  { 0x0500, 0x0064, 0x0fe0 },
+  { 0x0500, 0x0066, 0x0fe0 },
+  { 0x8500, 0x006b, 0x3fe0 },
+  { 0x8500, 0x0069, 0x2fe0 },
+  { 0x0500, 0x0068, 0x0fe0 },
+  { 0x0500, 0x006a, 0x0fe0 },
+  { 0x8500, 0x006d, 0x2fe0 },
+  { 0x0500, 0x006c, 0x0fe0 },
+  { 0x0500, 0x006e, 0x0fe0 },
+  { 0x8500, 0x0077, 0x4fe0 },
+  { 0x8500, 0x0073, 0x3fe0 },
+  { 0x8500, 0x0071, 0x2fe0 },
+  { 0x0500, 0x0070, 0x0fe0 },
+  { 0x0500, 0x0072, 0x0fe0 },
+  { 0x8500, 0x0075, 0x2fe0 },
+  { 0x0500, 0x0074, 0x0fe0 },
+  { 0x0500, 0x0076, 0x0fe0 },
+  { 0x9600, 0x007b, 0x3000 },
+  { 0x8500, 0x0079, 0x2fe0 },
+  { 0x0500, 0x0078, 0x0fe0 },
+  { 0x0500, 0x007a, 0x0fe0 },
+  { 0x9200, 0x007d, 0x2000 },
+  { 0x1900, 0x007c, 0x0000 },
+  { 0x1900, 0x007e, 0x0000 },
+  { 0x9500, 0x00bf, 0x7000 },
+  { 0x8000, 0x009f, 0x6000 },
+  { 0x8000, 0x008f, 0x5000 },
+  { 0x8000, 0x0087, 0x4000 },
+  { 0x8000, 0x0083, 0x3000 },
+  { 0x8000, 0x0081, 0x2000 },
+  { 0x0000, 0x0080, 0x0000 },
+  { 0x0000, 0x0082, 0x0000 },
+  { 0x8000, 0x0085, 0x2000 },
+  { 0x0000, 0x0084, 0x0000 },
+  { 0x0000, 0x0086, 0x0000 },
+  { 0x8000, 0x008b, 0x3000 },
+  { 0x8000, 0x0089, 0x2000 },
+  { 0x0000, 0x0088, 0x0000 },
+  { 0x0000, 0x008a, 0x0000 },
+  { 0x8000, 0x008d, 0x2000 },
+  { 0x0000, 0x008c, 0x0000 },
+  { 0x0000, 0x008e, 0x0000 },
+  { 0x8000, 0x0097, 0x4000 },
+  { 0x8000, 0x0093, 0x3000 },
+  { 0x8000, 0x0091, 0x2000 },
+  { 0x0000, 0x0090, 0x0000 },
+  { 0x0000, 0x0092, 0x0000 },
+  { 0x8000, 0x0095, 0x2000 },
+  { 0x0000, 0x0094, 0x0000 },
+  { 0x0000, 0x0096, 0x0000 },
+  { 0x8000, 0x009b, 0x3000 },
+  { 0x8000, 0x0099, 0x2000 },
+  { 0x0000, 0x0098, 0x0000 },
+  { 0x0000, 0x009a, 0x0000 },
+  { 0x8000, 0x009d, 0x2000 },
+  { 0x0000, 0x009c, 0x0000 },
+  { 0x0000, 0x009e, 0x0000 },
+  { 0x9800, 0x00af, 0x5000 },
+  { 0x9a00, 0x00a7, 0x4000 },
+  { 0x9700, 0x00a3, 0x3000 },
+  { 0x9500, 0x00a1, 0x2000 },
+  { 0x1d00, 0x00a0, 0x0000 },
+  { 0x1700, 0x00a2, 0x0000 },
+  { 0x9700, 0x00a5, 0x2000 },
+  { 0x1700, 0x00a4, 0x0000 },
+  { 0x1a00, 0x00a6, 0x0000 },
+  { 0x9400, 0x00ab, 0x3000 },
+  { 0x9a00, 0x00a9, 0x2000 },
+  { 0x1800, 0x00a8, 0x0000 },
+  { 0x0500, 0x00aa, 0x0000 },
+  { 0x8100, 0x00ad, 0x2000 },
+  { 0x1900, 0x00ac, 0x0000 },
+  { 0x1a00, 0x00ae, 0x0000 },
+  { 0x9500, 0x00b7, 0x4000 },
+  { 0x8f00, 0x00b3, 0x3000 },
+  { 0x9900, 0x00b1, 0x2000 },
+  { 0x1a00, 0x00b0, 0x0000 },
+  { 0x0f00, 0x00b2, 0x0000 },
+  { 0x8500, 0x00b5, 0x22e7 },
+  { 0x1800, 0x00b4, 0x0000 },
+  { 0x1a00, 0x00b6, 0x0000 },
+  { 0x9300, 0x00bb, 0x3000 },
+  { 0x8f00, 0x00b9, 0x2000 },
+  { 0x1800, 0x00b8, 0x0000 },
+  { 0x0500, 0x00ba, 0x0000 },
+  { 0x8f00, 0x00bd, 0x2000 },
+  { 0x0f00, 0x00bc, 0x0000 },
+  { 0x0f00, 0x00be, 0x0000 },
+  { 0x8500, 0x00df, 0x6000 },
+  { 0x8900, 0x00cf, 0x5020 },
+  { 0x8900, 0x00c7, 0x4020 },
+  { 0x8900, 0x00c3, 0x3020 },
+  { 0x8900, 0x00c1, 0x2020 },
+  { 0x0900, 0x00c0, 0x0020 },
+  { 0x0900, 0x00c2, 0x0020 },
+  { 0x8900, 0x00c5, 0x2020 },
+  { 0x0900, 0x00c4, 0x0020 },
+  { 0x0900, 0x00c6, 0x0020 },
+  { 0x8900, 0x00cb, 0x3020 },
+  { 0x8900, 0x00c9, 0x2020 },
+  { 0x0900, 0x00c8, 0x0020 },
+  { 0x0900, 0x00ca, 0x0020 },
+  { 0x8900, 0x00cd, 0x2020 },
+  { 0x0900, 0x00cc, 0x0020 },
+  { 0x0900, 0x00ce, 0x0020 },
+  { 0x9900, 0x00d7, 0x4000 },
+  { 0x8900, 0x00d3, 0x3020 },
+  { 0x8900, 0x00d1, 0x2020 },
+  { 0x0900, 0x00d0, 0x0020 },
+  { 0x0900, 0x00d2, 0x0020 },
+  { 0x8900, 0x00d5, 0x2020 },
+  { 0x0900, 0x00d4, 0x0020 },
+  { 0x0900, 0x00d6, 0x0020 },
+  { 0x8900, 0x00db, 0x3020 },
+  { 0x8900, 0x00d9, 0x2020 },
+  { 0x0900, 0x00d8, 0x0020 },
+  { 0x0900, 0x00da, 0x0020 },
+  { 0x8900, 0x00dd, 0x2020 },
+  { 0x0900, 0x00dc, 0x0020 },
+  { 0x0900, 0x00de, 0x0020 },
+  { 0x8500, 0x00ef, 0x5fe0 },
+  { 0x8500, 0x00e7, 0x4fe0 },
+  { 0x8500, 0x00e3, 0x3fe0 },
+  { 0x8500, 0x00e1, 0x2fe0 },
+  { 0x0500, 0x00e0, 0x0fe0 },
+  { 0x0500, 0x00e2, 0x0fe0 },
+  { 0x8500, 0x00e5, 0x2fe0 },
+  { 0x0500, 0x00e4, 0x0fe0 },
+  { 0x0500, 0x00e6, 0x0fe0 },
+  { 0x8500, 0x00eb, 0x3fe0 },
+  { 0x8500, 0x00e9, 0x2fe0 },
+  { 0x0500, 0x00e8, 0x0fe0 },
+  { 0x0500, 0x00ea, 0x0fe0 },
+  { 0x8500, 0x00ed, 0x2fe0 },
+  { 0x0500, 0x00ec, 0x0fe0 },
+  { 0x0500, 0x00ee, 0x0fe0 },
+  { 0x9900, 0x00f7, 0x4000 },
+  { 0x8500, 0x00f3, 0x3fe0 },
+  { 0x8500, 0x00f1, 0x2fe0 },
+  { 0x0500, 0x00f0, 0x0fe0 },
+  { 0x0500, 0x00f2, 0x0fe0 },
+  { 0x8500, 0x00f5, 0x2fe0 },
+  { 0x0500, 0x00f4, 0x0fe0 },
+  { 0x0500, 0x00f6, 0x0fe0 },
+  { 0x8500, 0x00fb, 0x3fe0 },
+  { 0x8500, 0x00f9, 0x2fe0 },
+  { 0x0500, 0x00f8, 0x0fe0 },
+  { 0x0500, 0x00fa, 0x0fe0 },
+  { 0x8500, 0x00fd, 0x2fe0 },
+  { 0x0500, 0x00fc, 0x0fe0 },
+  { 0x0500, 0x00fe, 0x0fe0 },
+  { 0x8500, 0x017f, 0x8ed4 },
+  { 0x8900, 0x013f, 0x7001 },
+  { 0x8500, 0x011f, 0x6fff },
+  { 0x8500, 0x010f, 0x5fff },
+  { 0x8500, 0x0107, 0x4fff },
+  { 0x8500, 0x0103, 0x3fff },
+  { 0x8500, 0x0101, 0x2fff },
+  { 0x0900, 0x0100, 0x0001 },
+  { 0x0900, 0x0102, 0x0001 },
+  { 0x8500, 0x0105, 0x2fff },
+  { 0x0900, 0x0104, 0x0001 },
+  { 0x0900, 0x0106, 0x0001 },
+  { 0x8500, 0x010b, 0x3fff },
+  { 0x8500, 0x0109, 0x2fff },
+  { 0x0900, 0x0108, 0x0001 },
+  { 0x0900, 0x010a, 0x0001 },
+  { 0x8500, 0x010d, 0x2fff },
+  { 0x0900, 0x010c, 0x0001 },
+  { 0x0900, 0x010e, 0x0001 },
+  { 0x8500, 0x0117, 0x4fff },
+  { 0x8500, 0x0113, 0x3fff },
+  { 0x8500, 0x0111, 0x2fff },
+  { 0x0900, 0x0110, 0x0001 },
+  { 0x0900, 0x0112, 0x0001 },
+  { 0x8500, 0x0115, 0x2fff },
+  { 0x0900, 0x0114, 0x0001 },
+  { 0x0900, 0x0116, 0x0001 },
+  { 0x8500, 0x011b, 0x3fff },
+  { 0x8500, 0x0119, 0x2fff },
+  { 0x0900, 0x0118, 0x0001 },
+  { 0x0900, 0x011a, 0x0001 },
+  { 0x8500, 0x011d, 0x2fff },
+  { 0x0900, 0x011c, 0x0001 },
+  { 0x0900, 0x011e, 0x0001 },
+  { 0x8500, 0x012f, 0x5fff },
+  { 0x8500, 0x0127, 0x4fff },
+  { 0x8500, 0x0123, 0x3fff },
+  { 0x8500, 0x0121, 0x2fff },
+  { 0x0900, 0x0120, 0x0001 },
+  { 0x0900, 0x0122, 0x0001 },
+  { 0x8500, 0x0125, 0x2fff },
+  { 0x0900, 0x0124, 0x0001 },
+  { 0x0900, 0x0126, 0x0001 },
+  { 0x8500, 0x012b, 0x3fff },
+  { 0x8500, 0x0129, 0x2fff },
+  { 0x0900, 0x0128, 0x0001 },
+  { 0x0900, 0x012a, 0x0001 },
+  { 0x8500, 0x012d, 0x2fff },
+  { 0x0900, 0x012c, 0x0001 },
+  { 0x0900, 0x012e, 0x0001 },
+  { 0x8500, 0x0137, 0x4fff },
+  { 0x8500, 0x0133, 0x3fff },
+  { 0x8500, 0x0131, 0x2f18 },
+  { 0x0900, 0x0130, 0x0f39 },
+  { 0x0900, 0x0132, 0x0001 },
+  { 0x8500, 0x0135, 0x2fff },
+  { 0x0900, 0x0134, 0x0001 },
+  { 0x0900, 0x0136, 0x0001 },
+  { 0x8900, 0x013b, 0x3001 },
+  { 0x8900, 0x0139, 0x2001 },
+  { 0x0500, 0x0138, 0x0000 },
+  { 0x0500, 0x013a, 0x0fff },
+  { 0x8900, 0x013d, 0x2001 },
+  { 0x0500, 0x013c, 0x0fff },
+  { 0x0500, 0x013e, 0x0fff },
+  { 0x8500, 0x015f, 0x6fff },
+  { 0x8500, 0x014f, 0x5fff },
+  { 0x8900, 0x0147, 0x4001 },
+  { 0x8900, 0x0143, 0x3001 },
+  { 0x8900, 0x0141, 0x2001 },
+  { 0x0500, 0x0140, 0x0fff },
+  { 0x0500, 0x0142, 0x0fff },
+  { 0x8900, 0x0145, 0x2001 },
+  { 0x0500, 0x0144, 0x0fff },
+  { 0x0500, 0x0146, 0x0fff },
+  { 0x8500, 0x014b, 0x3fff },
+  { 0x8500, 0x0149, 0x2000 },
+  { 0x0500, 0x0148, 0x0fff },
+  { 0x0900, 0x014a, 0x0001 },
+  { 0x8500, 0x014d, 0x2fff },
+  { 0x0900, 0x014c, 0x0001 },
+  { 0x0900, 0x014e, 0x0001 },
+  { 0x8500, 0x0157, 0x4fff },
+  { 0x8500, 0x0153, 0x3fff },
+  { 0x8500, 0x0151, 0x2fff },
+  { 0x0900, 0x0150, 0x0001 },
+  { 0x0900, 0x0152, 0x0001 },
+  { 0x8500, 0x0155, 0x2fff },
+  { 0x0900, 0x0154, 0x0001 },
+  { 0x0900, 0x0156, 0x0001 },
+  { 0x8500, 0x015b, 0x3fff },
+  { 0x8500, 0x0159, 0x2fff },
+  { 0x0900, 0x0158, 0x0001 },
+  { 0x0900, 0x015a, 0x0001 },
+  { 0x8500, 0x015d, 0x2fff },
+  { 0x0900, 0x015c, 0x0001 },
+  { 0x0900, 0x015e, 0x0001 },
+  { 0x8500, 0x016f, 0x5fff },
+  { 0x8500, 0x0167, 0x4fff },
+  { 0x8500, 0x0163, 0x3fff },
+  { 0x8500, 0x0161, 0x2fff },
+  { 0x0900, 0x0160, 0x0001 },
+  { 0x0900, 0x0162, 0x0001 },
+  { 0x8500, 0x0165, 0x2fff },
+  { 0x0900, 0x0164, 0x0001 },
+  { 0x0900, 0x0166, 0x0001 },
+  { 0x8500, 0x016b, 0x3fff },
+  { 0x8500, 0x0169, 0x2fff },
+  { 0x0900, 0x0168, 0x0001 },
+  { 0x0900, 0x016a, 0x0001 },
+  { 0x8500, 0x016d, 0x2fff },
+  { 0x0900, 0x016c, 0x0001 },
+  { 0x0900, 0x016e, 0x0001 },
+  { 0x8500, 0x0177, 0x4fff },
+  { 0x8500, 0x0173, 0x3fff },
+  { 0x8500, 0x0171, 0x2fff },
+  { 0x0900, 0x0170, 0x0001 },
+  { 0x0900, 0x0172, 0x0001 },
+  { 0x8500, 0x0175, 0x2fff },
+  { 0x0900, 0x0174, 0x0001 },
+  { 0x0900, 0x0176, 0x0001 },
+  { 0x8900, 0x017b, 0x3001 },
+  { 0x8900, 0x0179, 0x2001 },
+  { 0x0900, 0x0178, 0x0f87 },
+  { 0x0500, 0x017a, 0x0fff },
+  { 0x8900, 0x017d, 0x2001 },
+  { 0x0500, 0x017c, 0x0fff },
+  { 0x0500, 0x017e, 0x0fff },
+  { 0x8500, 0x01bf, 0x7038 },
+  { 0x8900, 0x019f, 0x60d6 },
+  { 0x8900, 0x018f, 0x50ca },
+  { 0x8900, 0x0187, 0x4001 },
+  { 0x8500, 0x0183, 0x3fff },
+  { 0x8900, 0x0181, 0x20d2 },
+  { 0x0500, 0x0180, 0x0000 },
+  { 0x0900, 0x0182, 0x0001 },
+  { 0x8500, 0x0185, 0x2fff },
+  { 0x0900, 0x0184, 0x0001 },
+  { 0x0900, 0x0186, 0x00ce },
+  { 0x8900, 0x018b, 0x3001 },
+  { 0x8900, 0x0189, 0x20cd },
+  { 0x0500, 0x0188, 0x0fff },
+  { 0x0900, 0x018a, 0x00cd },
+  { 0x8500, 0x018d, 0x2000 },
+  { 0x0500, 0x018c, 0x0fff },
+  { 0x0900, 0x018e, 0x004f },
+  { 0x8900, 0x0197, 0x40d1 },
+  { 0x8900, 0x0193, 0x30cd },
+  { 0x8900, 0x0191, 0x2001 },
+  { 0x0900, 0x0190, 0x00cb },
+  { 0x0500, 0x0192, 0x0fff },
+  { 0x8500, 0x0195, 0x2061 },
+  { 0x0900, 0x0194, 0x00cf },
+  { 0x0900, 0x0196, 0x00d3 },
+  { 0x8500, 0x019b, 0x3000 },
+  { 0x8500, 0x0199, 0x2fff },
+  { 0x0900, 0x0198, 0x0001 },
+  { 0x0500, 0x019a, 0x0000 },
+  { 0x8900, 0x019d, 0x20d5 },
+  { 0x0900, 0x019c, 0x00d3 },
+  { 0x0500, 0x019e, 0x0082 },
+  { 0x8900, 0x01af, 0x5001 },
+  { 0x8900, 0x01a7, 0x4001 },
+  { 0x8500, 0x01a3, 0x3fff },
+  { 0x8500, 0x01a1, 0x2fff },
+  { 0x0900, 0x01a0, 0x0001 },
+  { 0x0900, 0x01a2, 0x0001 },
+  { 0x8500, 0x01a5, 0x2fff },
+  { 0x0900, 0x01a4, 0x0001 },
+  { 0x0900, 0x01a6, 0x00da },
+  { 0x8500, 0x01ab, 0x3000 },
+  { 0x8900, 0x01a9, 0x20da },
+  { 0x0500, 0x01a8, 0x0fff },
+  { 0x0500, 0x01aa, 0x0000 },
+  { 0x8500, 0x01ad, 0x2fff },
+  { 0x0900, 0x01ac, 0x0001 },
+  { 0x0900, 0x01ae, 0x00da },
+  { 0x8900, 0x01b7, 0x40db },
+  { 0x8900, 0x01b3, 0x3001 },
+  { 0x8900, 0x01b1, 0x20d9 },
+  { 0x0500, 0x01b0, 0x0fff },
+  { 0x0900, 0x01b2, 0x00d9 },
+  { 0x8900, 0x01b5, 0x2001 },
+  { 0x0500, 0x01b4, 0x0fff },
+  { 0x0500, 0x01b6, 0x0fff },
+  { 0x8700, 0x01bb, 0x3000 },
+  { 0x8500, 0x01b9, 0x2fff },
+  { 0x0900, 0x01b8, 0x0001 },
+  { 0x0500, 0x01ba, 0x0000 },
+  { 0x8500, 0x01bd, 0x2fff },
+  { 0x0900, 0x01bc, 0x0001 },
+  { 0x0500, 0x01be, 0x0000 },
+  { 0x8500, 0x01df, 0x6fff },
+  { 0x8900, 0x01cf, 0x5001 },
+  { 0x8900, 0x01c7, 0x4002 },
+  { 0x8700, 0x01c3, 0x3000 },
+  { 0x8700, 0x01c1, 0x2000 },
+  { 0x0700, 0x01c0, 0x0000 },
+  { 0x0700, 0x01c2, 0x0000 },
+  { 0x8800, 0x01c5, 0x2000 },
+  { 0x0900, 0x01c4, 0x0002 },
+  { 0x0500, 0x01c6, 0x0ffe },
+  { 0x8800, 0x01cb, 0x3000 },
+  { 0x8500, 0x01c9, 0x2ffe },
+  { 0x0800, 0x01c8, 0x0000 },
+  { 0x0900, 0x01ca, 0x0002 },
+  { 0x8900, 0x01cd, 0x2001 },
+  { 0x0500, 0x01cc, 0x0ffe },
+  { 0x0500, 0x01ce, 0x0fff },
+  { 0x8900, 0x01d7, 0x4001 },
+  { 0x8900, 0x01d3, 0x3001 },
+  { 0x8900, 0x01d1, 0x2001 },
+  { 0x0500, 0x01d0, 0x0fff },
+  { 0x0500, 0x01d2, 0x0fff },
+  { 0x8900, 0x01d5, 0x2001 },
+  { 0x0500, 0x01d4, 0x0fff },
+  { 0x0500, 0x01d6, 0x0fff },
+  { 0x8900, 0x01db, 0x3001 },
+  { 0x8900, 0x01d9, 0x2001 },
+  { 0x0500, 0x01d8, 0x0fff },
+  { 0x0500, 0x01da, 0x0fff },
+  { 0x8500, 0x01dd, 0x2fb1 },
+  { 0x0500, 0x01dc, 0x0fff },
+  { 0x0900, 0x01de, 0x0001 },
+  { 0x8500, 0x01ef, 0x5fff },
+  { 0x8500, 0x01e7, 0x4fff },
+  { 0x8500, 0x01e3, 0x3fff },
+  { 0x8500, 0x01e1, 0x2fff },
+  { 0x0900, 0x01e0, 0x0001 },
+  { 0x0900, 0x01e2, 0x0001 },
+  { 0x8500, 0x01e5, 0x2fff },
+  { 0x0900, 0x01e4, 0x0001 },
+  { 0x0900, 0x01e6, 0x0001 },
+  { 0x8500, 0x01eb, 0x3fff },
+  { 0x8500, 0x01e9, 0x2fff },
+  { 0x0900, 0x01e8, 0x0001 },
+  { 0x0900, 0x01ea, 0x0001 },
+  { 0x8500, 0x01ed, 0x2fff },
+  { 0x0900, 0x01ec, 0x0001 },
+  { 0x0900, 0x01ee, 0x0001 },
+  { 0x8900, 0x01f7, 0x4fc8 },
+  { 0x8500, 0x01f3, 0x3ffe },
+  { 0x8900, 0x01f1, 0x2002 },
+  { 0x0500, 0x01f0, 0x0000 },
+  { 0x0800, 0x01f2, 0x0000 },
+  { 0x8500, 0x01f5, 0x2fff },
+  { 0x0900, 0x01f4, 0x0001 },
+  { 0x0900, 0x01f6, 0x0f9f },
+  { 0x8500, 0x01fb, 0x3fff },
+  { 0x8500, 0x01f9, 0x2fff },
+  { 0x0900, 0x01f8, 0x0001 },
+  { 0x0900, 0x01fa, 0x0001 },
+  { 0x8500, 0x01fd, 0x2fff },
+  { 0x0900, 0x01fc, 0x0001 },
+  { 0x0900, 0x01fe, 0x0001 },
+  { 0x8c00, 0x0318, 0x9000 },
+  { 0x8500, 0x0298, 0x8000 },
+  { 0x8500, 0x0258, 0x7000 },
+  { 0x8500, 0x021f, 0x6fff },
+  { 0x8500, 0x020f, 0x5fff },
+  { 0x8500, 0x0207, 0x4fff },
+  { 0x8500, 0x0203, 0x3fff },
+  { 0x8500, 0x0201, 0x2fff },
+  { 0x0900, 0x0200, 0x0001 },
+  { 0x0900, 0x0202, 0x0001 },
+  { 0x8500, 0x0205, 0x2fff },
+  { 0x0900, 0x0204, 0x0001 },
+  { 0x0900, 0x0206, 0x0001 },
+  { 0x8500, 0x020b, 0x3fff },
+  { 0x8500, 0x0209, 0x2fff },
+  { 0x0900, 0x0208, 0x0001 },
+  { 0x0900, 0x020a, 0x0001 },
+  { 0x8500, 0x020d, 0x2fff },
+  { 0x0900, 0x020c, 0x0001 },
+  { 0x0900, 0x020e, 0x0001 },
+  { 0x8500, 0x0217, 0x4fff },
+  { 0x8500, 0x0213, 0x3fff },
+  { 0x8500, 0x0211, 0x2fff },
+  { 0x0900, 0x0210, 0x0001 },
+  { 0x0900, 0x0212, 0x0001 },
+  { 0x8500, 0x0215, 0x2fff },
+  { 0x0900, 0x0214, 0x0001 },
+  { 0x0900, 0x0216, 0x0001 },
+  { 0x8500, 0x021b, 0x3fff },
+  { 0x8500, 0x0219, 0x2fff },
+  { 0x0900, 0x0218, 0x0001 },
+  { 0x0900, 0x021a, 0x0001 },
+  { 0x8500, 0x021d, 0x2fff },
+  { 0x0900, 0x021c, 0x0001 },
+  { 0x0900, 0x021e, 0x0001 },
+  { 0x8500, 0x022f, 0x5fff },
+  { 0x8500, 0x0227, 0x4fff },
+  { 0x8500, 0x0223, 0x3fff },
+  { 0x8500, 0x0221, 0x2000 },
+  { 0x0900, 0x0220, 0x0f7e },
+  { 0x0900, 0x0222, 0x0001 },
+  { 0x8500, 0x0225, 0x2fff },
+  { 0x0900, 0x0224, 0x0001 },
+  { 0x0900, 0x0226, 0x0001 },
+  { 0x8500, 0x022b, 0x3fff },
+  { 0x8500, 0x0229, 0x2fff },
+  { 0x0900, 0x0228, 0x0001 },
+  { 0x0900, 0x022a, 0x0001 },
+  { 0x8500, 0x022d, 0x2fff },
+  { 0x0900, 0x022c, 0x0001 },
+  { 0x0900, 0x022e, 0x0001 },
+  { 0x8500, 0x0250, 0x4000 },
+  { 0x8500, 0x0233, 0x3fff },
+  { 0x8500, 0x0231, 0x2fff },
+  { 0x0900, 0x0230, 0x0001 },
+  { 0x0900, 0x0232, 0x0001 },
+  { 0x8500, 0x0235, 0x2000 },
+  { 0x0500, 0x0234, 0x0000 },
+  { 0x0500, 0x0236, 0x0000 },
+  { 0x8500, 0x0254, 0x3f32 },
+  { 0x8500, 0x0252, 0x2000 },
+  { 0x0500, 0x0251, 0x0000 },
+  { 0x0500, 0x0253, 0x0f2e },
+  { 0x8500, 0x0256, 0x2f33 },
+  { 0x0500, 0x0255, 0x0000 },
+  { 0x0500, 0x0257, 0x0f33 },
+  { 0x8500, 0x0278, 0x6000 },
+  { 0x8500, 0x0268, 0x5f2f },
+  { 0x8500, 0x0260, 0x4f33 },
+  { 0x8500, 0x025c, 0x3000 },
+  { 0x8500, 0x025a, 0x2000 },
+  { 0x0500, 0x0259, 0x0f36 },
+  { 0x0500, 0x025b, 0x0f35 },
+  { 0x8500, 0x025e, 0x2000 },
+  { 0x0500, 0x025d, 0x0000 },
+  { 0x0500, 0x025f, 0x0000 },
+  { 0x8500, 0x0264, 0x3000 },
+  { 0x8500, 0x0262, 0x2000 },
+  { 0x0500, 0x0261, 0x0000 },
+  { 0x0500, 0x0263, 0x0f31 },
+  { 0x8500, 0x0266, 0x2000 },
+  { 0x0500, 0x0265, 0x0000 },
+  { 0x0500, 0x0267, 0x0000 },
+  { 0x8500, 0x0270, 0x4000 },
+  { 0x8500, 0x026c, 0x3000 },
+  { 0x8500, 0x026a, 0x2000 },
+  { 0x0500, 0x0269, 0x0f2d },
+  { 0x0500, 0x026b, 0x0000 },
+  { 0x8500, 0x026e, 0x2000 },
+  { 0x0500, 0x026d, 0x0000 },
+  { 0x0500, 0x026f, 0x0f2d },
+  { 0x8500, 0x0274, 0x3000 },
+  { 0x8500, 0x0272, 0x2f2b },
+  { 0x0500, 0x0271, 0x0000 },
+  { 0x0500, 0x0273, 0x0000 },
+  { 0x8500, 0x0276, 0x2000 },
+  { 0x0500, 0x0275, 0x0f2a },
+  { 0x0500, 0x0277, 0x0000 },
+  { 0x8500, 0x0288, 0x5f26 },
+  { 0x8500, 0x0280, 0x4f26 },
+  { 0x8500, 0x027c, 0x3000 },
+  { 0x8500, 0x027a, 0x2000 },
+  { 0x0500, 0x0279, 0x0000 },
+  { 0x0500, 0x027b, 0x0000 },
+  { 0x8500, 0x027e, 0x2000 },
+  { 0x0500, 0x027d, 0x0000 },
+  { 0x0500, 0x027f, 0x0000 },
+  { 0x8500, 0x0284, 0x3000 },
+  { 0x8500, 0x0282, 0x2000 },
+  { 0x0500, 0x0281, 0x0000 },
+  { 0x0500, 0x0283, 0x0f26 },
+  { 0x8500, 0x0286, 0x2000 },
+  { 0x0500, 0x0285, 0x0000 },
+  { 0x0500, 0x0287, 0x0000 },
+  { 0x8500, 0x0290, 0x4000 },
+  { 0x8500, 0x028c, 0x3000 },
+  { 0x8500, 0x028a, 0x2f27 },
+  { 0x0500, 0x0289, 0x0000 },
+  { 0x0500, 0x028b, 0x0f27 },
+  { 0x8500, 0x028e, 0x2000 },
+  { 0x0500, 0x028d, 0x0000 },
+  { 0x0500, 0x028f, 0x0000 },
+  { 0x8500, 0x0294, 0x3000 },
+  { 0x8500, 0x0292, 0x2f25 },
+  { 0x0500, 0x0291, 0x0000 },
+  { 0x0500, 0x0293, 0x0000 },
+  { 0x8500, 0x0296, 0x2000 },
+  { 0x0500, 0x0295, 0x0000 },
+  { 0x0500, 0x0297, 0x0000 },
+  { 0x9800, 0x02d8, 0x7000 },
+  { 0x8600, 0x02b8, 0x6000 },
+  { 0x8500, 0x02a8, 0x5000 },
+  { 0x8500, 0x02a0, 0x4000 },
+  { 0x8500, 0x029c, 0x3000 },
+  { 0x8500, 0x029a, 0x2000 },
+  { 0x0500, 0x0299, 0x0000 },
+  { 0x0500, 0x029b, 0x0000 },
+  { 0x8500, 0x029e, 0x2000 },
+  { 0x0500, 0x029d, 0x0000 },
+  { 0x0500, 0x029f, 0x0000 },
+  { 0x8500, 0x02a4, 0x3000 },
+  { 0x8500, 0x02a2, 0x2000 },
+  { 0x0500, 0x02a1, 0x0000 },
+  { 0x0500, 0x02a3, 0x0000 },
+  { 0x8500, 0x02a6, 0x2000 },
+  { 0x0500, 0x02a5, 0x0000 },
+  { 0x0500, 0x02a7, 0x0000 },
+  { 0x8600, 0x02b0, 0x4000 },
+  { 0x8500, 0x02ac, 0x3000 },
+  { 0x8500, 0x02aa, 0x2000 },
+  { 0x0500, 0x02a9, 0x0000 },
+  { 0x0500, 0x02ab, 0x0000 },
+  { 0x8500, 0x02ae, 0x2000 },
+  { 0x0500, 0x02ad, 0x0000 },
+  { 0x0500, 0x02af, 0x0000 },
+  { 0x8600, 0x02b4, 0x3000 },
+  { 0x8600, 0x02b2, 0x2000 },
+  { 0x0600, 0x02b1, 0x0000 },
+  { 0x0600, 0x02b3, 0x0000 },
+  { 0x8600, 0x02b6, 0x2000 },
+  { 0x0600, 0x02b5, 0x0000 },
+  { 0x0600, 0x02b7, 0x0000 },
+  { 0x8600, 0x02c8, 0x5000 },
+  { 0x8600, 0x02c0, 0x4000 },
+  { 0x8600, 0x02bc, 0x3000 },
+  { 0x8600, 0x02ba, 0x2000 },
+  { 0x0600, 0x02b9, 0x0000 },
+  { 0x0600, 0x02bb, 0x0000 },
+  { 0x8600, 0x02be, 0x2000 },
+  { 0x0600, 0x02bd, 0x0000 },
+  { 0x0600, 0x02bf, 0x0000 },
+  { 0x9800, 0x02c4, 0x3000 },
+  { 0x9800, 0x02c2, 0x2000 },
+  { 0x0600, 0x02c1, 0x0000 },
+  { 0x1800, 0x02c3, 0x0000 },
+  { 0x8600, 0x02c6, 0x2000 },
+  { 0x1800, 0x02c5, 0x0000 },
+  { 0x0600, 0x02c7, 0x0000 },
+  { 0x8600, 0x02d0, 0x4000 },
+  { 0x8600, 0x02cc, 0x3000 },
+  { 0x8600, 0x02ca, 0x2000 },
+  { 0x0600, 0x02c9, 0x0000 },
+  { 0x0600, 0x02cb, 0x0000 },
+  { 0x8600, 0x02ce, 0x2000 },
+  { 0x0600, 0x02cd, 0x0000 },
+  { 0x0600, 0x02cf, 0x0000 },
+  { 0x9800, 0x02d4, 0x3000 },
+  { 0x9800, 0x02d2, 0x2000 },
+  { 0x0600, 0x02d1, 0x0000 },
+  { 0x1800, 0x02d3, 0x0000 },
+  { 0x9800, 0x02d6, 0x2000 },
+  { 0x1800, 0x02d5, 0x0000 },
+  { 0x1800, 0x02d7, 0x0000 },
+  { 0x9800, 0x02f8, 0x6000 },
+  { 0x9800, 0x02e8, 0x5000 },
+  { 0x8600, 0x02e0, 0x4000 },
+  { 0x9800, 0x02dc, 0x3000 },
+  { 0x9800, 0x02da, 0x2000 },
+  { 0x1800, 0x02d9, 0x0000 },
+  { 0x1800, 0x02db, 0x0000 },
+  { 0x9800, 0x02de, 0x2000 },
+  { 0x1800, 0x02dd, 0x0000 },
+  { 0x1800, 0x02df, 0x0000 },
+  { 0x8600, 0x02e4, 0x3000 },
+  { 0x8600, 0x02e2, 0x2000 },
+  { 0x0600, 0x02e1, 0x0000 },
+  { 0x0600, 0x02e3, 0x0000 },
+  { 0x9800, 0x02e6, 0x2000 },
+  { 0x1800, 0x02e5, 0x0000 },
+  { 0x1800, 0x02e7, 0x0000 },
+  { 0x9800, 0x02f0, 0x4000 },
+  { 0x9800, 0x02ec, 0x3000 },
+  { 0x9800, 0x02ea, 0x2000 },
+  { 0x1800, 0x02e9, 0x0000 },
+  { 0x1800, 0x02eb, 0x0000 },
+  { 0x8600, 0x02ee, 0x2000 },
+  { 0x1800, 0x02ed, 0x0000 },
+  { 0x1800, 0x02ef, 0x0000 },
+  { 0x9800, 0x02f4, 0x3000 },
+  { 0x9800, 0x02f2, 0x2000 },
+  { 0x1800, 0x02f1, 0x0000 },
+  { 0x1800, 0x02f3, 0x0000 },
+  { 0x9800, 0x02f6, 0x2000 },
+  { 0x1800, 0x02f5, 0x0000 },
+  { 0x1800, 0x02f7, 0x0000 },
+  { 0x8c00, 0x0308, 0x5000 },
+  { 0x8c00, 0x0300, 0x4000 },
+  { 0x9800, 0x02fc, 0x3000 },
+  { 0x9800, 0x02fa, 0x2000 },
+  { 0x1800, 0x02f9, 0x0000 },
+  { 0x1800, 0x02fb, 0x0000 },
+  { 0x9800, 0x02fe, 0x2000 },
+  { 0x1800, 0x02fd, 0x0000 },
+  { 0x1800, 0x02ff, 0x0000 },
+  { 0x8c00, 0x0304, 0x3000 },
+  { 0x8c00, 0x0302, 0x2000 },
+  { 0x0c00, 0x0301, 0x0000 },
+  { 0x0c00, 0x0303, 0x0000 },
+  { 0x8c00, 0x0306, 0x2000 },
+  { 0x0c00, 0x0305, 0x0000 },
+  { 0x0c00, 0x0307, 0x0000 },
+  { 0x8c00, 0x0310, 0x4000 },
+  { 0x8c00, 0x030c, 0x3000 },
+  { 0x8c00, 0x030a, 0x2000 },
+  { 0x0c00, 0x0309, 0x0000 },
+  { 0x0c00, 0x030b, 0x0000 },
+  { 0x8c00, 0x030e, 0x2000 },
+  { 0x0c00, 0x030d, 0x0000 },
+  { 0x0c00, 0x030f, 0x0000 },
+  { 0x8c00, 0x0314, 0x3000 },
+  { 0x8c00, 0x0312, 0x2000 },
+  { 0x0c00, 0x0311, 0x0000 },
+  { 0x0c00, 0x0313, 0x0000 },
+  { 0x8c00, 0x0316, 0x2000 },
+  { 0x0c00, 0x0315, 0x0000 },
+  { 0x0c00, 0x0317, 0x0000 },
+  { 0x8500, 0x03b0, 0x8000 },
+  { 0x8c00, 0x035d, 0x7000 },
+  { 0x8c00, 0x0338, 0x6000 },
+  { 0x8c00, 0x0328, 0x5000 },
+  { 0x8c00, 0x0320, 0x4000 },
+  { 0x8c00, 0x031c, 0x3000 },
+  { 0x8c00, 0x031a, 0x2000 },
+  { 0x0c00, 0x0319, 0x0000 },
+  { 0x0c00, 0x031b, 0x0000 },
+  { 0x8c00, 0x031e, 0x2000 },
+  { 0x0c00, 0x031d, 0x0000 },
+  { 0x0c00, 0x031f, 0x0000 },
+  { 0x8c00, 0x0324, 0x3000 },
+  { 0x8c00, 0x0322, 0x2000 },
+  { 0x0c00, 0x0321, 0x0000 },
+  { 0x0c00, 0x0323, 0x0000 },
+  { 0x8c00, 0x0326, 0x2000 },
+  { 0x0c00, 0x0325, 0x0000 },
+  { 0x0c00, 0x0327, 0x0000 },
+  { 0x8c00, 0x0330, 0x4000 },
+  { 0x8c00, 0x032c, 0x3000 },
+  { 0x8c00, 0x032a, 0x2000 },
+  { 0x0c00, 0x0329, 0x0000 },
+  { 0x0c00, 0x032b, 0x0000 },
+  { 0x8c00, 0x032e, 0x2000 },
+  { 0x0c00, 0x032d, 0x0000 },
+  { 0x0c00, 0x032f, 0x0000 },
+  { 0x8c00, 0x0334, 0x3000 },
+  { 0x8c00, 0x0332, 0x2000 },
+  { 0x0c00, 0x0331, 0x0000 },
+  { 0x0c00, 0x0333, 0x0000 },
+  { 0x8c00, 0x0336, 0x2000 },
+  { 0x0c00, 0x0335, 0x0000 },
+  { 0x0c00, 0x0337, 0x0000 },
+  { 0x8c00, 0x0348, 0x5000 },
+  { 0x8c00, 0x0340, 0x4000 },
+  { 0x8c00, 0x033c, 0x3000 },
+  { 0x8c00, 0x033a, 0x2000 },
+  { 0x0c00, 0x0339, 0x0000 },
+  { 0x0c00, 0x033b, 0x0000 },
+  { 0x8c00, 0x033e, 0x2000 },
+  { 0x0c00, 0x033d, 0x0000 },
+  { 0x0c00, 0x033f, 0x0000 },
+  { 0x8c00, 0x0344, 0x3000 },
+  { 0x8c00, 0x0342, 0x2000 },
+  { 0x0c00, 0x0341, 0x0000 },
+  { 0x0c00, 0x0343, 0x0000 },
+  { 0x8c00, 0x0346, 0x2000 },
+  { 0x0c00, 0x0345, 0x0000 },
+  { 0x0c00, 0x0347, 0x0000 },
+  { 0x8c00, 0x0350, 0x4000 },
+  { 0x8c00, 0x034c, 0x3000 },
+  { 0x8c00, 0x034a, 0x2000 },
+  { 0x0c00, 0x0349, 0x0000 },
+  { 0x0c00, 0x034b, 0x0000 },
+  { 0x8c00, 0x034e, 0x2000 },
+  { 0x0c00, 0x034d, 0x0000 },
+  { 0x0c00, 0x034f, 0x0000 },
+  { 0x8c00, 0x0354, 0x3000 },
+  { 0x8c00, 0x0352, 0x2000 },
+  { 0x0c00, 0x0351, 0x0000 },
+  { 0x0c00, 0x0353, 0x0000 },
+  { 0x8c00, 0x0356, 0x2000 },
+  { 0x0c00, 0x0355, 0x0000 },
+  { 0x0c00, 0x0357, 0x0000 },
+  { 0x8900, 0x038f, 0x603f },
+  { 0x8c00, 0x036d, 0x5000 },
+  { 0x8c00, 0x0365, 0x4000 },
+  { 0x8c00, 0x0361, 0x3000 },
+  { 0x8c00, 0x035f, 0x2000 },
+  { 0x0c00, 0x035e, 0x0000 },
+  { 0x0c00, 0x0360, 0x0000 },
+  { 0x8c00, 0x0363, 0x2000 },
+  { 0x0c00, 0x0362, 0x0000 },
+  { 0x0c00, 0x0364, 0x0000 },
+  { 0x8c00, 0x0369, 0x3000 },
+  { 0x8c00, 0x0367, 0x2000 },
+  { 0x0c00, 0x0366, 0x0000 },
+  { 0x0c00, 0x0368, 0x0000 },
+  { 0x8c00, 0x036b, 0x2000 },
+  { 0x0c00, 0x036a, 0x0000 },
+  { 0x0c00, 0x036c, 0x0000 },
+  { 0x9800, 0x0385, 0x4000 },
+  { 0x9800, 0x0375, 0x3000 },
+  { 0x8c00, 0x036f, 0x2000 },
+  { 0x0c00, 0x036e, 0x0000 },
+  { 0x1800, 0x0374, 0x0000 },
+  { 0x9500, 0x037e, 0x2000 },
+  { 0x0600, 0x037a, 0x0000 },
+  { 0x1800, 0x0384, 0x0000 },
+  { 0x8900, 0x0389, 0x3025 },
+  { 0x9500, 0x0387, 0x2000 },
+  { 0x0900, 0x0386, 0x0026 },
+  { 0x0900, 0x0388, 0x0025 },
+  { 0x8900, 0x038c, 0x2040 },
+  { 0x0900, 0x038a, 0x0025 },
+  { 0x0900, 0x038e, 0x003f },
+  { 0x8900, 0x039f, 0x5020 },
+  { 0x8900, 0x0397, 0x4020 },
+  { 0x8900, 0x0393, 0x3020 },
+  { 0x8900, 0x0391, 0x2020 },
+  { 0x0500, 0x0390, 0x0000 },
+  { 0x0900, 0x0392, 0x0020 },
+  { 0x8900, 0x0395, 0x2020 },
+  { 0x0900, 0x0394, 0x0020 },
+  { 0x0900, 0x0396, 0x0020 },
+  { 0x8900, 0x039b, 0x3020 },
+  { 0x8900, 0x0399, 0x2020 },
+  { 0x0900, 0x0398, 0x0020 },
+  { 0x0900, 0x039a, 0x0020 },
+  { 0x8900, 0x039d, 0x2020 },
+  { 0x0900, 0x039c, 0x0020 },
+  { 0x0900, 0x039e, 0x0020 },
+  { 0x8900, 0x03a8, 0x4020 },
+  { 0x8900, 0x03a4, 0x3020 },
+  { 0x8900, 0x03a1, 0x2020 },
+  { 0x0900, 0x03a0, 0x0020 },
+  { 0x0900, 0x03a3, 0x0020 },
+  { 0x8900, 0x03a6, 0x2020 },
+  { 0x0900, 0x03a5, 0x0020 },
+  { 0x0900, 0x03a7, 0x0020 },
+  { 0x8500, 0x03ac, 0x3fda },
+  { 0x8900, 0x03aa, 0x2020 },
+  { 0x0900, 0x03a9, 0x0020 },
+  { 0x0900, 0x03ab, 0x0020 },
+  { 0x8500, 0x03ae, 0x2fdb },
+  { 0x0500, 0x03ad, 0x0fdb },
+  { 0x0500, 0x03af, 0x0fdb },
+  { 0x8500, 0x03f1, 0x7fb0 },
+  { 0x8500, 0x03d1, 0x6fc7 },
+  { 0x8500, 0x03c0, 0x5fe0 },
+  { 0x8500, 0x03b8, 0x4fe0 },
+  { 0x8500, 0x03b4, 0x3fe0 },
+  { 0x8500, 0x03b2, 0x2fe0 },
+  { 0x0500, 0x03b1, 0x0fe0 },
+  { 0x0500, 0x03b3, 0x0fe0 },
+  { 0x8500, 0x03b6, 0x2fe0 },
+  { 0x0500, 0x03b5, 0x0fe0 },
+  { 0x0500, 0x03b7, 0x0fe0 },
+  { 0x8500, 0x03bc, 0x3fe0 },
+  { 0x8500, 0x03ba, 0x2fe0 },
+  { 0x0500, 0x03b9, 0x0fe0 },
+  { 0x0500, 0x03bb, 0x0fe0 },
+  { 0x8500, 0x03be, 0x2fe0 },
+  { 0x0500, 0x03bd, 0x0fe0 },
+  { 0x0500, 0x03bf, 0x0fe0 },
+  { 0x8500, 0x03c8, 0x4fe0 },
+  { 0x8500, 0x03c4, 0x3fe0 },
+  { 0x8500, 0x03c2, 0x2fe1 },
+  { 0x0500, 0x03c1, 0x0fe0 },
+  { 0x0500, 0x03c3, 0x0fe0 },
+  { 0x8500, 0x03c6, 0x2fe0 },
+  { 0x0500, 0x03c5, 0x0fe0 },
+  { 0x0500, 0x03c7, 0x0fe0 },
+  { 0x8500, 0x03cc, 0x3fc0 },
+  { 0x8500, 0x03ca, 0x2fe0 },
+  { 0x0500, 0x03c9, 0x0fe0 },
+  { 0x0500, 0x03cb, 0x0fe0 },
+  { 0x8500, 0x03ce, 0x2fc1 },
+  { 0x0500, 0x03cd, 0x0fc1 },
+  { 0x0500, 0x03d0, 0x0fc2 },
+  { 0x8500, 0x03e1, 0x5fff },
+  { 0x8500, 0x03d9, 0x4fff },
+  { 0x8500, 0x03d5, 0x3fd1 },
+  { 0x8900, 0x03d3, 0x2000 },
+  { 0x0900, 0x03d2, 0x0000 },
+  { 0x0900, 0x03d4, 0x0000 },
+  { 0x8500, 0x03d7, 0x2000 },
+  { 0x0500, 0x03d6, 0x0fca },
+  { 0x0900, 0x03d8, 0x0001 },
+  { 0x8500, 0x03dd, 0x3fff },
+  { 0x8500, 0x03db, 0x2fff },
+  { 0x0900, 0x03da, 0x0001 },
+  { 0x0900, 0x03dc, 0x0001 },
+  { 0x8500, 0x03df, 0x2fff },
+  { 0x0900, 0x03de, 0x0001 },
+  { 0x0900, 0x03e0, 0x0001 },
+  { 0x8500, 0x03e9, 0x4fff },
+  { 0x8500, 0x03e5, 0x3fff },
+  { 0x8500, 0x03e3, 0x2fff },
+  { 0x0900, 0x03e2, 0x0001 },
+  { 0x0900, 0x03e4, 0x0001 },
+  { 0x8500, 0x03e7, 0x2fff },
+  { 0x0900, 0x03e6, 0x0001 },
+  { 0x0900, 0x03e8, 0x0001 },
+  { 0x8500, 0x03ed, 0x3fff },
+  { 0x8500, 0x03eb, 0x2fff },
+  { 0x0900, 0x03ea, 0x0001 },
+  { 0x0900, 0x03ec, 0x0001 },
+  { 0x8500, 0x03ef, 0x2fff },
+  { 0x0900, 0x03ee, 0x0001 },
+  { 0x0500, 0x03f0, 0x0faa },
+  { 0x8900, 0x0415, 0x6020 },
+  { 0x8900, 0x0405, 0x5050 },
+  { 0x8900, 0x03f9, 0x4ff9 },
+  { 0x8500, 0x03f5, 0x3fa0 },
+  { 0x8500, 0x03f3, 0x2000 },
+  { 0x0500, 0x03f2, 0x0007 },
+  { 0x0900, 0x03f4, 0x0fc4 },
+  { 0x8900, 0x03f7, 0x2001 },
+  { 0x1900, 0x03f6, 0x0000 },
+  { 0x0500, 0x03f8, 0x0fff },
+  { 0x8900, 0x0401, 0x3050 },
+  { 0x8500, 0x03fb, 0x2fff },
+  { 0x0900, 0x03fa, 0x0001 },
+  { 0x0900, 0x0400, 0x0050 },
+  { 0x8900, 0x0403, 0x2050 },
+  { 0x0900, 0x0402, 0x0050 },
+  { 0x0900, 0x0404, 0x0050 },
+  { 0x8900, 0x040d, 0x4050 },
+  { 0x8900, 0x0409, 0x3050 },
+  { 0x8900, 0x0407, 0x2050 },
+  { 0x0900, 0x0406, 0x0050 },
+  { 0x0900, 0x0408, 0x0050 },
+  { 0x8900, 0x040b, 0x2050 },
+  { 0x0900, 0x040a, 0x0050 },
+  { 0x0900, 0x040c, 0x0050 },
+  { 0x8900, 0x0411, 0x3020 },
+  { 0x8900, 0x040f, 0x2050 },
+  { 0x0900, 0x040e, 0x0050 },
+  { 0x0900, 0x0410, 0x0020 },
+  { 0x8900, 0x0413, 0x2020 },
+  { 0x0900, 0x0412, 0x0020 },
+  { 0x0900, 0x0414, 0x0020 },
+  { 0x8900, 0x0425, 0x5020 },
+  { 0x8900, 0x041d, 0x4020 },
+  { 0x8900, 0x0419, 0x3020 },
+  { 0x8900, 0x0417, 0x2020 },
+  { 0x0900, 0x0416, 0x0020 },
+  { 0x0900, 0x0418, 0x0020 },
+  { 0x8900, 0x041b, 0x2020 },
+  { 0x0900, 0x041a, 0x0020 },
+  { 0x0900, 0x041c, 0x0020 },
+  { 0x8900, 0x0421, 0x3020 },
+  { 0x8900, 0x041f, 0x2020 },
+  { 0x0900, 0x041e, 0x0020 },
+  { 0x0900, 0x0420, 0x0020 },
+  { 0x8900, 0x0423, 0x2020 },
+  { 0x0900, 0x0422, 0x0020 },
+  { 0x0900, 0x0424, 0x0020 },
+  { 0x8900, 0x042d, 0x4020 },
+  { 0x8900, 0x0429, 0x3020 },
+  { 0x8900, 0x0427, 0x2020 },
+  { 0x0900, 0x0426, 0x0020 },
+  { 0x0900, 0x0428, 0x0020 },
+  { 0x8900, 0x042b, 0x2020 },
+  { 0x0900, 0x042a, 0x0020 },
+  { 0x0900, 0x042c, 0x0020 },
+  { 0x8500, 0x0431, 0x3fe0 },
+  { 0x8900, 0x042f, 0x2020 },
+  { 0x0900, 0x042e, 0x0020 },
+  { 0x0500, 0x0430, 0x0fe0 },
+  { 0x8500, 0x0433, 0x2fe0 },
+  { 0x0500, 0x0432, 0x0fe0 },
+  { 0x0500, 0x0434, 0x0fe0 },
+  { 0x8700, 0x06a4, 0xa000 },
+  { 0x8500, 0x0563, 0x9fd0 },
+  { 0x8900, 0x04b6, 0x8001 },
+  { 0x8500, 0x0475, 0x7fff },
+  { 0x8500, 0x0455, 0x6fb0 },
+  { 0x8500, 0x0445, 0x5fe0 },
+  { 0x8500, 0x043d, 0x4fe0 },
+  { 0x8500, 0x0439, 0x3fe0 },
+  { 0x8500, 0x0437, 0x2fe0 },
+  { 0x0500, 0x0436, 0x0fe0 },
+  { 0x0500, 0x0438, 0x0fe0 },
+  { 0x8500, 0x043b, 0x2fe0 },
+  { 0x0500, 0x043a, 0x0fe0 },
+  { 0x0500, 0x043c, 0x0fe0 },
+  { 0x8500, 0x0441, 0x3fe0 },
+  { 0x8500, 0x043f, 0x2fe0 },
+  { 0x0500, 0x043e, 0x0fe0 },
+  { 0x0500, 0x0440, 0x0fe0 },
+  { 0x8500, 0x0443, 0x2fe0 },
+  { 0x0500, 0x0442, 0x0fe0 },
+  { 0x0500, 0x0444, 0x0fe0 },
+  { 0x8500, 0x044d, 0x4fe0 },
+  { 0x8500, 0x0449, 0x3fe0 },
+  { 0x8500, 0x0447, 0x2fe0 },
+  { 0x0500, 0x0446, 0x0fe0 },
+  { 0x0500, 0x0448, 0x0fe0 },
+  { 0x8500, 0x044b, 0x2fe0 },
+  { 0x0500, 0x044a, 0x0fe0 },
+  { 0x0500, 0x044c, 0x0fe0 },
+  { 0x8500, 0x0451, 0x3fb0 },
+  { 0x8500, 0x044f, 0x2fe0 },
+  { 0x0500, 0x044e, 0x0fe0 },
+  { 0x0500, 0x0450, 0x0fb0 },
+  { 0x8500, 0x0453, 0x2fb0 },
+  { 0x0500, 0x0452, 0x0fb0 },
+  { 0x0500, 0x0454, 0x0fb0 },
+  { 0x8500, 0x0465, 0x5fff },
+  { 0x8500, 0x045d, 0x4fb0 },
+  { 0x8500, 0x0459, 0x3fb0 },
+  { 0x8500, 0x0457, 0x2fb0 },
+  { 0x0500, 0x0456, 0x0fb0 },
+  { 0x0500, 0x0458, 0x0fb0 },
+  { 0x8500, 0x045b, 0x2fb0 },
+  { 0x0500, 0x045a, 0x0fb0 },
+  { 0x0500, 0x045c, 0x0fb0 },
+  { 0x8500, 0x0461, 0x3fff },
+  { 0x8500, 0x045f, 0x2fb0 },
+  { 0x0500, 0x045e, 0x0fb0 },
+  { 0x0900, 0x0460, 0x0001 },
+  { 0x8500, 0x0463, 0x2fff },
+  { 0x0900, 0x0462, 0x0001 },
+  { 0x0900, 0x0464, 0x0001 },
+  { 0x8500, 0x046d, 0x4fff },
+  { 0x8500, 0x0469, 0x3fff },
+  { 0x8500, 0x0467, 0x2fff },
+  { 0x0900, 0x0466, 0x0001 },
+  { 0x0900, 0x0468, 0x0001 },
+  { 0x8500, 0x046b, 0x2fff },
+  { 0x0900, 0x046a, 0x0001 },
+  { 0x0900, 0x046c, 0x0001 },
+  { 0x8500, 0x0471, 0x3fff },
+  { 0x8500, 0x046f, 0x2fff },
+  { 0x0900, 0x046e, 0x0001 },
+  { 0x0900, 0x0470, 0x0001 },
+  { 0x8500, 0x0473, 0x2fff },
+  { 0x0900, 0x0472, 0x0001 },
+  { 0x0900, 0x0474, 0x0001 },
+  { 0x8900, 0x0496, 0x6001 },
+  { 0x8c00, 0x0485, 0x5000 },
+  { 0x8500, 0x047d, 0x4fff },
+  { 0x8500, 0x0479, 0x3fff },
+  { 0x8500, 0x0477, 0x2fff },
+  { 0x0900, 0x0476, 0x0001 },
+  { 0x0900, 0x0478, 0x0001 },
+  { 0x8500, 0x047b, 0x2fff },
+  { 0x0900, 0x047a, 0x0001 },
+  { 0x0900, 0x047c, 0x0001 },
+  { 0x8500, 0x0481, 0x3fff },
+  { 0x8500, 0x047f, 0x2fff },
+  { 0x0900, 0x047e, 0x0001 },
+  { 0x0900, 0x0480, 0x0001 },
+  { 0x8c00, 0x0483, 0x2000 },
+  { 0x1a00, 0x0482, 0x0000 },
+  { 0x0c00, 0x0484, 0x0000 },
+  { 0x8900, 0x048e, 0x4001 },
+  { 0x8900, 0x048a, 0x3001 },
+  { 0x8b00, 0x0488, 0x2000 },
+  { 0x0c00, 0x0486, 0x0000 },
+  { 0x0b00, 0x0489, 0x0000 },
+  { 0x8900, 0x048c, 0x2001 },
+  { 0x0500, 0x048b, 0x0fff },
+  { 0x0500, 0x048d, 0x0fff },
+  { 0x8900, 0x0492, 0x3001 },
+  { 0x8900, 0x0490, 0x2001 },
+  { 0x0500, 0x048f, 0x0fff },
+  { 0x0500, 0x0491, 0x0fff },
+  { 0x8900, 0x0494, 0x2001 },
+  { 0x0500, 0x0493, 0x0fff },
+  { 0x0500, 0x0495, 0x0fff },
+  { 0x8900, 0x04a6, 0x5001 },
+  { 0x8900, 0x049e, 0x4001 },
+  { 0x8900, 0x049a, 0x3001 },
+  { 0x8900, 0x0498, 0x2001 },
+  { 0x0500, 0x0497, 0x0fff },
+  { 0x0500, 0x0499, 0x0fff },
+  { 0x8900, 0x049c, 0x2001 },
+  { 0x0500, 0x049b, 0x0fff },
+  { 0x0500, 0x049d, 0x0fff },
+  { 0x8900, 0x04a2, 0x3001 },
+  { 0x8900, 0x04a0, 0x2001 },
+  { 0x0500, 0x049f, 0x0fff },
+  { 0x0500, 0x04a1, 0x0fff },
+  { 0x8900, 0x04a4, 0x2001 },
+  { 0x0500, 0x04a3, 0x0fff },
+  { 0x0500, 0x04a5, 0x0fff },
+  { 0x8900, 0x04ae, 0x4001 },
+  { 0x8900, 0x04aa, 0x3001 },
+  { 0x8900, 0x04a8, 0x2001 },
+  { 0x0500, 0x04a7, 0x0fff },
+  { 0x0500, 0x04a9, 0x0fff },
+  { 0x8900, 0x04ac, 0x2001 },
+  { 0x0500, 0x04ab, 0x0fff },
+  { 0x0500, 0x04ad, 0x0fff },
+  { 0x8900, 0x04b2, 0x3001 },
+  { 0x8900, 0x04b0, 0x2001 },
+  { 0x0500, 0x04af, 0x0fff },
+  { 0x0500, 0x04b1, 0x0fff },
+  { 0x8900, 0x04b4, 0x2001 },
+  { 0x0500, 0x04b3, 0x0fff },
+  { 0x0500, 0x04b5, 0x0fff },
+  { 0x8500, 0x04f9, 0x7fff },
+  { 0x8500, 0x04d7, 0x6fff },
+  { 0x8500, 0x04c6, 0x5fff },
+  { 0x8900, 0x04be, 0x4001 },
+  { 0x8900, 0x04ba, 0x3001 },
+  { 0x8900, 0x04b8, 0x2001 },
+  { 0x0500, 0x04b7, 0x0fff },
+  { 0x0500, 0x04b9, 0x0fff },
+  { 0x8900, 0x04bc, 0x2001 },
+  { 0x0500, 0x04bb, 0x0fff },
+  { 0x0500, 0x04bd, 0x0fff },
+  { 0x8500, 0x04c2, 0x3fff },
+  { 0x8900, 0x04c0, 0x2000 },
+  { 0x0500, 0x04bf, 0x0fff },
+  { 0x0900, 0x04c1, 0x0001 },
+  { 0x8500, 0x04c4, 0x2fff },
+  { 0x0900, 0x04c3, 0x0001 },
+  { 0x0900, 0x04c5, 0x0001 },
+  { 0x8500, 0x04ce, 0x4fff },
+  { 0x8500, 0x04ca, 0x3fff },
+  { 0x8500, 0x04c8, 0x2fff },
+  { 0x0900, 0x04c7, 0x0001 },
+  { 0x0900, 0x04c9, 0x0001 },
+  { 0x8500, 0x04cc, 0x2fff },
+  { 0x0900, 0x04cb, 0x0001 },
+  { 0x0900, 0x04cd, 0x0001 },
+  { 0x8500, 0x04d3, 0x3fff },
+  { 0x8500, 0x04d1, 0x2fff },
+  { 0x0900, 0x04d0, 0x0001 },
+  { 0x0900, 0x04d2, 0x0001 },
+  { 0x8500, 0x04d5, 0x2fff },
+  { 0x0900, 0x04d4, 0x0001 },
+  { 0x0900, 0x04d6, 0x0001 },
+  { 0x8500, 0x04e7, 0x5fff },
+  { 0x8500, 0x04df, 0x4fff },
+  { 0x8500, 0x04db, 0x3fff },
+  { 0x8500, 0x04d9, 0x2fff },
+  { 0x0900, 0x04d8, 0x0001 },
+  { 0x0900, 0x04da, 0x0001 },
+  { 0x8500, 0x04dd, 0x2fff },
+  { 0x0900, 0x04dc, 0x0001 },
+  { 0x0900, 0x04de, 0x0001 },
+  { 0x8500, 0x04e3, 0x3fff },
+  { 0x8500, 0x04e1, 0x2fff },
+  { 0x0900, 0x04e0, 0x0001 },
+  { 0x0900, 0x04e2, 0x0001 },
+  { 0x8500, 0x04e5, 0x2fff },
+  { 0x0900, 0x04e4, 0x0001 },
+  { 0x0900, 0x04e6, 0x0001 },
+  { 0x8500, 0x04ef, 0x4fff },
+  { 0x8500, 0x04eb, 0x3fff },
+  { 0x8500, 0x04e9, 0x2fff },
+  { 0x0900, 0x04e8, 0x0001 },
+  { 0x0900, 0x04ea, 0x0001 },
+  { 0x8500, 0x04ed, 0x2fff },
+  { 0x0900, 0x04ec, 0x0001 },
+  { 0x0900, 0x04ee, 0x0001 },
+  { 0x8500, 0x04f3, 0x3fff },
+  { 0x8500, 0x04f1, 0x2fff },
+  { 0x0900, 0x04f0, 0x0001 },
+  { 0x0900, 0x04f2, 0x0001 },
+  { 0x8500, 0x04f5, 0x2fff },
+  { 0x0900, 0x04f4, 0x0001 },
+  { 0x0900, 0x04f8, 0x0001 },
+  { 0x8900, 0x0540, 0x6030 },
+  { 0x8500, 0x050f, 0x5fff },
+  { 0x8500, 0x0507, 0x4fff },
+  { 0x8500, 0x0503, 0x3fff },
+  { 0x8500, 0x0501, 0x2fff },
+  { 0x0900, 0x0500, 0x0001 },
+  { 0x0900, 0x0502, 0x0001 },
+  { 0x8500, 0x0505, 0x2fff },
+  { 0x0900, 0x0504, 0x0001 },
+  { 0x0900, 0x0506, 0x0001 },
+  { 0x8500, 0x050b, 0x3fff },
+  { 0x8500, 0x0509, 0x2fff },
+  { 0x0900, 0x0508, 0x0001 },
+  { 0x0900, 0x050a, 0x0001 },
+  { 0x8500, 0x050d, 0x2fff },
+  { 0x0900, 0x050c, 0x0001 },
+  { 0x0900, 0x050e, 0x0001 },
+  { 0x8900, 0x0538, 0x4030 },
+  { 0x8900, 0x0534, 0x3030 },
+  { 0x8900, 0x0532, 0x2030 },
+  { 0x0900, 0x0531, 0x0030 },
+  { 0x0900, 0x0533, 0x0030 },
+  { 0x8900, 0x0536, 0x2030 },
+  { 0x0900, 0x0535, 0x0030 },
+  { 0x0900, 0x0537, 0x0030 },
+  { 0x8900, 0x053c, 0x3030 },
+  { 0x8900, 0x053a, 0x2030 },
+  { 0x0900, 0x0539, 0x0030 },
+  { 0x0900, 0x053b, 0x0030 },
+  { 0x8900, 0x053e, 0x2030 },
+  { 0x0900, 0x053d, 0x0030 },
+  { 0x0900, 0x053f, 0x0030 },
+  { 0x8900, 0x0550, 0x5030 },
+  { 0x8900, 0x0548, 0x4030 },
+  { 0x8900, 0x0544, 0x3030 },
+  { 0x8900, 0x0542, 0x2030 },
+  { 0x0900, 0x0541, 0x0030 },
+  { 0x0900, 0x0543, 0x0030 },
+  { 0x8900, 0x0546, 0x2030 },
+  { 0x0900, 0x0545, 0x0030 },
+  { 0x0900, 0x0547, 0x0030 },
+  { 0x8900, 0x054c, 0x3030 },
+  { 0x8900, 0x054a, 0x2030 },
+  { 0x0900, 0x0549, 0x0030 },
+  { 0x0900, 0x054b, 0x0030 },
+  { 0x8900, 0x054e, 0x2030 },
+  { 0x0900, 0x054d, 0x0030 },
+  { 0x0900, 0x054f, 0x0030 },
+  { 0x9500, 0x055a, 0x4000 },
+  { 0x8900, 0x0554, 0x3030 },
+  { 0x8900, 0x0552, 0x2030 },
+  { 0x0900, 0x0551, 0x0030 },
+  { 0x0900, 0x0553, 0x0030 },
+  { 0x8900, 0x0556, 0x2030 },
+  { 0x0900, 0x0555, 0x0030 },
+  { 0x0600, 0x0559, 0x0000 },
+  { 0x9500, 0x055e, 0x3000 },
+  { 0x9500, 0x055c, 0x2000 },
+  { 0x1500, 0x055b, 0x0000 },
+  { 0x1500, 0x055d, 0x0000 },
+  { 0x8500, 0x0561, 0x2fd0 },
+  { 0x1500, 0x055f, 0x0000 },
+  { 0x0500, 0x0562, 0x0fd0 },
+  { 0x9a00, 0x060f, 0x8000 },
+  { 0x8c00, 0x05ab, 0x7000 },
+  { 0x8500, 0x0583, 0x6fd0 },
+  { 0x8500, 0x0573, 0x5fd0 },
+  { 0x8500, 0x056b, 0x4fd0 },
+  { 0x8500, 0x0567, 0x3fd0 },
+  { 0x8500, 0x0565, 0x2fd0 },
+  { 0x0500, 0x0564, 0x0fd0 },
+  { 0x0500, 0x0566, 0x0fd0 },
+  { 0x8500, 0x0569, 0x2fd0 },
+  { 0x0500, 0x0568, 0x0fd0 },
+  { 0x0500, 0x056a, 0x0fd0 },
+  { 0x8500, 0x056f, 0x3fd0 },
+  { 0x8500, 0x056d, 0x2fd0 },
+  { 0x0500, 0x056c, 0x0fd0 },
+  { 0x0500, 0x056e, 0x0fd0 },
+  { 0x8500, 0x0571, 0x2fd0 },
+  { 0x0500, 0x0570, 0x0fd0 },
+  { 0x0500, 0x0572, 0x0fd0 },
+  { 0x8500, 0x057b, 0x4fd0 },
+  { 0x8500, 0x0577, 0x3fd0 },
+  { 0x8500, 0x0575, 0x2fd0 },
+  { 0x0500, 0x0574, 0x0fd0 },
+  { 0x0500, 0x0576, 0x0fd0 },
+  { 0x8500, 0x0579, 0x2fd0 },
+  { 0x0500, 0x0578, 0x0fd0 },
+  { 0x0500, 0x057a, 0x0fd0 },
+  { 0x8500, 0x057f, 0x3fd0 },
+  { 0x8500, 0x057d, 0x2fd0 },
+  { 0x0500, 0x057c, 0x0fd0 },
+  { 0x0500, 0x057e, 0x0fd0 },
+  { 0x8500, 0x0581, 0x2fd0 },
+  { 0x0500, 0x0580, 0x0fd0 },
+  { 0x0500, 0x0582, 0x0fd0 },
+  { 0x8c00, 0x059a, 0x5000 },
+  { 0x8c00, 0x0592, 0x4000 },
+  { 0x8500, 0x0587, 0x3000 },
+  { 0x8500, 0x0585, 0x2fd0 },
+  { 0x0500, 0x0584, 0x0fd0 },
+  { 0x0500, 0x0586, 0x0fd0 },
+  { 0x9100, 0x058a, 0x2000 },
+  { 0x1500, 0x0589, 0x0000 },
+  { 0x0c00, 0x0591, 0x0000 },
+  { 0x8c00, 0x0596, 0x3000 },
+  { 0x8c00, 0x0594, 0x2000 },
+  { 0x0c00, 0x0593, 0x0000 },
+  { 0x0c00, 0x0595, 0x0000 },
+  { 0x8c00, 0x0598, 0x2000 },
+  { 0x0c00, 0x0597, 0x0000 },
+  { 0x0c00, 0x0599, 0x0000 },
+  { 0x8c00, 0x05a3, 0x4000 },
+  { 0x8c00, 0x059e, 0x3000 },
+  { 0x8c00, 0x059c, 0x2000 },
+  { 0x0c00, 0x059b, 0x0000 },
+  { 0x0c00, 0x059d, 0x0000 },
+  { 0x8c00, 0x05a0, 0x2000 },
+  { 0x0c00, 0x059f, 0x0000 },
+  { 0x0c00, 0x05a1, 0x0000 },
+  { 0x8c00, 0x05a7, 0x3000 },
+  { 0x8c00, 0x05a5, 0x2000 },
+  { 0x0c00, 0x05a4, 0x0000 },
+  { 0x0c00, 0x05a6, 0x0000 },
+  { 0x8c00, 0x05a9, 0x2000 },
+  { 0x0c00, 0x05a8, 0x0000 },
+  { 0x0c00, 0x05aa, 0x0000 },
+  { 0x8700, 0x05d7, 0x6000 },
+  { 0x8c00, 0x05bc, 0x5000 },
+  { 0x8c00, 0x05b3, 0x4000 },
+  { 0x8c00, 0x05af, 0x3000 },
+  { 0x8c00, 0x05ad, 0x2000 },
+  { 0x0c00, 0x05ac, 0x0000 },
+  { 0x0c00, 0x05ae, 0x0000 },
+  { 0x8c00, 0x05b1, 0x2000 },
+  { 0x0c00, 0x05b0, 0x0000 },
+  { 0x0c00, 0x05b2, 0x0000 },
+  { 0x8c00, 0x05b7, 0x3000 },
+  { 0x8c00, 0x05b5, 0x2000 },
+  { 0x0c00, 0x05b4, 0x0000 },
+  { 0x0c00, 0x05b6, 0x0000 },
+  { 0x8c00, 0x05b9, 0x2000 },
+  { 0x0c00, 0x05b8, 0x0000 },
+  { 0x0c00, 0x05bb, 0x0000 },
+  { 0x8c00, 0x05c4, 0x4000 },
+  { 0x9500, 0x05c0, 0x3000 },
+  { 0x9500, 0x05be, 0x2000 },
+  { 0x0c00, 0x05bd, 0x0000 },
+  { 0x0c00, 0x05bf, 0x0000 },
+  { 0x8c00, 0x05c2, 0x2000 },
+  { 0x0c00, 0x05c1, 0x0000 },
+  { 0x1500, 0x05c3, 0x0000 },
+  { 0x8700, 0x05d3, 0x3000 },
+  { 0x8700, 0x05d1, 0x2000 },
+  { 0x0700, 0x05d0, 0x0000 },
+  { 0x0700, 0x05d2, 0x0000 },
+  { 0x8700, 0x05d5, 0x2000 },
+  { 0x0700, 0x05d4, 0x0000 },
+  { 0x0700, 0x05d6, 0x0000 },
+  { 0x8700, 0x05e7, 0x5000 },
+  { 0x8700, 0x05df, 0x4000 },
+  { 0x8700, 0x05db, 0x3000 },
+  { 0x8700, 0x05d9, 0x2000 },
+  { 0x0700, 0x05d8, 0x0000 },
+  { 0x0700, 0x05da, 0x0000 },
+  { 0x8700, 0x05dd, 0x2000 },
+  { 0x0700, 0x05dc, 0x0000 },
+  { 0x0700, 0x05de, 0x0000 },
+  { 0x8700, 0x05e3, 0x3000 },
+  { 0x8700, 0x05e1, 0x2000 },
+  { 0x0700, 0x05e0, 0x0000 },
+  { 0x0700, 0x05e2, 0x0000 },
+  { 0x8700, 0x05e5, 0x2000 },
+  { 0x0700, 0x05e4, 0x0000 },
+  { 0x0700, 0x05e6, 0x0000 },
+  { 0x9500, 0x05f4, 0x4000 },
+  { 0x8700, 0x05f0, 0x3000 },
+  { 0x8700, 0x05e9, 0x2000 },
+  { 0x0700, 0x05e8, 0x0000 },
+  { 0x0700, 0x05ea, 0x0000 },
+  { 0x8700, 0x05f2, 0x2000 },
+  { 0x0700, 0x05f1, 0x0000 },
+  { 0x1500, 0x05f3, 0x0000 },
+  { 0x8100, 0x0603, 0x3000 },
+  { 0x8100, 0x0601, 0x2000 },
+  { 0x0100, 0x0600, 0x0000 },
+  { 0x0100, 0x0602, 0x0000 },
+  { 0x9500, 0x060d, 0x2000 },
+  { 0x1500, 0x060c, 0x0000 },
+  { 0x1a00, 0x060e, 0x0000 },
+  { 0x8d00, 0x0664, 0x7000 },
+  { 0x8700, 0x0638, 0x6000 },
+  { 0x8700, 0x0628, 0x5000 },
+  { 0x9500, 0x061f, 0x4000 },
+  { 0x8c00, 0x0613, 0x3000 },
+  { 0x8c00, 0x0611, 0x2000 },
+  { 0x0c00, 0x0610, 0x0000 },
+  { 0x0c00, 0x0612, 0x0000 },
+  { 0x8c00, 0x0615, 0x2000 },
+  { 0x0c00, 0x0614, 0x0000 },
+  { 0x1500, 0x061b, 0x0000 },
+  { 0x8700, 0x0624, 0x3000 },
+  { 0x8700, 0x0622, 0x2000 },
+  { 0x0700, 0x0621, 0x0000 },
+  { 0x0700, 0x0623, 0x0000 },
+  { 0x8700, 0x0626, 0x2000 },
+  { 0x0700, 0x0625, 0x0000 },
+  { 0x0700, 0x0627, 0x0000 },
+  { 0x8700, 0x0630, 0x4000 },
+  { 0x8700, 0x062c, 0x3000 },
+  { 0x8700, 0x062a, 0x2000 },
+  { 0x0700, 0x0629, 0x0000 },
+  { 0x0700, 0x062b, 0x0000 },
+  { 0x8700, 0x062e, 0x2000 },
+  { 0x0700, 0x062d, 0x0000 },
+  { 0x0700, 0x062f, 0x0000 },
+  { 0x8700, 0x0634, 0x3000 },
+  { 0x8700, 0x0632, 0x2000 },
+  { 0x0700, 0x0631, 0x0000 },
+  { 0x0700, 0x0633, 0x0000 },
+  { 0x8700, 0x0636, 0x2000 },
+  { 0x0700, 0x0635, 0x0000 },
+  { 0x0700, 0x0637, 0x0000 },
+  { 0x8c00, 0x064d, 0x5000 },
+  { 0x8700, 0x0645, 0x4000 },
+  { 0x8700, 0x0641, 0x3000 },
+  { 0x8700, 0x063a, 0x2000 },
+  { 0x0700, 0x0639, 0x0000 },
+  { 0x0600, 0x0640, 0x0000 },
+  { 0x8700, 0x0643, 0x2000 },
+  { 0x0700, 0x0642, 0x0000 },
+  { 0x0700, 0x0644, 0x0000 },
+  { 0x8700, 0x0649, 0x3000 },
+  { 0x8700, 0x0647, 0x2000 },
+  { 0x0700, 0x0646, 0x0000 },
+  { 0x0700, 0x0648, 0x0000 },
+  { 0x8c00, 0x064b, 0x2000 },
+  { 0x0700, 0x064a, 0x0000 },
+  { 0x0c00, 0x064c, 0x0000 },
+  { 0x8c00, 0x0655, 0x4000 },
+  { 0x8c00, 0x0651, 0x3000 },
+  { 0x8c00, 0x064f, 0x2000 },
+  { 0x0c00, 0x064e, 0x0000 },
+  { 0x0c00, 0x0650, 0x0000 },
+  { 0x8c00, 0x0653, 0x2000 },
+  { 0x0c00, 0x0652, 0x0000 },
+  { 0x0c00, 0x0654, 0x0000 },
+  { 0x8d00, 0x0660, 0x3000 },
+  { 0x8c00, 0x0657, 0x2000 },
+  { 0x0c00, 0x0656, 0x0000 },
+  { 0x0c00, 0x0658, 0x0000 },
+  { 0x8d00, 0x0662, 0x2000 },
+  { 0x0d00, 0x0661, 0x0000 },
+  { 0x0d00, 0x0663, 0x0000 },
+  { 0x8700, 0x0684, 0x6000 },
+  { 0x8700, 0x0674, 0x5000 },
+  { 0x9500, 0x066c, 0x4000 },
+  { 0x8d00, 0x0668, 0x3000 },
+  { 0x8d00, 0x0666, 0x2000 },
+  { 0x0d00, 0x0665, 0x0000 },
+  { 0x0d00, 0x0667, 0x0000 },
+  { 0x9500, 0x066a, 0x2000 },
+  { 0x0d00, 0x0669, 0x0000 },
+  { 0x1500, 0x066b, 0x0000 },
+  { 0x8c00, 0x0670, 0x3000 },
+  { 0x8700, 0x066e, 0x2000 },
+  { 0x1500, 0x066d, 0x0000 },
+  { 0x0700, 0x066f, 0x0000 },
+  { 0x8700, 0x0672, 0x2000 },
+  { 0x0700, 0x0671, 0x0000 },
+  { 0x0700, 0x0673, 0x0000 },
+  { 0x8700, 0x067c, 0x4000 },
+  { 0x8700, 0x0678, 0x3000 },
+  { 0x8700, 0x0676, 0x2000 },
+  { 0x0700, 0x0675, 0x0000 },
+  { 0x0700, 0x0677, 0x0000 },
+  { 0x8700, 0x067a, 0x2000 },
+  { 0x0700, 0x0679, 0x0000 },
+  { 0x0700, 0x067b, 0x0000 },
+  { 0x8700, 0x0680, 0x3000 },
+  { 0x8700, 0x067e, 0x2000 },
+  { 0x0700, 0x067d, 0x0000 },
+  { 0x0700, 0x067f, 0x0000 },
+  { 0x8700, 0x0682, 0x2000 },
+  { 0x0700, 0x0681, 0x0000 },
+  { 0x0700, 0x0683, 0x0000 },
+  { 0x8700, 0x0694, 0x5000 },
+  { 0x8700, 0x068c, 0x4000 },
+  { 0x8700, 0x0688, 0x3000 },
+  { 0x8700, 0x0686, 0x2000 },
+  { 0x0700, 0x0685, 0x0000 },
+  { 0x0700, 0x0687, 0x0000 },
+  { 0x8700, 0x068a, 0x2000 },
+  { 0x0700, 0x0689, 0x0000 },
+  { 0x0700, 0x068b, 0x0000 },
+  { 0x8700, 0x0690, 0x3000 },
+  { 0x8700, 0x068e, 0x2000 },
+  { 0x0700, 0x068d, 0x0000 },
+  { 0x0700, 0x068f, 0x0000 },
+  { 0x8700, 0x0692, 0x2000 },
+  { 0x0700, 0x0691, 0x0000 },
+  { 0x0700, 0x0693, 0x0000 },
+  { 0x8700, 0x069c, 0x4000 },
+  { 0x8700, 0x0698, 0x3000 },
+  { 0x8700, 0x0696, 0x2000 },
+  { 0x0700, 0x0695, 0x0000 },
+  { 0x0700, 0x0697, 0x0000 },
+  { 0x8700, 0x069a, 0x2000 },
+  { 0x0700, 0x0699, 0x0000 },
+  { 0x0700, 0x069b, 0x0000 },
+  { 0x8700, 0x06a0, 0x3000 },
+  { 0x8700, 0x069e, 0x2000 },
+  { 0x0700, 0x069d, 0x0000 },
+  { 0x0700, 0x069f, 0x0000 },
+  { 0x8700, 0x06a2, 0x2000 },
+  { 0x0700, 0x06a1, 0x0000 },
+  { 0x0700, 0x06a3, 0x0000 },
+  { 0x8700, 0x0926, 0x9000 },
+  { 0x8700, 0x0725, 0x8000 },
+  { 0x8c00, 0x06e4, 0x7000 },
+  { 0x8700, 0x06c4, 0x6000 },
+  { 0x8700, 0x06b4, 0x5000 },
+  { 0x8700, 0x06ac, 0x4000 },
+  { 0x8700, 0x06a8, 0x3000 },
+  { 0x8700, 0x06a6, 0x2000 },
+  { 0x0700, 0x06a5, 0x0000 },
+  { 0x0700, 0x06a7, 0x0000 },
+  { 0x8700, 0x06aa, 0x2000 },
+  { 0x0700, 0x06a9, 0x0000 },
+  { 0x0700, 0x06ab, 0x0000 },
+  { 0x8700, 0x06b0, 0x3000 },
+  { 0x8700, 0x06ae, 0x2000 },
+  { 0x0700, 0x06ad, 0x0000 },
+  { 0x0700, 0x06af, 0x0000 },
+  { 0x8700, 0x06b2, 0x2000 },
+  { 0x0700, 0x06b1, 0x0000 },
+  { 0x0700, 0x06b3, 0x0000 },
+  { 0x8700, 0x06bc, 0x4000 },
+  { 0x8700, 0x06b8, 0x3000 },
+  { 0x8700, 0x06b6, 0x2000 },
+  { 0x0700, 0x06b5, 0x0000 },
+  { 0x0700, 0x06b7, 0x0000 },
+  { 0x8700, 0x06ba, 0x2000 },
+  { 0x0700, 0x06b9, 0x0000 },
+  { 0x0700, 0x06bb, 0x0000 },
+  { 0x8700, 0x06c0, 0x3000 },
+  { 0x8700, 0x06be, 0x2000 },
+  { 0x0700, 0x06bd, 0x0000 },
+  { 0x0700, 0x06bf, 0x0000 },
+  { 0x8700, 0x06c2, 0x2000 },
+  { 0x0700, 0x06c1, 0x0000 },
+  { 0x0700, 0x06c3, 0x0000 },
+  { 0x9500, 0x06d4, 0x5000 },
+  { 0x8700, 0x06cc, 0x4000 },
+  { 0x8700, 0x06c8, 0x3000 },
+  { 0x8700, 0x06c6, 0x2000 },
+  { 0x0700, 0x06c5, 0x0000 },
+  { 0x0700, 0x06c7, 0x0000 },
+  { 0x8700, 0x06ca, 0x2000 },
+  { 0x0700, 0x06c9, 0x0000 },
+  { 0x0700, 0x06cb, 0x0000 },
+  { 0x8700, 0x06d0, 0x3000 },
+  { 0x8700, 0x06ce, 0x2000 },
+  { 0x0700, 0x06cd, 0x0000 },
+  { 0x0700, 0x06cf, 0x0000 },
+  { 0x8700, 0x06d2, 0x2000 },
+  { 0x0700, 0x06d1, 0x0000 },
+  { 0x0700, 0x06d3, 0x0000 },
+  { 0x8c00, 0x06dc, 0x4000 },
+  { 0x8c00, 0x06d8, 0x3000 },
+  { 0x8c00, 0x06d6, 0x2000 },
+  { 0x0700, 0x06d5, 0x0000 },
+  { 0x0c00, 0x06d7, 0x0000 },
+  { 0x8c00, 0x06da, 0x2000 },
+  { 0x0c00, 0x06d9, 0x0000 },
+  { 0x0c00, 0x06db, 0x0000 },
+  { 0x8c00, 0x06e0, 0x3000 },
+  { 0x8b00, 0x06de, 0x2000 },
+  { 0x0100, 0x06dd, 0x0000 },
+  { 0x0c00, 0x06df, 0x0000 },
+  { 0x8c00, 0x06e2, 0x2000 },
+  { 0x0c00, 0x06e1, 0x0000 },
+  { 0x0c00, 0x06e3, 0x0000 },
+  { 0x9500, 0x0704, 0x6000 },
+  { 0x8d00, 0x06f4, 0x5000 },
+  { 0x8c00, 0x06ec, 0x4000 },
+  { 0x8c00, 0x06e8, 0x3000 },
+  { 0x8600, 0x06e6, 0x2000 },
+  { 0x0600, 0x06e5, 0x0000 },
+  { 0x0c00, 0x06e7, 0x0000 },
+  { 0x8c00, 0x06ea, 0x2000 },
+  { 0x1a00, 0x06e9, 0x0000 },
+  { 0x0c00, 0x06eb, 0x0000 },
+  { 0x8d00, 0x06f0, 0x3000 },
+  { 0x8700, 0x06ee, 0x2000 },
+  { 0x0c00, 0x06ed, 0x0000 },
+  { 0x0700, 0x06ef, 0x0000 },
+  { 0x8d00, 0x06f2, 0x2000 },
+  { 0x0d00, 0x06f1, 0x0000 },
+  { 0x0d00, 0x06f3, 0x0000 },
+  { 0x8700, 0x06fc, 0x4000 },
+  { 0x8d00, 0x06f8, 0x3000 },
+  { 0x8d00, 0x06f6, 0x2000 },
+  { 0x0d00, 0x06f5, 0x0000 },
+  { 0x0d00, 0x06f7, 0x0000 },
+  { 0x8700, 0x06fa, 0x2000 },
+  { 0x0d00, 0x06f9, 0x0000 },
+  { 0x0700, 0x06fb, 0x0000 },
+  { 0x9500, 0x0700, 0x3000 },
+  { 0x9a00, 0x06fe, 0x2000 },
+  { 0x1a00, 0x06fd, 0x0000 },
+  { 0x0700, 0x06ff, 0x0000 },
+  { 0x9500, 0x0702, 0x2000 },
+  { 0x1500, 0x0701, 0x0000 },
+  { 0x1500, 0x0703, 0x0000 },
+  { 0x8700, 0x0715, 0x5000 },
+  { 0x9500, 0x070c, 0x4000 },
+  { 0x9500, 0x0708, 0x3000 },
+  { 0x9500, 0x0706, 0x2000 },
+  { 0x1500, 0x0705, 0x0000 },
+  { 0x1500, 0x0707, 0x0000 },
+  { 0x9500, 0x070a, 0x2000 },
+  { 0x1500, 0x0709, 0x0000 },
+  { 0x1500, 0x070b, 0x0000 },
+  { 0x8c00, 0x0711, 0x3000 },
+  { 0x8100, 0x070f, 0x2000 },
+  { 0x1500, 0x070d, 0x0000 },
+  { 0x0700, 0x0710, 0x0000 },
+  { 0x8700, 0x0713, 0x2000 },
+  { 0x0700, 0x0712, 0x0000 },
+  { 0x0700, 0x0714, 0x0000 },
+  { 0x8700, 0x071d, 0x4000 },
+  { 0x8700, 0x0719, 0x3000 },
+  { 0x8700, 0x0717, 0x2000 },
+  { 0x0700, 0x0716, 0x0000 },
+  { 0x0700, 0x0718, 0x0000 },
+  { 0x8700, 0x071b, 0x2000 },
+  { 0x0700, 0x071a, 0x0000 },
+  { 0x0700, 0x071c, 0x0000 },
+  { 0x8700, 0x0721, 0x3000 },
+  { 0x8700, 0x071f, 0x2000 },
+  { 0x0700, 0x071e, 0x0000 },
+  { 0x0700, 0x0720, 0x0000 },
+  { 0x8700, 0x0723, 0x2000 },
+  { 0x0700, 0x0722, 0x0000 },
+  { 0x0700, 0x0724, 0x0000 },
+  { 0x8700, 0x0797, 0x7000 },
+  { 0x8c00, 0x0745, 0x6000 },
+  { 0x8c00, 0x0735, 0x5000 },
+  { 0x8700, 0x072d, 0x4000 },
+  { 0x8700, 0x0729, 0x3000 },
+  { 0x8700, 0x0727, 0x2000 },
+  { 0x0700, 0x0726, 0x0000 },
+  { 0x0700, 0x0728, 0x0000 },
+  { 0x8700, 0x072b, 0x2000 },
+  { 0x0700, 0x072a, 0x0000 },
+  { 0x0700, 0x072c, 0x0000 },
+  { 0x8c00, 0x0731, 0x3000 },
+  { 0x8700, 0x072f, 0x2000 },
+  { 0x0700, 0x072e, 0x0000 },
+  { 0x0c00, 0x0730, 0x0000 },
+  { 0x8c00, 0x0733, 0x2000 },
+  { 0x0c00, 0x0732, 0x0000 },
+  { 0x0c00, 0x0734, 0x0000 },
+  { 0x8c00, 0x073d, 0x4000 },
+  { 0x8c00, 0x0739, 0x3000 },
+  { 0x8c00, 0x0737, 0x2000 },
+  { 0x0c00, 0x0736, 0x0000 },
+  { 0x0c00, 0x0738, 0x0000 },
+  { 0x8c00, 0x073b, 0x2000 },
+  { 0x0c00, 0x073a, 0x0000 },
+  { 0x0c00, 0x073c, 0x0000 },
+  { 0x8c00, 0x0741, 0x3000 },
+  { 0x8c00, 0x073f, 0x2000 },
+  { 0x0c00, 0x073e, 0x0000 },
+  { 0x0c00, 0x0740, 0x0000 },
+  { 0x8c00, 0x0743, 0x2000 },
+  { 0x0c00, 0x0742, 0x0000 },
+  { 0x0c00, 0x0744, 0x0000 },
+  { 0x8700, 0x0787, 0x5000 },
+  { 0x8700, 0x074f, 0x4000 },
+  { 0x8c00, 0x0749, 0x3000 },
+  { 0x8c00, 0x0747, 0x2000 },
+  { 0x0c00, 0x0746, 0x0000 },
+  { 0x0c00, 0x0748, 0x0000 },
+  { 0x8700, 0x074d, 0x2000 },
+  { 0x0c00, 0x074a, 0x0000 },
+  { 0x0700, 0x074e, 0x0000 },
+  { 0x8700, 0x0783, 0x3000 },
+  { 0x8700, 0x0781, 0x2000 },
+  { 0x0700, 0x0780, 0x0000 },
+  { 0x0700, 0x0782, 0x0000 },
+  { 0x8700, 0x0785, 0x2000 },
+  { 0x0700, 0x0784, 0x0000 },
+  { 0x0700, 0x0786, 0x0000 },
+  { 0x8700, 0x078f, 0x4000 },
+  { 0x8700, 0x078b, 0x3000 },
+  { 0x8700, 0x0789, 0x2000 },
+  { 0x0700, 0x0788, 0x0000 },
+  { 0x0700, 0x078a, 0x0000 },
+  { 0x8700, 0x078d, 0x2000 },
+  { 0x0700, 0x078c, 0x0000 },
+  { 0x0700, 0x078e, 0x0000 },
+  { 0x8700, 0x0793, 0x3000 },
+  { 0x8700, 0x0791, 0x2000 },
+  { 0x0700, 0x0790, 0x0000 },
+  { 0x0700, 0x0792, 0x0000 },
+  { 0x8700, 0x0795, 0x2000 },
+  { 0x0700, 0x0794, 0x0000 },
+  { 0x0700, 0x0796, 0x0000 },
+  { 0x8700, 0x0906, 0x6000 },
+  { 0x8c00, 0x07a7, 0x5000 },
+  { 0x8700, 0x079f, 0x4000 },
+  { 0x8700, 0x079b, 0x3000 },
+  { 0x8700, 0x0799, 0x2000 },
+  { 0x0700, 0x0798, 0x0000 },
+  { 0x0700, 0x079a, 0x0000 },
+  { 0x8700, 0x079d, 0x2000 },
+  { 0x0700, 0x079c, 0x0000 },
+  { 0x0700, 0x079e, 0x0000 },
+  { 0x8700, 0x07a3, 0x3000 },
+  { 0x8700, 0x07a1, 0x2000 },
+  { 0x0700, 0x07a0, 0x0000 },
+  { 0x0700, 0x07a2, 0x0000 },
+  { 0x8700, 0x07a5, 0x2000 },
+  { 0x0700, 0x07a4, 0x0000 },
+  { 0x0c00, 0x07a6, 0x0000 },
+  { 0x8c00, 0x07af, 0x4000 },
+  { 0x8c00, 0x07ab, 0x3000 },
+  { 0x8c00, 0x07a9, 0x2000 },
+  { 0x0c00, 0x07a8, 0x0000 },
+  { 0x0c00, 0x07aa, 0x0000 },
+  { 0x8c00, 0x07ad, 0x2000 },
+  { 0x0c00, 0x07ac, 0x0000 },
+  { 0x0c00, 0x07ae, 0x0000 },
+  { 0x8c00, 0x0902, 0x3000 },
+  { 0x8700, 0x07b1, 0x2000 },
+  { 0x0c00, 0x07b0, 0x0000 },
+  { 0x0c00, 0x0901, 0x0000 },
+  { 0x8700, 0x0904, 0x2000 },
+  { 0x0a00, 0x0903, 0x0000 },
+  { 0x0700, 0x0905, 0x0000 },
+  { 0x8700, 0x0916, 0x5000 },
+  { 0x8700, 0x090e, 0x4000 },
+  { 0x8700, 0x090a, 0x3000 },
+  { 0x8700, 0x0908, 0x2000 },
+  { 0x0700, 0x0907, 0x0000 },
+  { 0x0700, 0x0909, 0x0000 },
+  { 0x8700, 0x090c, 0x2000 },
+  { 0x0700, 0x090b, 0x0000 },
+  { 0x0700, 0x090d, 0x0000 },
+  { 0x8700, 0x0912, 0x3000 },
+  { 0x8700, 0x0910, 0x2000 },
+  { 0x0700, 0x090f, 0x0000 },
+  { 0x0700, 0x0911, 0x0000 },
+  { 0x8700, 0x0914, 0x2000 },
+  { 0x0700, 0x0913, 0x0000 },
+  { 0x0700, 0x0915, 0x0000 },
+  { 0x8700, 0x091e, 0x4000 },
+  { 0x8700, 0x091a, 0x3000 },
+  { 0x8700, 0x0918, 0x2000 },
+  { 0x0700, 0x0917, 0x0000 },
+  { 0x0700, 0x0919, 0x0000 },
+  { 0x8700, 0x091c, 0x2000 },
+  { 0x0700, 0x091b, 0x0000 },
+  { 0x0700, 0x091d, 0x0000 },
+  { 0x8700, 0x0922, 0x3000 },
+  { 0x8700, 0x0920, 0x2000 },
+  { 0x0700, 0x091f, 0x0000 },
+  { 0x0700, 0x0921, 0x0000 },
+  { 0x8700, 0x0924, 0x2000 },
+  { 0x0700, 0x0923, 0x0000 },
+  { 0x0700, 0x0925, 0x0000 },
+  { 0x8c00, 0x09cd, 0x8000 },
+  { 0x8d00, 0x096d, 0x7000 },
+  { 0x8c00, 0x0948, 0x6000 },
+  { 0x8700, 0x0936, 0x5000 },
+  { 0x8700, 0x092e, 0x4000 },
+  { 0x8700, 0x092a, 0x3000 },
+  { 0x8700, 0x0928, 0x2000 },
+  { 0x0700, 0x0927, 0x0000 },
+  { 0x0700, 0x0929, 0x0000 },
+  { 0x8700, 0x092c, 0x2000 },
+  { 0x0700, 0x092b, 0x0000 },
+  { 0x0700, 0x092d, 0x0000 },
+  { 0x8700, 0x0932, 0x3000 },
+  { 0x8700, 0x0930, 0x2000 },
+  { 0x0700, 0x092f, 0x0000 },
+  { 0x0700, 0x0931, 0x0000 },
+  { 0x8700, 0x0934, 0x2000 },
+  { 0x0700, 0x0933, 0x0000 },
+  { 0x0700, 0x0935, 0x0000 },
+  { 0x8a00, 0x0940, 0x4000 },
+  { 0x8c00, 0x093c, 0x3000 },
+  { 0x8700, 0x0938, 0x2000 },
+  { 0x0700, 0x0937, 0x0000 },
+  { 0x0700, 0x0939, 0x0000 },
+  { 0x8a00, 0x093e, 0x2000 },
+  { 0x0700, 0x093d, 0x0000 },
+  { 0x0a00, 0x093f, 0x0000 },
+  { 0x8c00, 0x0944, 0x3000 },
+  { 0x8c00, 0x0942, 0x2000 },
+  { 0x0c00, 0x0941, 0x0000 },
+  { 0x0c00, 0x0943, 0x0000 },
+  { 0x8c00, 0x0946, 0x2000 },
+  { 0x0c00, 0x0945, 0x0000 },
+  { 0x0c00, 0x0947, 0x0000 },
+  { 0x8700, 0x095d, 0x5000 },
+  { 0x8c00, 0x0952, 0x4000 },
+  { 0x8a00, 0x094c, 0x3000 },
+  { 0x8a00, 0x094a, 0x2000 },
+  { 0x0a00, 0x0949, 0x0000 },
+  { 0x0a00, 0x094b, 0x0000 },
+  { 0x8700, 0x0950, 0x2000 },
+  { 0x0c00, 0x094d, 0x0000 },
+  { 0x0c00, 0x0951, 0x0000 },
+  { 0x8700, 0x0959, 0x3000 },
+  { 0x8c00, 0x0954, 0x2000 },
+  { 0x0c00, 0x0953, 0x0000 },
+  { 0x0700, 0x0958, 0x0000 },
+  { 0x8700, 0x095b, 0x2000 },
+  { 0x0700, 0x095a, 0x0000 },
+  { 0x0700, 0x095c, 0x0000 },
+  { 0x9500, 0x0965, 0x4000 },
+  { 0x8700, 0x0961, 0x3000 },
+  { 0x8700, 0x095f, 0x2000 },
+  { 0x0700, 0x095e, 0x0000 },
+  { 0x0700, 0x0960, 0x0000 },
+  { 0x8c00, 0x0963, 0x2000 },
+  { 0x0c00, 0x0962, 0x0000 },
+  { 0x1500, 0x0964, 0x0000 },
+  { 0x8d00, 0x0969, 0x3000 },
+  { 0x8d00, 0x0967, 0x2000 },
+  { 0x0d00, 0x0966, 0x0000 },
+  { 0x0d00, 0x0968, 0x0000 },
+  { 0x8d00, 0x096b, 0x2000 },
+  { 0x0d00, 0x096a, 0x0000 },
+  { 0x0d00, 0x096c, 0x0000 },
+  { 0x8700, 0x09a2, 0x6000 },
+  { 0x8700, 0x0990, 0x5000 },
+  { 0x8700, 0x0986, 0x4000 },
+  { 0x8c00, 0x0981, 0x3000 },
+  { 0x8d00, 0x096f, 0x2000 },
+  { 0x0d00, 0x096e, 0x0000 },
+  { 0x1500, 0x0970, 0x0000 },
+  { 0x8a00, 0x0983, 0x2000 },
+  { 0x0a00, 0x0982, 0x0000 },
+  { 0x0700, 0x0985, 0x0000 },
+  { 0x8700, 0x098a, 0x3000 },
+  { 0x8700, 0x0988, 0x2000 },
+  { 0x0700, 0x0987, 0x0000 },
+  { 0x0700, 0x0989, 0x0000 },
+  { 0x8700, 0x098c, 0x2000 },
+  { 0x0700, 0x098b, 0x0000 },
+  { 0x0700, 0x098f, 0x0000 },
+  { 0x8700, 0x099a, 0x4000 },
+  { 0x8700, 0x0996, 0x3000 },
+  { 0x8700, 0x0994, 0x2000 },
+  { 0x0700, 0x0993, 0x0000 },
+  { 0x0700, 0x0995, 0x0000 },
+  { 0x8700, 0x0998, 0x2000 },
+  { 0x0700, 0x0997, 0x0000 },
+  { 0x0700, 0x0999, 0x0000 },
+  { 0x8700, 0x099e, 0x3000 },
+  { 0x8700, 0x099c, 0x2000 },
+  { 0x0700, 0x099b, 0x0000 },
+  { 0x0700, 0x099d, 0x0000 },
+  { 0x8700, 0x09a0, 0x2000 },
+  { 0x0700, 0x099f, 0x0000 },
+  { 0x0700, 0x09a1, 0x0000 },
+  { 0x8700, 0x09b7, 0x5000 },
+  { 0x8700, 0x09ab, 0x4000 },
+  { 0x8700, 0x09a6, 0x3000 },
+  { 0x8700, 0x09a4, 0x2000 },
+  { 0x0700, 0x09a3, 0x0000 },
+  { 0x0700, 0x09a5, 0x0000 },
+  { 0x8700, 0x09a8, 0x2000 },
+  { 0x0700, 0x09a7, 0x0000 },
+  { 0x0700, 0x09aa, 0x0000 },
+  { 0x8700, 0x09af, 0x3000 },
+  { 0x8700, 0x09ad, 0x2000 },
+  { 0x0700, 0x09ac, 0x0000 },
+  { 0x0700, 0x09ae, 0x0000 },
+  { 0x8700, 0x09b2, 0x2000 },
+  { 0x0700, 0x09b0, 0x0000 },
+  { 0x0700, 0x09b6, 0x0000 },
+  { 0x8c00, 0x09c1, 0x4000 },
+  { 0x8700, 0x09bd, 0x3000 },
+  { 0x8700, 0x09b9, 0x2000 },
+  { 0x0700, 0x09b8, 0x0000 },
+  { 0x0c00, 0x09bc, 0x0000 },
+  { 0x8a00, 0x09bf, 0x2000 },
+  { 0x0a00, 0x09be, 0x0000 },
+  { 0x0a00, 0x09c0, 0x0000 },
+  { 0x8a00, 0x09c7, 0x3000 },
+  { 0x8c00, 0x09c3, 0x2000 },
+  { 0x0c00, 0x09c2, 0x0000 },
+  { 0x0c00, 0x09c4, 0x0000 },
+  { 0x8a00, 0x09cb, 0x2000 },
+  { 0x0a00, 0x09c8, 0x0000 },
+  { 0x0a00, 0x09cc, 0x0000 },
+  { 0x8700, 0x0a2b, 0x7000 },
+  { 0x8a00, 0x0a03, 0x6000 },
+  { 0x8d00, 0x09ed, 0x5000 },
+  { 0x8c00, 0x09e3, 0x4000 },
+  { 0x8700, 0x09df, 0x3000 },
+  { 0x8700, 0x09dc, 0x2000 },
+  { 0x0a00, 0x09d7, 0x0000 },
+  { 0x0700, 0x09dd, 0x0000 },
+  { 0x8700, 0x09e1, 0x2000 },
+  { 0x0700, 0x09e0, 0x0000 },
+  { 0x0c00, 0x09e2, 0x0000 },
+  { 0x8d00, 0x09e9, 0x3000 },
+  { 0x8d00, 0x09e7, 0x2000 },
+  { 0x0d00, 0x09e6, 0x0000 },
+  { 0x0d00, 0x09e8, 0x0000 },
+  { 0x8d00, 0x09eb, 0x2000 },
+  { 0x0d00, 0x09ea, 0x0000 },
+  { 0x0d00, 0x09ec, 0x0000 },
+  { 0x8f00, 0x09f5, 0x4000 },
+  { 0x8700, 0x09f1, 0x3000 },
+  { 0x8d00, 0x09ef, 0x2000 },
+  { 0x0d00, 0x09ee, 0x0000 },
+  { 0x0700, 0x09f0, 0x0000 },
+  { 0x9700, 0x09f3, 0x2000 },
+  { 0x1700, 0x09f2, 0x0000 },
+  { 0x0f00, 0x09f4, 0x0000 },
+  { 0x8f00, 0x09f9, 0x3000 },
+  { 0x8f00, 0x09f7, 0x2000 },
+  { 0x0f00, 0x09f6, 0x0000 },
+  { 0x0f00, 0x09f8, 0x0000 },
+  { 0x8c00, 0x0a01, 0x2000 },
+  { 0x1a00, 0x09fa, 0x0000 },
+  { 0x0c00, 0x0a02, 0x0000 },
+  { 0x8700, 0x0a1a, 0x5000 },
+  { 0x8700, 0x0a10, 0x4000 },
+  { 0x8700, 0x0a08, 0x3000 },
+  { 0x8700, 0x0a06, 0x2000 },
+  { 0x0700, 0x0a05, 0x0000 },
+  { 0x0700, 0x0a07, 0x0000 },
+  { 0x8700, 0x0a0a, 0x2000 },
+  { 0x0700, 0x0a09, 0x0000 },
+  { 0x0700, 0x0a0f, 0x0000 },
+  { 0x8700, 0x0a16, 0x3000 },
+  { 0x8700, 0x0a14, 0x2000 },
+  { 0x0700, 0x0a13, 0x0000 },
+  { 0x0700, 0x0a15, 0x0000 },
+  { 0x8700, 0x0a18, 0x2000 },
+  { 0x0700, 0x0a17, 0x0000 },
+  { 0x0700, 0x0a19, 0x0000 },
+  { 0x8700, 0x0a22, 0x4000 },
+  { 0x8700, 0x0a1e, 0x3000 },
+  { 0x8700, 0x0a1c, 0x2000 },
+  { 0x0700, 0x0a1b, 0x0000 },
+  { 0x0700, 0x0a1d, 0x0000 },
+  { 0x8700, 0x0a20, 0x2000 },
+  { 0x0700, 0x0a1f, 0x0000 },
+  { 0x0700, 0x0a21, 0x0000 },
+  { 0x8700, 0x0a26, 0x3000 },
+  { 0x8700, 0x0a24, 0x2000 },
+  { 0x0700, 0x0a23, 0x0000 },
+  { 0x0700, 0x0a25, 0x0000 },
+  { 0x8700, 0x0a28, 0x2000 },
+  { 0x0700, 0x0a27, 0x0000 },
+  { 0x0700, 0x0a2a, 0x0000 },
+  { 0x8d00, 0x0a6a, 0x6000 },
+  { 0x8c00, 0x0a41, 0x5000 },
+  { 0x8700, 0x0a35, 0x4000 },
+  { 0x8700, 0x0a2f, 0x3000 },
+  { 0x8700, 0x0a2d, 0x2000 },
+  { 0x0700, 0x0a2c, 0x0000 },
+  { 0x0700, 0x0a2e, 0x0000 },
+  { 0x8700, 0x0a32, 0x2000 },
+  { 0x0700, 0x0a30, 0x0000 },
+  { 0x0700, 0x0a33, 0x0000 },
+  { 0x8c00, 0x0a3c, 0x3000 },
+  { 0x8700, 0x0a38, 0x2000 },
+  { 0x0700, 0x0a36, 0x0000 },
+  { 0x0700, 0x0a39, 0x0000 },
+  { 0x8a00, 0x0a3f, 0x2000 },
+  { 0x0a00, 0x0a3e, 0x0000 },
+  { 0x0a00, 0x0a40, 0x0000 },
+  { 0x8700, 0x0a5a, 0x4000 },
+  { 0x8c00, 0x0a4b, 0x3000 },
+  { 0x8c00, 0x0a47, 0x2000 },
+  { 0x0c00, 0x0a42, 0x0000 },
+  { 0x0c00, 0x0a48, 0x0000 },
+  { 0x8c00, 0x0a4d, 0x2000 },
+  { 0x0c00, 0x0a4c, 0x0000 },
+  { 0x0700, 0x0a59, 0x0000 },
+  { 0x8d00, 0x0a66, 0x3000 },
+  { 0x8700, 0x0a5c, 0x2000 },
+  { 0x0700, 0x0a5b, 0x0000 },
+  { 0x0700, 0x0a5e, 0x0000 },
+  { 0x8d00, 0x0a68, 0x2000 },
+  { 0x0d00, 0x0a67, 0x0000 },
+  { 0x0d00, 0x0a69, 0x0000 },
+  { 0x8700, 0x0a87, 0x5000 },
+  { 0x8700, 0x0a72, 0x4000 },
+  { 0x8d00, 0x0a6e, 0x3000 },
+  { 0x8d00, 0x0a6c, 0x2000 },
+  { 0x0d00, 0x0a6b, 0x0000 },
+  { 0x0d00, 0x0a6d, 0x0000 },
+  { 0x8c00, 0x0a70, 0x2000 },
+  { 0x0d00, 0x0a6f, 0x0000 },
+  { 0x0c00, 0x0a71, 0x0000 },
+  { 0x8c00, 0x0a82, 0x3000 },
+  { 0x8700, 0x0a74, 0x2000 },
+  { 0x0700, 0x0a73, 0x0000 },
+  { 0x0c00, 0x0a81, 0x0000 },
+  { 0x8700, 0x0a85, 0x2000 },
+  { 0x0a00, 0x0a83, 0x0000 },
+  { 0x0700, 0x0a86, 0x0000 },
+  { 0x8700, 0x0a90, 0x4000 },
+  { 0x8700, 0x0a8b, 0x3000 },
+  { 0x8700, 0x0a89, 0x2000 },
+  { 0x0700, 0x0a88, 0x0000 },
+  { 0x0700, 0x0a8a, 0x0000 },
+  { 0x8700, 0x0a8d, 0x2000 },
+  { 0x0700, 0x0a8c, 0x0000 },
+  { 0x0700, 0x0a8f, 0x0000 },
+  { 0x8700, 0x0a95, 0x3000 },
+  { 0x8700, 0x0a93, 0x2000 },
+  { 0x0700, 0x0a91, 0x0000 },
+  { 0x0700, 0x0a94, 0x0000 },
+  { 0x8700, 0x0a97, 0x2000 },
+  { 0x0700, 0x0a96, 0x0000 },
+  { 0x0700, 0x0a98, 0x0000 },
+  { 0x8700, 0x10ef, 0xb000 },
+  { 0x8700, 0x0dc6, 0xa000 },
+  { 0x8700, 0x0c31, 0x9000 },
+  { 0x8700, 0x0b5f, 0x8000 },
+  { 0x8a00, 0x0b03, 0x7000 },
+  { 0x8a00, 0x0abe, 0x6000 },
+  { 0x8700, 0x0aaa, 0x5000 },
+  { 0x8700, 0x0aa1, 0x4000 },
+  { 0x8700, 0x0a9d, 0x3000 },
+  { 0x8700, 0x0a9b, 0x2000 },
+  { 0x0700, 0x0a9a, 0x0000 },
+  { 0x0700, 0x0a9c, 0x0000 },
+  { 0x8700, 0x0a9f, 0x2000 },
+  { 0x0700, 0x0a9e, 0x0000 },
+  { 0x0700, 0x0aa0, 0x0000 },
+  { 0x8700, 0x0aa5, 0x3000 },
+  { 0x8700, 0x0aa3, 0x2000 },
+  { 0x0700, 0x0aa2, 0x0000 },
+  { 0x0700, 0x0aa4, 0x0000 },
+  { 0x8700, 0x0aa7, 0x2000 },
+  { 0x0700, 0x0aa6, 0x0000 },
+  { 0x0700, 0x0aa8, 0x0000 },
+  { 0x8700, 0x0ab3, 0x4000 },
+  { 0x8700, 0x0aae, 0x3000 },
+  { 0x8700, 0x0aac, 0x2000 },
+  { 0x0700, 0x0aab, 0x0000 },
+  { 0x0700, 0x0aad, 0x0000 },
+  { 0x8700, 0x0ab0, 0x2000 },
+  { 0x0700, 0x0aaf, 0x0000 },
+  { 0x0700, 0x0ab2, 0x0000 },
+  { 0x8700, 0x0ab8, 0x3000 },
+  { 0x8700, 0x0ab6, 0x2000 },
+  { 0x0700, 0x0ab5, 0x0000 },
+  { 0x0700, 0x0ab7, 0x0000 },
+  { 0x8c00, 0x0abc, 0x2000 },
+  { 0x0700, 0x0ab9, 0x0000 },
+  { 0x0700, 0x0abd, 0x0000 },
+  { 0x8700, 0x0ae1, 0x5000 },
+  { 0x8c00, 0x0ac7, 0x4000 },
+  { 0x8c00, 0x0ac2, 0x3000 },
+  { 0x8a00, 0x0ac0, 0x2000 },
+  { 0x0a00, 0x0abf, 0x0000 },
+  { 0x0c00, 0x0ac1, 0x0000 },
+  { 0x8c00, 0x0ac4, 0x2000 },
+  { 0x0c00, 0x0ac3, 0x0000 },
+  { 0x0c00, 0x0ac5, 0x0000 },
+  { 0x8a00, 0x0acc, 0x3000 },
+  { 0x8a00, 0x0ac9, 0x2000 },
+  { 0x0c00, 0x0ac8, 0x0000 },
+  { 0x0a00, 0x0acb, 0x0000 },
+  { 0x8700, 0x0ad0, 0x2000 },
+  { 0x0c00, 0x0acd, 0x0000 },
+  { 0x0700, 0x0ae0, 0x0000 },
+  { 0x8d00, 0x0aeb, 0x4000 },
+  { 0x8d00, 0x0ae7, 0x3000 },
+  { 0x8c00, 0x0ae3, 0x2000 },
+  { 0x0c00, 0x0ae2, 0x0000 },
+  { 0x0d00, 0x0ae6, 0x0000 },
+  { 0x8d00, 0x0ae9, 0x2000 },
+  { 0x0d00, 0x0ae8, 0x0000 },
+  { 0x0d00, 0x0aea, 0x0000 },
+  { 0x8d00, 0x0aef, 0x3000 },
+  { 0x8d00, 0x0aed, 0x2000 },
+  { 0x0d00, 0x0aec, 0x0000 },
+  { 0x0d00, 0x0aee, 0x0000 },
+  { 0x8c00, 0x0b01, 0x2000 },
+  { 0x1700, 0x0af1, 0x0000 },
+  { 0x0a00, 0x0b02, 0x0000 },
+  { 0x8700, 0x0b28, 0x6000 },
+  { 0x8700, 0x0b18, 0x5000 },
+  { 0x8700, 0x0b0c, 0x4000 },
+  { 0x8700, 0x0b08, 0x3000 },
+  { 0x8700, 0x0b06, 0x2000 },
+  { 0x0700, 0x0b05, 0x0000 },
+  { 0x0700, 0x0b07, 0x0000 },
+  { 0x8700, 0x0b0a, 0x2000 },
+  { 0x0700, 0x0b09, 0x0000 },
+  { 0x0700, 0x0b0b, 0x0000 },
+  { 0x8700, 0x0b14, 0x3000 },
+  { 0x8700, 0x0b10, 0x2000 },
+  { 0x0700, 0x0b0f, 0x0000 },
+  { 0x0700, 0x0b13, 0x0000 },
+  { 0x8700, 0x0b16, 0x2000 },
+  { 0x0700, 0x0b15, 0x0000 },
+  { 0x0700, 0x0b17, 0x0000 },
+  { 0x8700, 0x0b20, 0x4000 },
+  { 0x8700, 0x0b1c, 0x3000 },
+  { 0x8700, 0x0b1a, 0x2000 },
+  { 0x0700, 0x0b19, 0x0000 },
+  { 0x0700, 0x0b1b, 0x0000 },
+  { 0x8700, 0x0b1e, 0x2000 },
+  { 0x0700, 0x0b1d, 0x0000 },
+  { 0x0700, 0x0b1f, 0x0000 },
+  { 0x8700, 0x0b24, 0x3000 },
+  { 0x8700, 0x0b22, 0x2000 },
+  { 0x0700, 0x0b21, 0x0000 },
+  { 0x0700, 0x0b23, 0x0000 },
+  { 0x8700, 0x0b26, 0x2000 },
+  { 0x0700, 0x0b25, 0x0000 },
+  { 0x0700, 0x0b27, 0x0000 },
+  { 0x8700, 0x0b3d, 0x5000 },
+  { 0x8700, 0x0b32, 0x4000 },
+  { 0x8700, 0x0b2d, 0x3000 },
+  { 0x8700, 0x0b2b, 0x2000 },
+  { 0x0700, 0x0b2a, 0x0000 },
+  { 0x0700, 0x0b2c, 0x0000 },
+  { 0x8700, 0x0b2f, 0x2000 },
+  { 0x0700, 0x0b2e, 0x0000 },
+  { 0x0700, 0x0b30, 0x0000 },
+  { 0x8700, 0x0b37, 0x3000 },
+  { 0x8700, 0x0b35, 0x2000 },
+  { 0x0700, 0x0b33, 0x0000 },
+  { 0x0700, 0x0b36, 0x0000 },
+  { 0x8700, 0x0b39, 0x2000 },
+  { 0x0700, 0x0b38, 0x0000 },
+  { 0x0c00, 0x0b3c, 0x0000 },
+  { 0x8a00, 0x0b48, 0x4000 },
+  { 0x8c00, 0x0b41, 0x3000 },
+  { 0x8c00, 0x0b3f, 0x2000 },
+  { 0x0a00, 0x0b3e, 0x0000 },
+  { 0x0a00, 0x0b40, 0x0000 },
+  { 0x8c00, 0x0b43, 0x2000 },
+  { 0x0c00, 0x0b42, 0x0000 },
+  { 0x0a00, 0x0b47, 0x0000 },
+  { 0x8c00, 0x0b56, 0x3000 },
+  { 0x8a00, 0x0b4c, 0x2000 },
+  { 0x0a00, 0x0b4b, 0x0000 },
+  { 0x0c00, 0x0b4d, 0x0000 },
+  { 0x8700, 0x0b5c, 0x2000 },
+  { 0x0a00, 0x0b57, 0x0000 },
+  { 0x0700, 0x0b5d, 0x0000 },
+  { 0x8d00, 0x0be7, 0x7000 },
+  { 0x8700, 0x0b9c, 0x6000 },
+  { 0x8700, 0x0b83, 0x5000 },
+  { 0x8d00, 0x0b6b, 0x4000 },
+  { 0x8d00, 0x0b67, 0x3000 },
+  { 0x8700, 0x0b61, 0x2000 },
+  { 0x0700, 0x0b60, 0x0000 },
+  { 0x0d00, 0x0b66, 0x0000 },
+  { 0x8d00, 0x0b69, 0x2000 },
+  { 0x0d00, 0x0b68, 0x0000 },
+  { 0x0d00, 0x0b6a, 0x0000 },
+  { 0x8d00, 0x0b6f, 0x3000 },
+  { 0x8d00, 0x0b6d, 0x2000 },
+  { 0x0d00, 0x0b6c, 0x0000 },
+  { 0x0d00, 0x0b6e, 0x0000 },
+  { 0x8700, 0x0b71, 0x2000 },
+  { 0x1a00, 0x0b70, 0x0000 },
+  { 0x0c00, 0x0b82, 0x0000 },
+  { 0x8700, 0x0b8f, 0x4000 },
+  { 0x8700, 0x0b88, 0x3000 },
+  { 0x8700, 0x0b86, 0x2000 },
+  { 0x0700, 0x0b85, 0x0000 },
+  { 0x0700, 0x0b87, 0x0000 },
+  { 0x8700, 0x0b8a, 0x2000 },
+  { 0x0700, 0x0b89, 0x0000 },
+  { 0x0700, 0x0b8e, 0x0000 },
+  { 0x8700, 0x0b94, 0x3000 },
+  { 0x8700, 0x0b92, 0x2000 },
+  { 0x0700, 0x0b90, 0x0000 },
+  { 0x0700, 0x0b93, 0x0000 },
+  { 0x8700, 0x0b99, 0x2000 },
+  { 0x0700, 0x0b95, 0x0000 },
+  { 0x0700, 0x0b9a, 0x0000 },
+  { 0x8700, 0x0bb7, 0x5000 },
+  { 0x8700, 0x0bae, 0x4000 },
+  { 0x8700, 0x0ba4, 0x3000 },
+  { 0x8700, 0x0b9f, 0x2000 },
+  { 0x0700, 0x0b9e, 0x0000 },
+  { 0x0700, 0x0ba3, 0x0000 },
+  { 0x8700, 0x0ba9, 0x2000 },
+  { 0x0700, 0x0ba8, 0x0000 },
+  { 0x0700, 0x0baa, 0x0000 },
+  { 0x8700, 0x0bb2, 0x3000 },
+  { 0x8700, 0x0bb0, 0x2000 },
+  { 0x0700, 0x0baf, 0x0000 },
+  { 0x0700, 0x0bb1, 0x0000 },
+  { 0x8700, 0x0bb4, 0x2000 },
+  { 0x0700, 0x0bb3, 0x0000 },
+  { 0x0700, 0x0bb5, 0x0000 },
+  { 0x8a00, 0x0bc6, 0x4000 },
+  { 0x8a00, 0x0bbf, 0x3000 },
+  { 0x8700, 0x0bb9, 0x2000 },
+  { 0x0700, 0x0bb8, 0x0000 },
+  { 0x0a00, 0x0bbe, 0x0000 },
+  { 0x8a00, 0x0bc1, 0x2000 },
+  { 0x0c00, 0x0bc0, 0x0000 },
+  { 0x0a00, 0x0bc2, 0x0000 },
+  { 0x8a00, 0x0bcb, 0x3000 },
+  { 0x8a00, 0x0bc8, 0x2000 },
+  { 0x0a00, 0x0bc7, 0x0000 },
+  { 0x0a00, 0x0bca, 0x0000 },
+  { 0x8c00, 0x0bcd, 0x2000 },
+  { 0x0a00, 0x0bcc, 0x0000 },
+  { 0x0a00, 0x0bd7, 0x0000 },
+  { 0x8700, 0x0c0f, 0x6000 },
+  { 0x9a00, 0x0bf7, 0x5000 },
+  { 0x8d00, 0x0bef, 0x4000 },
+  { 0x8d00, 0x0beb, 0x3000 },
+  { 0x8d00, 0x0be9, 0x2000 },
+  { 0x0d00, 0x0be8, 0x0000 },
+  { 0x0d00, 0x0bea, 0x0000 },
+  { 0x8d00, 0x0bed, 0x2000 },
+  { 0x0d00, 0x0bec, 0x0000 },
+  { 0x0d00, 0x0bee, 0x0000 },
+  { 0x9a00, 0x0bf3, 0x3000 },
+  { 0x8f00, 0x0bf1, 0x2000 },
+  { 0x0f00, 0x0bf0, 0x0000 },
+  { 0x0f00, 0x0bf2, 0x0000 },
+  { 0x9a00, 0x0bf5, 0x2000 },
+  { 0x1a00, 0x0bf4, 0x0000 },
+  { 0x1a00, 0x0bf6, 0x0000 },
+  { 0x8700, 0x0c06, 0x4000 },
+  { 0x8a00, 0x0c01, 0x3000 },
+  { 0x9700, 0x0bf9, 0x2000 },
+  { 0x1a00, 0x0bf8, 0x0000 },
+  { 0x1a00, 0x0bfa, 0x0000 },
+  { 0x8a00, 0x0c03, 0x2000 },
+  { 0x0a00, 0x0c02, 0x0000 },
+  { 0x0700, 0x0c05, 0x0000 },
+  { 0x8700, 0x0c0a, 0x3000 },
+  { 0x8700, 0x0c08, 0x2000 },
+  { 0x0700, 0x0c07, 0x0000 },
+  { 0x0700, 0x0c09, 0x0000 },
+  { 0x8700, 0x0c0c, 0x2000 },
+  { 0x0700, 0x0c0b, 0x0000 },
+  { 0x0700, 0x0c0e, 0x0000 },
+  { 0x8700, 0x0c20, 0x5000 },
+  { 0x8700, 0x0c18, 0x4000 },
+  { 0x8700, 0x0c14, 0x3000 },
+  { 0x8700, 0x0c12, 0x2000 },
+  { 0x0700, 0x0c10, 0x0000 },
+  { 0x0700, 0x0c13, 0x0000 },
+  { 0x8700, 0x0c16, 0x2000 },
+  { 0x0700, 0x0c15, 0x0000 },
+  { 0x0700, 0x0c17, 0x0000 },
+  { 0x8700, 0x0c1c, 0x3000 },
+  { 0x8700, 0x0c1a, 0x2000 },
+  { 0x0700, 0x0c19, 0x0000 },
+  { 0x0700, 0x0c1b, 0x0000 },
+  { 0x8700, 0x0c1e, 0x2000 },
+  { 0x0700, 0x0c1d, 0x0000 },
+  { 0x0700, 0x0c1f, 0x0000 },
+  { 0x8700, 0x0c28, 0x4000 },
+  { 0x8700, 0x0c24, 0x3000 },
+  { 0x8700, 0x0c22, 0x2000 },
+  { 0x0700, 0x0c21, 0x0000 },
+  { 0x0700, 0x0c23, 0x0000 },
+  { 0x8700, 0x0c26, 0x2000 },
+  { 0x0700, 0x0c25, 0x0000 },
+  { 0x0700, 0x0c27, 0x0000 },
+  { 0x8700, 0x0c2d, 0x3000 },
+  { 0x8700, 0x0c2b, 0x2000 },
+  { 0x0700, 0x0c2a, 0x0000 },
+  { 0x0700, 0x0c2c, 0x0000 },
+  { 0x8700, 0x0c2f, 0x2000 },
+  { 0x0700, 0x0c2e, 0x0000 },
+  { 0x0700, 0x0c30, 0x0000 },
+  { 0x8700, 0x0d0e, 0x8000 },
+  { 0x8700, 0x0ca1, 0x7000 },
+  { 0x8d00, 0x0c6c, 0x6000 },
+  { 0x8c00, 0x0c47, 0x5000 },
+  { 0x8c00, 0x0c3e, 0x4000 },
+  { 0x8700, 0x0c36, 0x3000 },
+  { 0x8700, 0x0c33, 0x2000 },
+  { 0x0700, 0x0c32, 0x0000 },
+  { 0x0700, 0x0c35, 0x0000 },
+  { 0x8700, 0x0c38, 0x2000 },
+  { 0x0700, 0x0c37, 0x0000 },
+  { 0x0700, 0x0c39, 0x0000 },
+  { 0x8a00, 0x0c42, 0x3000 },
+  { 0x8c00, 0x0c40, 0x2000 },
+  { 0x0c00, 0x0c3f, 0x0000 },
+  { 0x0a00, 0x0c41, 0x0000 },
+  { 0x8a00, 0x0c44, 0x2000 },
+  { 0x0a00, 0x0c43, 0x0000 },
+  { 0x0c00, 0x0c46, 0x0000 },
+  { 0x8700, 0x0c60, 0x4000 },
+  { 0x8c00, 0x0c4c, 0x3000 },
+  { 0x8c00, 0x0c4a, 0x2000 },
+  { 0x0c00, 0x0c48, 0x0000 },
+  { 0x0c00, 0x0c4b, 0x0000 },
+  { 0x8c00, 0x0c55, 0x2000 },
+  { 0x0c00, 0x0c4d, 0x0000 },
+  { 0x0c00, 0x0c56, 0x0000 },
+  { 0x8d00, 0x0c68, 0x3000 },
+  { 0x8d00, 0x0c66, 0x2000 },
+  { 0x0700, 0x0c61, 0x0000 },
+  { 0x0d00, 0x0c67, 0x0000 },
+  { 0x8d00, 0x0c6a, 0x2000 },
+  { 0x0d00, 0x0c69, 0x0000 },
+  { 0x0d00, 0x0c6b, 0x0000 },
+  { 0x8700, 0x0c90, 0x5000 },
+  { 0x8700, 0x0c87, 0x4000 },
+  { 0x8a00, 0x0c82, 0x3000 },
+  { 0x8d00, 0x0c6e, 0x2000 },
+  { 0x0d00, 0x0c6d, 0x0000 },
+  { 0x0d00, 0x0c6f, 0x0000 },
+  { 0x8700, 0x0c85, 0x2000 },
+  { 0x0a00, 0x0c83, 0x0000 },
+  { 0x0700, 0x0c86, 0x0000 },
+  { 0x8700, 0x0c8b, 0x3000 },
+  { 0x8700, 0x0c89, 0x2000 },
+  { 0x0700, 0x0c88, 0x0000 },
+  { 0x0700, 0x0c8a, 0x0000 },
+  { 0x8700, 0x0c8e, 0x2000 },
+  { 0x0700, 0x0c8c, 0x0000 },
+  { 0x0700, 0x0c8f, 0x0000 },
+  { 0x8700, 0x0c99, 0x4000 },
+  { 0x8700, 0x0c95, 0x3000 },
+  { 0x8700, 0x0c93, 0x2000 },
+  { 0x0700, 0x0c92, 0x0000 },
+  { 0x0700, 0x0c94, 0x0000 },
+  { 0x8700, 0x0c97, 0x2000 },
+  { 0x0700, 0x0c96, 0x0000 },
+  { 0x0700, 0x0c98, 0x0000 },
+  { 0x8700, 0x0c9d, 0x3000 },
+  { 0x8700, 0x0c9b, 0x2000 },
+  { 0x0700, 0x0c9a, 0x0000 },
+  { 0x0700, 0x0c9c, 0x0000 },
+  { 0x8700, 0x0c9f, 0x2000 },
+  { 0x0700, 0x0c9e, 0x0000 },
+  { 0x0700, 0x0ca0, 0x0000 },
+  { 0x8c00, 0x0cc6, 0x6000 },
+  { 0x8700, 0x0cb2, 0x5000 },
+  { 0x8700, 0x0caa, 0x4000 },
+  { 0x8700, 0x0ca5, 0x3000 },
+  { 0x8700, 0x0ca3, 0x2000 },
+  { 0x0700, 0x0ca2, 0x0000 },
+  { 0x0700, 0x0ca4, 0x0000 },
+  { 0x8700, 0x0ca7, 0x2000 },
+  { 0x0700, 0x0ca6, 0x0000 },
+  { 0x0700, 0x0ca8, 0x0000 },
+  { 0x8700, 0x0cae, 0x3000 },
+  { 0x8700, 0x0cac, 0x2000 },
+  { 0x0700, 0x0cab, 0x0000 },
+  { 0x0700, 0x0cad, 0x0000 },
+  { 0x8700, 0x0cb0, 0x2000 },
+  { 0x0700, 0x0caf, 0x0000 },
+  { 0x0700, 0x0cb1, 0x0000 },
+  { 0x8700, 0x0cbd, 0x4000 },
+  { 0x8700, 0x0cb7, 0x3000 },
+  { 0x8700, 0x0cb5, 0x2000 },
+  { 0x0700, 0x0cb3, 0x0000 },
+  { 0x0700, 0x0cb6, 0x0000 },
+  { 0x8700, 0x0cb9, 0x2000 },
+  { 0x0700, 0x0cb8, 0x0000 },
+  { 0x0c00, 0x0cbc, 0x0000 },
+  { 0x8a00, 0x0cc1, 0x3000 },
+  { 0x8c00, 0x0cbf, 0x2000 },
+  { 0x0a00, 0x0cbe, 0x0000 },
+  { 0x0a00, 0x0cc0, 0x0000 },
+  { 0x8a00, 0x0cc3, 0x2000 },
+  { 0x0a00, 0x0cc2, 0x0000 },
+  { 0x0a00, 0x0cc4, 0x0000 },
+  { 0x8d00, 0x0cea, 0x5000 },
+  { 0x8a00, 0x0cd6, 0x4000 },
+  { 0x8a00, 0x0ccb, 0x3000 },
+  { 0x8a00, 0x0cc8, 0x2000 },
+  { 0x0a00, 0x0cc7, 0x0000 },
+  { 0x0a00, 0x0cca, 0x0000 },
+  { 0x8c00, 0x0ccd, 0x2000 },
+  { 0x0c00, 0x0ccc, 0x0000 },
+  { 0x0a00, 0x0cd5, 0x0000 },
+  { 0x8d00, 0x0ce6, 0x3000 },
+  { 0x8700, 0x0ce0, 0x2000 },
+  { 0x0700, 0x0cde, 0x0000 },
+  { 0x0700, 0x0ce1, 0x0000 },
+  { 0x8d00, 0x0ce8, 0x2000 },
+  { 0x0d00, 0x0ce7, 0x0000 },
+  { 0x0d00, 0x0ce9, 0x0000 },
+  { 0x8700, 0x0d05, 0x4000 },
+  { 0x8d00, 0x0cee, 0x3000 },
+  { 0x8d00, 0x0cec, 0x2000 },
+  { 0x0d00, 0x0ceb, 0x0000 },
+  { 0x0d00, 0x0ced, 0x0000 },
+  { 0x8a00, 0x0d02, 0x2000 },
+  { 0x0d00, 0x0cef, 0x0000 },
+  { 0x0a00, 0x0d03, 0x0000 },
+  { 0x8700, 0x0d09, 0x3000 },
+  { 0x8700, 0x0d07, 0x2000 },
+  { 0x0700, 0x0d06, 0x0000 },
+  { 0x0700, 0x0d08, 0x0000 },
+  { 0x8700, 0x0d0b, 0x2000 },
+  { 0x0700, 0x0d0a, 0x0000 },
+  { 0x0700, 0x0d0c, 0x0000 },
+  { 0x8d00, 0x0d6c, 0x7000 },
+  { 0x8700, 0x0d30, 0x6000 },
+  { 0x8700, 0x0d1f, 0x5000 },
+  { 0x8700, 0x0d17, 0x4000 },
+  { 0x8700, 0x0d13, 0x3000 },
+  { 0x8700, 0x0d10, 0x2000 },
+  { 0x0700, 0x0d0f, 0x0000 },
+  { 0x0700, 0x0d12, 0x0000 },
+  { 0x8700, 0x0d15, 0x2000 },
+  { 0x0700, 0x0d14, 0x0000 },
+  { 0x0700, 0x0d16, 0x0000 },
+  { 0x8700, 0x0d1b, 0x3000 },
+  { 0x8700, 0x0d19, 0x2000 },
+  { 0x0700, 0x0d18, 0x0000 },
+  { 0x0700, 0x0d1a, 0x0000 },
+  { 0x8700, 0x0d1d, 0x2000 },
+  { 0x0700, 0x0d1c, 0x0000 },
+  { 0x0700, 0x0d1e, 0x0000 },
+  { 0x8700, 0x0d27, 0x4000 },
+  { 0x8700, 0x0d23, 0x3000 },
+  { 0x8700, 0x0d21, 0x2000 },
+  { 0x0700, 0x0d20, 0x0000 },
+  { 0x0700, 0x0d22, 0x0000 },
+  { 0x8700, 0x0d25, 0x2000 },
+  { 0x0700, 0x0d24, 0x0000 },
+  { 0x0700, 0x0d26, 0x0000 },
+  { 0x8700, 0x0d2c, 0x3000 },
+  { 0x8700, 0x0d2a, 0x2000 },
+  { 0x0700, 0x0d28, 0x0000 },
+  { 0x0700, 0x0d2b, 0x0000 },
+  { 0x8700, 0x0d2e, 0x2000 },
+  { 0x0700, 0x0d2d, 0x0000 },
+  { 0x0700, 0x0d2f, 0x0000 },
+  { 0x8a00, 0x0d46, 0x5000 },
+  { 0x8700, 0x0d38, 0x4000 },
+  { 0x8700, 0x0d34, 0x3000 },
+  { 0x8700, 0x0d32, 0x2000 },
+  { 0x0700, 0x0d31, 0x0000 },
+  { 0x0700, 0x0d33, 0x0000 },
+  { 0x8700, 0x0d36, 0x2000 },
+  { 0x0700, 0x0d35, 0x0000 },
+  { 0x0700, 0x0d37, 0x0000 },
+  { 0x8a00, 0x0d40, 0x3000 },
+  { 0x8a00, 0x0d3e, 0x2000 },
+  { 0x0700, 0x0d39, 0x0000 },
+  { 0x0a00, 0x0d3f, 0x0000 },
+  { 0x8c00, 0x0d42, 0x2000 },
+  { 0x0c00, 0x0d41, 0x0000 },
+  { 0x0c00, 0x0d43, 0x0000 },
+  { 0x8700, 0x0d60, 0x4000 },
+  { 0x8a00, 0x0d4b, 0x3000 },
+  { 0x8a00, 0x0d48, 0x2000 },
+  { 0x0a00, 0x0d47, 0x0000 },
+  { 0x0a00, 0x0d4a, 0x0000 },
+  { 0x8c00, 0x0d4d, 0x2000 },
+  { 0x0a00, 0x0d4c, 0x0000 },
+  { 0x0a00, 0x0d57, 0x0000 },
+  { 0x8d00, 0x0d68, 0x3000 },
+  { 0x8d00, 0x0d66, 0x2000 },
+  { 0x0700, 0x0d61, 0x0000 },
+  { 0x0d00, 0x0d67, 0x0000 },
+  { 0x8d00, 0x0d6a, 0x2000 },
+  { 0x0d00, 0x0d69, 0x0000 },
+  { 0x0d00, 0x0d6b, 0x0000 },
+  { 0x8700, 0x0da2, 0x6000 },
+  { 0x8700, 0x0d8f, 0x5000 },
+  { 0x8700, 0x0d87, 0x4000 },
+  { 0x8a00, 0x0d82, 0x3000 },
+  { 0x8d00, 0x0d6e, 0x2000 },
+  { 0x0d00, 0x0d6d, 0x0000 },
+  { 0x0d00, 0x0d6f, 0x0000 },
+  { 0x8700, 0x0d85, 0x2000 },
+  { 0x0a00, 0x0d83, 0x0000 },
+  { 0x0700, 0x0d86, 0x0000 },
+  { 0x8700, 0x0d8b, 0x3000 },
+  { 0x8700, 0x0d89, 0x2000 },
+  { 0x0700, 0x0d88, 0x0000 },
+  { 0x0700, 0x0d8a, 0x0000 },
+  { 0x8700, 0x0d8d, 0x2000 },
+  { 0x0700, 0x0d8c, 0x0000 },
+  { 0x0700, 0x0d8e, 0x0000 },
+  { 0x8700, 0x0d9a, 0x4000 },
+  { 0x8700, 0x0d93, 0x3000 },
+  { 0x8700, 0x0d91, 0x2000 },
+  { 0x0700, 0x0d90, 0x0000 },
+  { 0x0700, 0x0d92, 0x0000 },
+  { 0x8700, 0x0d95, 0x2000 },
+  { 0x0700, 0x0d94, 0x0000 },
+  { 0x0700, 0x0d96, 0x0000 },
+  { 0x8700, 0x0d9e, 0x3000 },
+  { 0x8700, 0x0d9c, 0x2000 },
+  { 0x0700, 0x0d9b, 0x0000 },
+  { 0x0700, 0x0d9d, 0x0000 },
+  { 0x8700, 0x0da0, 0x2000 },
+  { 0x0700, 0x0d9f, 0x0000 },
+  { 0x0700, 0x0da1, 0x0000 },
+  { 0x8700, 0x0db3, 0x5000 },
+  { 0x8700, 0x0daa, 0x4000 },
+  { 0x8700, 0x0da6, 0x3000 },
+  { 0x8700, 0x0da4, 0x2000 },
+  { 0x0700, 0x0da3, 0x0000 },
+  { 0x0700, 0x0da5, 0x0000 },
+  { 0x8700, 0x0da8, 0x2000 },
+  { 0x0700, 0x0da7, 0x0000 },
+  { 0x0700, 0x0da9, 0x0000 },
+  { 0x8700, 0x0dae, 0x3000 },
+  { 0x8700, 0x0dac, 0x2000 },
+  { 0x0700, 0x0dab, 0x0000 },
+  { 0x0700, 0x0dad, 0x0000 },
+  { 0x8700, 0x0db0, 0x2000 },
+  { 0x0700, 0x0daf, 0x0000 },
+  { 0x0700, 0x0db1, 0x0000 },
+  { 0x8700, 0x0dbb, 0x4000 },
+  { 0x8700, 0x0db7, 0x3000 },
+  { 0x8700, 0x0db5, 0x2000 },
+  { 0x0700, 0x0db4, 0x0000 },
+  { 0x0700, 0x0db6, 0x0000 },
+  { 0x8700, 0x0db9, 0x2000 },
+  { 0x0700, 0x0db8, 0x0000 },
+  { 0x0700, 0x0dba, 0x0000 },
+  { 0x8700, 0x0dc2, 0x3000 },
+  { 0x8700, 0x0dc0, 0x2000 },
+  { 0x0700, 0x0dbd, 0x0000 },
+  { 0x0700, 0x0dc1, 0x0000 },
+  { 0x8700, 0x0dc4, 0x2000 },
+  { 0x0700, 0x0dc3, 0x0000 },
+  { 0x0700, 0x0dc5, 0x0000 },
+  { 0x8700, 0x0f55, 0x9000 },
+  { 0x8700, 0x0ea5, 0x8000 },
+  { 0x8700, 0x0e2d, 0x7000 },
+  { 0x8700, 0x0e0d, 0x6000 },
+  { 0x8a00, 0x0ddf, 0x5000 },
+  { 0x8c00, 0x0dd6, 0x4000 },
+  { 0x8a00, 0x0dd1, 0x3000 },
+  { 0x8a00, 0x0dcf, 0x2000 },
+  { 0x0c00, 0x0dca, 0x0000 },
+  { 0x0a00, 0x0dd0, 0x0000 },
+  { 0x8c00, 0x0dd3, 0x2000 },
+  { 0x0c00, 0x0dd2, 0x0000 },
+  { 0x0c00, 0x0dd4, 0x0000 },
+  { 0x8a00, 0x0ddb, 0x3000 },
+  { 0x8a00, 0x0dd9, 0x2000 },
+  { 0x0a00, 0x0dd8, 0x0000 },
+  { 0x0a00, 0x0dda, 0x0000 },
+  { 0x8a00, 0x0ddd, 0x2000 },
+  { 0x0a00, 0x0ddc, 0x0000 },
+  { 0x0a00, 0x0dde, 0x0000 },
+  { 0x8700, 0x0e05, 0x4000 },
+  { 0x8700, 0x0e01, 0x3000 },
+  { 0x8a00, 0x0df3, 0x2000 },
+  { 0x0a00, 0x0df2, 0x0000 },
+  { 0x1500, 0x0df4, 0x0000 },
+  { 0x8700, 0x0e03, 0x2000 },
+  { 0x0700, 0x0e02, 0x0000 },
+  { 0x0700, 0x0e04, 0x0000 },
+  { 0x8700, 0x0e09, 0x3000 },
+  { 0x8700, 0x0e07, 0x2000 },
+  { 0x0700, 0x0e06, 0x0000 },
+  { 0x0700, 0x0e08, 0x0000 },
+  { 0x8700, 0x0e0b, 0x2000 },
+  { 0x0700, 0x0e0a, 0x0000 },
+  { 0x0700, 0x0e0c, 0x0000 },
+  { 0x8700, 0x0e1d, 0x5000 },
+  { 0x8700, 0x0e15, 0x4000 },
+  { 0x8700, 0x0e11, 0x3000 },
+  { 0x8700, 0x0e0f, 0x2000 },
+  { 0x0700, 0x0e0e, 0x0000 },
+  { 0x0700, 0x0e10, 0x0000 },
+  { 0x8700, 0x0e13, 0x2000 },
+  { 0x0700, 0x0e12, 0x0000 },
+  { 0x0700, 0x0e14, 0x0000 },
+  { 0x8700, 0x0e19, 0x3000 },
+  { 0x8700, 0x0e17, 0x2000 },
+  { 0x0700, 0x0e16, 0x0000 },
+  { 0x0700, 0x0e18, 0x0000 },
+  { 0x8700, 0x0e1b, 0x2000 },
+  { 0x0700, 0x0e1a, 0x0000 },
+  { 0x0700, 0x0e1c, 0x0000 },
+  { 0x8700, 0x0e25, 0x4000 },
+  { 0x8700, 0x0e21, 0x3000 },
+  { 0x8700, 0x0e1f, 0x2000 },
+  { 0x0700, 0x0e1e, 0x0000 },
+  { 0x0700, 0x0e20, 0x0000 },
+  { 0x8700, 0x0e23, 0x2000 },
+  { 0x0700, 0x0e22, 0x0000 },
+  { 0x0700, 0x0e24, 0x0000 },
+  { 0x8700, 0x0e29, 0x3000 },
+  { 0x8700, 0x0e27, 0x2000 },
+  { 0x0700, 0x0e26, 0x0000 },
+  { 0x0700, 0x0e28, 0x0000 },
+  { 0x8700, 0x0e2b, 0x2000 },
+  { 0x0700, 0x0e2a, 0x0000 },
+  { 0x0700, 0x0e2c, 0x0000 },
+  { 0x8d00, 0x0e51, 0x6000 },
+  { 0x8700, 0x0e41, 0x5000 },
+  { 0x8c00, 0x0e35, 0x4000 },
+  { 0x8c00, 0x0e31, 0x3000 },
+  { 0x8700, 0x0e2f, 0x2000 },
+  { 0x0700, 0x0e2e, 0x0000 },
+  { 0x0700, 0x0e30, 0x0000 },
+  { 0x8700, 0x0e33, 0x2000 },
+  { 0x0700, 0x0e32, 0x0000 },
+  { 0x0c00, 0x0e34, 0x0000 },
+  { 0x8c00, 0x0e39, 0x3000 },
+  { 0x8c00, 0x0e37, 0x2000 },
+  { 0x0c00, 0x0e36, 0x0000 },
+  { 0x0c00, 0x0e38, 0x0000 },
+  { 0x9700, 0x0e3f, 0x2000 },
+  { 0x0c00, 0x0e3a, 0x0000 },
+  { 0x0700, 0x0e40, 0x0000 },
+  { 0x8c00, 0x0e49, 0x4000 },
+  { 0x8700, 0x0e45, 0x3000 },
+  { 0x8700, 0x0e43, 0x2000 },
+  { 0x0700, 0x0e42, 0x0000 },
+  { 0x0700, 0x0e44, 0x0000 },
+  { 0x8c00, 0x0e47, 0x2000 },
+  { 0x0600, 0x0e46, 0x0000 },
+  { 0x0c00, 0x0e48, 0x0000 },
+  { 0x8c00, 0x0e4d, 0x3000 },
+  { 0x8c00, 0x0e4b, 0x2000 },
+  { 0x0c00, 0x0e4a, 0x0000 },
+  { 0x0c00, 0x0e4c, 0x0000 },
+  { 0x9500, 0x0e4f, 0x2000 },
+  { 0x0c00, 0x0e4e, 0x0000 },
+  { 0x0d00, 0x0e50, 0x0000 },
+  { 0x8700, 0x0e8a, 0x5000 },
+  { 0x8d00, 0x0e59, 0x4000 },
+  { 0x8d00, 0x0e55, 0x3000 },
+  { 0x8d00, 0x0e53, 0x2000 },
+  { 0x0d00, 0x0e52, 0x0000 },
+  { 0x0d00, 0x0e54, 0x0000 },
+  { 0x8d00, 0x0e57, 0x2000 },
+  { 0x0d00, 0x0e56, 0x0000 },
+  { 0x0d00, 0x0e58, 0x0000 },
+  { 0x8700, 0x0e82, 0x3000 },
+  { 0x9500, 0x0e5b, 0x2000 },
+  { 0x1500, 0x0e5a, 0x0000 },
+  { 0x0700, 0x0e81, 0x0000 },
+  { 0x8700, 0x0e87, 0x2000 },
+  { 0x0700, 0x0e84, 0x0000 },
+  { 0x0700, 0x0e88, 0x0000 },
+  { 0x8700, 0x0e9b, 0x4000 },
+  { 0x8700, 0x0e96, 0x3000 },
+  { 0x8700, 0x0e94, 0x2000 },
+  { 0x0700, 0x0e8d, 0x0000 },
+  { 0x0700, 0x0e95, 0x0000 },
+  { 0x8700, 0x0e99, 0x2000 },
+  { 0x0700, 0x0e97, 0x0000 },
+  { 0x0700, 0x0e9a, 0x0000 },
+  { 0x8700, 0x0e9f, 0x3000 },
+  { 0x8700, 0x0e9d, 0x2000 },
+  { 0x0700, 0x0e9c, 0x0000 },
+  { 0x0700, 0x0e9e, 0x0000 },
+  { 0x8700, 0x0ea2, 0x2000 },
+  { 0x0700, 0x0ea1, 0x0000 },
+  { 0x0700, 0x0ea3, 0x0000 },
+  { 0x9a00, 0x0f14, 0x7000 },
+  { 0x8d00, 0x0ed0, 0x6000 },
+  { 0x8c00, 0x0eb9, 0x5000 },
+  { 0x8c00, 0x0eb1, 0x4000 },
+  { 0x8700, 0x0ead, 0x3000 },
+  { 0x8700, 0x0eaa, 0x2000 },
+  { 0x0700, 0x0ea7, 0x0000 },
+  { 0x0700, 0x0eab, 0x0000 },
+  { 0x8700, 0x0eaf, 0x2000 },
+  { 0x0700, 0x0eae, 0x0000 },
+  { 0x0700, 0x0eb0, 0x0000 },
+  { 0x8c00, 0x0eb5, 0x3000 },
+  { 0x8700, 0x0eb3, 0x2000 },
+  { 0x0700, 0x0eb2, 0x0000 },
+  { 0x0c00, 0x0eb4, 0x0000 },
+  { 0x8c00, 0x0eb7, 0x2000 },
+  { 0x0c00, 0x0eb6, 0x0000 },
+  { 0x0c00, 0x0eb8, 0x0000 },
+  { 0x8700, 0x0ec4, 0x4000 },
+  { 0x8700, 0x0ec0, 0x3000 },
+  { 0x8c00, 0x0ebc, 0x2000 },
+  { 0x0c00, 0x0ebb, 0x0000 },
+  { 0x0700, 0x0ebd, 0x0000 },
+  { 0x8700, 0x0ec2, 0x2000 },
+  { 0x0700, 0x0ec1, 0x0000 },
+  { 0x0700, 0x0ec3, 0x0000 },
+  { 0x8c00, 0x0eca, 0x3000 },
+  { 0x8c00, 0x0ec8, 0x2000 },
+  { 0x0600, 0x0ec6, 0x0000 },
+  { 0x0c00, 0x0ec9, 0x0000 },
+  { 0x8c00, 0x0ecc, 0x2000 },
+  { 0x0c00, 0x0ecb, 0x0000 },
+  { 0x0c00, 0x0ecd, 0x0000 },
+  { 0x9500, 0x0f04, 0x5000 },
+  { 0x8d00, 0x0ed8, 0x4000 },
+  { 0x8d00, 0x0ed4, 0x3000 },
+  { 0x8d00, 0x0ed2, 0x2000 },
+  { 0x0d00, 0x0ed1, 0x0000 },
+  { 0x0d00, 0x0ed3, 0x0000 },
+  { 0x8d00, 0x0ed6, 0x2000 },
+  { 0x0d00, 0x0ed5, 0x0000 },
+  { 0x0d00, 0x0ed7, 0x0000 },
+  { 0x8700, 0x0f00, 0x3000 },
+  { 0x8700, 0x0edc, 0x2000 },
+  { 0x0d00, 0x0ed9, 0x0000 },
+  { 0x0700, 0x0edd, 0x0000 },
+  { 0x9a00, 0x0f02, 0x2000 },
+  { 0x1a00, 0x0f01, 0x0000 },
+  { 0x1a00, 0x0f03, 0x0000 },
+  { 0x9500, 0x0f0c, 0x4000 },
+  { 0x9500, 0x0f08, 0x3000 },
+  { 0x9500, 0x0f06, 0x2000 },
+  { 0x1500, 0x0f05, 0x0000 },
+  { 0x1500, 0x0f07, 0x0000 },
+  { 0x9500, 0x0f0a, 0x2000 },
+  { 0x1500, 0x0f09, 0x0000 },
+  { 0x1500, 0x0f0b, 0x0000 },
+  { 0x9500, 0x0f10, 0x3000 },
+  { 0x9500, 0x0f0e, 0x2000 },
+  { 0x1500, 0x0f0d, 0x0000 },
+  { 0x1500, 0x0f0f, 0x0000 },
+  { 0x9500, 0x0f12, 0x2000 },
+  { 0x1500, 0x0f11, 0x0000 },
+  { 0x1a00, 0x0f13, 0x0000 },
+  { 0x9a00, 0x0f34, 0x6000 },
+  { 0x8d00, 0x0f24, 0x5000 },
+  { 0x9a00, 0x0f1c, 0x4000 },
+  { 0x8c00, 0x0f18, 0x3000 },
+  { 0x9a00, 0x0f16, 0x2000 },
+  { 0x1a00, 0x0f15, 0x0000 },
+  { 0x1a00, 0x0f17, 0x0000 },
+  { 0x9a00, 0x0f1a, 0x2000 },
+  { 0x0c00, 0x0f19, 0x0000 },
+  { 0x1a00, 0x0f1b, 0x0000 },
+  { 0x8d00, 0x0f20, 0x3000 },
+  { 0x9a00, 0x0f1e, 0x2000 },
+  { 0x1a00, 0x0f1d, 0x0000 },
+  { 0x1a00, 0x0f1f, 0x0000 },
+  { 0x8d00, 0x0f22, 0x2000 },
+  { 0x0d00, 0x0f21, 0x0000 },
+  { 0x0d00, 0x0f23, 0x0000 },
+  { 0x8f00, 0x0f2c, 0x4000 },
+  { 0x8d00, 0x0f28, 0x3000 },
+  { 0x8d00, 0x0f26, 0x2000 },
+  { 0x0d00, 0x0f25, 0x0000 },
+  { 0x0d00, 0x0f27, 0x0000 },
+  { 0x8f00, 0x0f2a, 0x2000 },
+  { 0x0d00, 0x0f29, 0x0000 },
+  { 0x0f00, 0x0f2b, 0x0000 },
+  { 0x8f00, 0x0f30, 0x3000 },
+  { 0x8f00, 0x0f2e, 0x2000 },
+  { 0x0f00, 0x0f2d, 0x0000 },
+  { 0x0f00, 0x0f2f, 0x0000 },
+  { 0x8f00, 0x0f32, 0x2000 },
+  { 0x0f00, 0x0f31, 0x0000 },
+  { 0x0f00, 0x0f33, 0x0000 },
+  { 0x8700, 0x0f44, 0x5000 },
+  { 0x9600, 0x0f3c, 0x4000 },
+  { 0x9a00, 0x0f38, 0x3000 },
+  { 0x9a00, 0x0f36, 0x2000 },
+  { 0x0c00, 0x0f35, 0x0000 },
+  { 0x0c00, 0x0f37, 0x0000 },
+  { 0x9600, 0x0f3a, 0x2000 },
+  { 0x0c00, 0x0f39, 0x0000 },
+  { 0x1200, 0x0f3b, 0x0000 },
+  { 0x8700, 0x0f40, 0x3000 },
+  { 0x8a00, 0x0f3e, 0x2000 },
+  { 0x1200, 0x0f3d, 0x0000 },
+  { 0x0a00, 0x0f3f, 0x0000 },
+  { 0x8700, 0x0f42, 0x2000 },
+  { 0x0700, 0x0f41, 0x0000 },
+  { 0x0700, 0x0f43, 0x0000 },
+  { 0x8700, 0x0f4d, 0x4000 },
+  { 0x8700, 0x0f49, 0x3000 },
+  { 0x8700, 0x0f46, 0x2000 },
+  { 0x0700, 0x0f45, 0x0000 },
+  { 0x0700, 0x0f47, 0x0000 },
+  { 0x8700, 0x0f4b, 0x2000 },
+  { 0x0700, 0x0f4a, 0x0000 },
+  { 0x0700, 0x0f4c, 0x0000 },
+  { 0x8700, 0x0f51, 0x3000 },
+  { 0x8700, 0x0f4f, 0x2000 },
+  { 0x0700, 0x0f4e, 0x0000 },
+  { 0x0700, 0x0f50, 0x0000 },
+  { 0x8700, 0x0f53, 0x2000 },
+  { 0x0700, 0x0f52, 0x0000 },
+  { 0x0700, 0x0f54, 0x0000 },
+  { 0x8700, 0x1013, 0x8000 },
+  { 0x8c00, 0x0fa0, 0x7000 },
+  { 0x8c00, 0x0f7b, 0x6000 },
+  { 0x8700, 0x0f65, 0x5000 },
+  { 0x8700, 0x0f5d, 0x4000 },
+  { 0x8700, 0x0f59, 0x3000 },
+  { 0x8700, 0x0f57, 0x2000 },
+  { 0x0700, 0x0f56, 0x0000 },
+  { 0x0700, 0x0f58, 0x0000 },
+  { 0x8700, 0x0f5b, 0x2000 },
+  { 0x0700, 0x0f5a, 0x0000 },
+  { 0x0700, 0x0f5c, 0x0000 },
+  { 0x8700, 0x0f61, 0x3000 },
+  { 0x8700, 0x0f5f, 0x2000 },
+  { 0x0700, 0x0f5e, 0x0000 },
+  { 0x0700, 0x0f60, 0x0000 },
+  { 0x8700, 0x0f63, 0x2000 },
+  { 0x0700, 0x0f62, 0x0000 },
+  { 0x0700, 0x0f64, 0x0000 },
+  { 0x8c00, 0x0f73, 0x4000 },
+  { 0x8700, 0x0f69, 0x3000 },
+  { 0x8700, 0x0f67, 0x2000 },
+  { 0x0700, 0x0f66, 0x0000 },
+  { 0x0700, 0x0f68, 0x0000 },
+  { 0x8c00, 0x0f71, 0x2000 },
+  { 0x0700, 0x0f6a, 0x0000 },
+  { 0x0c00, 0x0f72, 0x0000 },
+  { 0x8c00, 0x0f77, 0x3000 },
+  { 0x8c00, 0x0f75, 0x2000 },
+  { 0x0c00, 0x0f74, 0x0000 },
+  { 0x0c00, 0x0f76, 0x0000 },
+  { 0x8c00, 0x0f79, 0x2000 },
+  { 0x0c00, 0x0f78, 0x0000 },
+  { 0x0c00, 0x0f7a, 0x0000 },
+  { 0x8700, 0x0f8b, 0x5000 },
+  { 0x8c00, 0x0f83, 0x4000 },
+  { 0x8a00, 0x0f7f, 0x3000 },
+  { 0x8c00, 0x0f7d, 0x2000 },
+  { 0x0c00, 0x0f7c, 0x0000 },
+  { 0x0c00, 0x0f7e, 0x0000 },
+  { 0x8c00, 0x0f81, 0x2000 },
+  { 0x0c00, 0x0f80, 0x0000 },
+  { 0x0c00, 0x0f82, 0x0000 },
+  { 0x8c00, 0x0f87, 0x3000 },
+  { 0x9500, 0x0f85, 0x2000 },
+  { 0x0c00, 0x0f84, 0x0000 },
+  { 0x0c00, 0x0f86, 0x0000 },
+  { 0x8700, 0x0f89, 0x2000 },
+  { 0x0700, 0x0f88, 0x0000 },
+  { 0x0700, 0x0f8a, 0x0000 },
+  { 0x8c00, 0x0f97, 0x4000 },
+  { 0x8c00, 0x0f93, 0x3000 },
+  { 0x8c00, 0x0f91, 0x2000 },
+  { 0x0c00, 0x0f90, 0x0000 },
+  { 0x0c00, 0x0f92, 0x0000 },
+  { 0x8c00, 0x0f95, 0x2000 },
+  { 0x0c00, 0x0f94, 0x0000 },
+  { 0x0c00, 0x0f96, 0x0000 },
+  { 0x8c00, 0x0f9c, 0x3000 },
+  { 0x8c00, 0x0f9a, 0x2000 },
+  { 0x0c00, 0x0f99, 0x0000 },
+  { 0x0c00, 0x0f9b, 0x0000 },
+  { 0x8c00, 0x0f9e, 0x2000 },
+  { 0x0c00, 0x0f9d, 0x0000 },
+  { 0x0c00, 0x0f9f, 0x0000 },
+  { 0x9a00, 0x0fc1, 0x6000 },
+  { 0x8c00, 0x0fb0, 0x5000 },
+  { 0x8c00, 0x0fa8, 0x4000 },
+  { 0x8c00, 0x0fa4, 0x3000 },
+  { 0x8c00, 0x0fa2, 0x2000 },
+  { 0x0c00, 0x0fa1, 0x0000 },
+  { 0x0c00, 0x0fa3, 0x0000 },
+  { 0x8c00, 0x0fa6, 0x2000 },
+  { 0x0c00, 0x0fa5, 0x0000 },
+  { 0x0c00, 0x0fa7, 0x0000 },
+  { 0x8c00, 0x0fac, 0x3000 },
+  { 0x8c00, 0x0faa, 0x2000 },
+  { 0x0c00, 0x0fa9, 0x0000 },
+  { 0x0c00, 0x0fab, 0x0000 },
+  { 0x8c00, 0x0fae, 0x2000 },
+  { 0x0c00, 0x0fad, 0x0000 },
+  { 0x0c00, 0x0faf, 0x0000 },
+  { 0x8c00, 0x0fb8, 0x4000 },
+  { 0x8c00, 0x0fb4, 0x3000 },
+  { 0x8c00, 0x0fb2, 0x2000 },
+  { 0x0c00, 0x0fb1, 0x0000 },
+  { 0x0c00, 0x0fb3, 0x0000 },
+  { 0x8c00, 0x0fb6, 0x2000 },
+  { 0x0c00, 0x0fb5, 0x0000 },
+  { 0x0c00, 0x0fb7, 0x0000 },
+  { 0x8c00, 0x0fbc, 0x3000 },
+  { 0x8c00, 0x0fba, 0x2000 },
+  { 0x0c00, 0x0fb9, 0x0000 },
+  { 0x0c00, 0x0fbb, 0x0000 },
+  { 0x9a00, 0x0fbf, 0x2000 },
+  { 0x1a00, 0x0fbe, 0x0000 },
+  { 0x1a00, 0x0fc0, 0x0000 },
+  { 0x8700, 0x1003, 0x5000 },
+  { 0x9a00, 0x0fc9, 0x4000 },
+  { 0x9a00, 0x0fc5, 0x3000 },
+  { 0x9a00, 0x0fc3, 0x2000 },
+  { 0x1a00, 0x0fc2, 0x0000 },
+  { 0x1a00, 0x0fc4, 0x0000 },
+  { 0x9a00, 0x0fc7, 0x2000 },
+  { 0x0c00, 0x0fc6, 0x0000 },
+  { 0x1a00, 0x0fc8, 0x0000 },
+  { 0x9a00, 0x0fcf, 0x3000 },
+  { 0x9a00, 0x0fcb, 0x2000 },
+  { 0x1a00, 0x0fca, 0x0000 },
+  { 0x1a00, 0x0fcc, 0x0000 },
+  { 0x8700, 0x1001, 0x2000 },
+  { 0x0700, 0x1000, 0x0000 },
+  { 0x0700, 0x1002, 0x0000 },
+  { 0x8700, 0x100b, 0x4000 },
+  { 0x8700, 0x1007, 0x3000 },
+  { 0x8700, 0x1005, 0x2000 },
+  { 0x0700, 0x1004, 0x0000 },
+  { 0x0700, 0x1006, 0x0000 },
+  { 0x8700, 0x1009, 0x2000 },
+  { 0x0700, 0x1008, 0x0000 },
+  { 0x0700, 0x100a, 0x0000 },
+  { 0x8700, 0x100f, 0x3000 },
+  { 0x8700, 0x100d, 0x2000 },
+  { 0x0700, 0x100c, 0x0000 },
+  { 0x0700, 0x100e, 0x0000 },
+  { 0x8700, 0x1011, 0x2000 },
+  { 0x0700, 0x1010, 0x0000 },
+  { 0x0700, 0x1012, 0x0000 },
+  { 0x8900, 0x10a5, 0x7000 },
+  { 0x8c00, 0x1039, 0x6000 },
+  { 0x8700, 0x1024, 0x5000 },
+  { 0x8700, 0x101b, 0x4000 },
+  { 0x8700, 0x1017, 0x3000 },
+  { 0x8700, 0x1015, 0x2000 },
+  { 0x0700, 0x1014, 0x0000 },
+  { 0x0700, 0x1016, 0x0000 },
+  { 0x8700, 0x1019, 0x2000 },
+  { 0x0700, 0x1018, 0x0000 },
+  { 0x0700, 0x101a, 0x0000 },
+  { 0x8700, 0x101f, 0x3000 },
+  { 0x8700, 0x101d, 0x2000 },
+  { 0x0700, 0x101c, 0x0000 },
+  { 0x0700, 0x101e, 0x0000 },
+  { 0x8700, 0x1021, 0x2000 },
+  { 0x0700, 0x1020, 0x0000 },
+  { 0x0700, 0x1023, 0x0000 },
+  { 0x8c00, 0x102e, 0x4000 },
+  { 0x8700, 0x1029, 0x3000 },
+  { 0x8700, 0x1026, 0x2000 },
+  { 0x0700, 0x1025, 0x0000 },
+  { 0x0700, 0x1027, 0x0000 },
+  { 0x8a00, 0x102c, 0x2000 },
+  { 0x0700, 0x102a, 0x0000 },
+  { 0x0c00, 0x102d, 0x0000 },
+  { 0x8c00, 0x1032, 0x3000 },
+  { 0x8c00, 0x1030, 0x2000 },
+  { 0x0c00, 0x102f, 0x0000 },
+  { 0x0a00, 0x1031, 0x0000 },
+  { 0x8c00, 0x1037, 0x2000 },
+  { 0x0c00, 0x1036, 0x0000 },
+  { 0x0a00, 0x1038, 0x0000 },
+  { 0x9500, 0x104f, 0x5000 },
+  { 0x8d00, 0x1047, 0x4000 },
+  { 0x8d00, 0x1043, 0x3000 },
+  { 0x8d00, 0x1041, 0x2000 },
+  { 0x0d00, 0x1040, 0x0000 },
+  { 0x0d00, 0x1042, 0x0000 },
+  { 0x8d00, 0x1045, 0x2000 },
+  { 0x0d00, 0x1044, 0x0000 },
+  { 0x0d00, 0x1046, 0x0000 },
+  { 0x9500, 0x104b, 0x3000 },
+  { 0x8d00, 0x1049, 0x2000 },
+  { 0x0d00, 0x1048, 0x0000 },
+  { 0x1500, 0x104a, 0x0000 },
+  { 0x9500, 0x104d, 0x2000 },
+  { 0x1500, 0x104c, 0x0000 },
+  { 0x1500, 0x104e, 0x0000 },
+  { 0x8a00, 0x1057, 0x4000 },
+  { 0x8700, 0x1053, 0x3000 },
+  { 0x8700, 0x1051, 0x2000 },
+  { 0x0700, 0x1050, 0x0000 },
+  { 0x0700, 0x1052, 0x0000 },
+  { 0x8700, 0x1055, 0x2000 },
+  { 0x0700, 0x1054, 0x0000 },
+  { 0x0a00, 0x1056, 0x0000 },
+  { 0x8900, 0x10a1, 0x3000 },
+  { 0x8c00, 0x1059, 0x2000 },
+  { 0x0c00, 0x1058, 0x0000 },
+  { 0x0900, 0x10a0, 0x0000 },
+  { 0x8900, 0x10a3, 0x2000 },
+  { 0x0900, 0x10a2, 0x0000 },
+  { 0x0900, 0x10a4, 0x0000 },
+  { 0x8900, 0x10c5, 0x6000 },
+  { 0x8900, 0x10b5, 0x5000 },
+  { 0x8900, 0x10ad, 0x4000 },
+  { 0x8900, 0x10a9, 0x3000 },
+  { 0x8900, 0x10a7, 0x2000 },
+  { 0x0900, 0x10a6, 0x0000 },
+  { 0x0900, 0x10a8, 0x0000 },
+  { 0x8900, 0x10ab, 0x2000 },
+  { 0x0900, 0x10aa, 0x0000 },
+  { 0x0900, 0x10ac, 0x0000 },
+  { 0x8900, 0x10b1, 0x3000 },
+  { 0x8900, 0x10af, 0x2000 },
+  { 0x0900, 0x10ae, 0x0000 },
+  { 0x0900, 0x10b0, 0x0000 },
+  { 0x8900, 0x10b3, 0x2000 },
+  { 0x0900, 0x10b2, 0x0000 },
+  { 0x0900, 0x10b4, 0x0000 },
+  { 0x8900, 0x10bd, 0x4000 },
+  { 0x8900, 0x10b9, 0x3000 },
+  { 0x8900, 0x10b7, 0x2000 },
+  { 0x0900, 0x10b6, 0x0000 },
+  { 0x0900, 0x10b8, 0x0000 },
+  { 0x8900, 0x10bb, 0x2000 },
+  { 0x0900, 0x10ba, 0x0000 },
+  { 0x0900, 0x10bc, 0x0000 },
+  { 0x8900, 0x10c1, 0x3000 },
+  { 0x8900, 0x10bf, 0x2000 },
+  { 0x0900, 0x10be, 0x0000 },
+  { 0x0900, 0x10c0, 0x0000 },
+  { 0x8900, 0x10c3, 0x2000 },
+  { 0x0900, 0x10c2, 0x0000 },
+  { 0x0900, 0x10c4, 0x0000 },
+  { 0x8700, 0x10df, 0x5000 },
+  { 0x8700, 0x10d7, 0x4000 },
+  { 0x8700, 0x10d3, 0x3000 },
+  { 0x8700, 0x10d1, 0x2000 },
+  { 0x0700, 0x10d0, 0x0000 },
+  { 0x0700, 0x10d2, 0x0000 },
+  { 0x8700, 0x10d5, 0x2000 },
+  { 0x0700, 0x10d4, 0x0000 },
+  { 0x0700, 0x10d6, 0x0000 },
+  { 0x8700, 0x10db, 0x3000 },
+  { 0x8700, 0x10d9, 0x2000 },
+  { 0x0700, 0x10d8, 0x0000 },
+  { 0x0700, 0x10da, 0x0000 },
+  { 0x8700, 0x10dd, 0x2000 },
+  { 0x0700, 0x10dc, 0x0000 },
+  { 0x0700, 0x10de, 0x0000 },
+  { 0x8700, 0x10e7, 0x4000 },
+  { 0x8700, 0x10e3, 0x3000 },
+  { 0x8700, 0x10e1, 0x2000 },
+  { 0x0700, 0x10e0, 0x0000 },
+  { 0x0700, 0x10e2, 0x0000 },
+  { 0x8700, 0x10e5, 0x2000 },
+  { 0x0700, 0x10e4, 0x0000 },
+  { 0x0700, 0x10e6, 0x0000 },
+  { 0x8700, 0x10eb, 0x3000 },
+  { 0x8700, 0x10e9, 0x2000 },
+  { 0x0700, 0x10e8, 0x0000 },
+  { 0x0700, 0x10ea, 0x0000 },
+  { 0x8700, 0x10ed, 0x2000 },
+  { 0x0700, 0x10ec, 0x0000 },
+  { 0x0700, 0x10ee, 0x0000 },
+  { 0x8700, 0x1322, 0xa000 },
+  { 0x8700, 0x1205, 0x9000 },
+  { 0x8700, 0x117a, 0x8000 },
+  { 0x8700, 0x1135, 0x7000 },
+  { 0x8700, 0x1115, 0x6000 },
+  { 0x8700, 0x1105, 0x5000 },
+  { 0x8700, 0x10f7, 0x4000 },
+  { 0x8700, 0x10f3, 0x3000 },
+  { 0x8700, 0x10f1, 0x2000 },
+  { 0x0700, 0x10f0, 0x0000 },
+  { 0x0700, 0x10f2, 0x0000 },
+  { 0x8700, 0x10f5, 0x2000 },
+  { 0x0700, 0x10f4, 0x0000 },
+  { 0x0700, 0x10f6, 0x0000 },
+  { 0x8700, 0x1101, 0x3000 },
+  { 0x9500, 0x10fb, 0x2000 },
+  { 0x0700, 0x10f8, 0x0000 },
+  { 0x0700, 0x1100, 0x0000 },
+  { 0x8700, 0x1103, 0x2000 },
+  { 0x0700, 0x1102, 0x0000 },
+  { 0x0700, 0x1104, 0x0000 },
+  { 0x8700, 0x110d, 0x4000 },
+  { 0x8700, 0x1109, 0x3000 },
+  { 0x8700, 0x1107, 0x2000 },
+  { 0x0700, 0x1106, 0x0000 },
+  { 0x0700, 0x1108, 0x0000 },
+  { 0x8700, 0x110b, 0x2000 },
+  { 0x0700, 0x110a, 0x0000 },
+  { 0x0700, 0x110c, 0x0000 },
+  { 0x8700, 0x1111, 0x3000 },
+  { 0x8700, 0x110f, 0x2000 },
+  { 0x0700, 0x110e, 0x0000 },
+  { 0x0700, 0x1110, 0x0000 },
+  { 0x8700, 0x1113, 0x2000 },
+  { 0x0700, 0x1112, 0x0000 },
+  { 0x0700, 0x1114, 0x0000 },
+  { 0x8700, 0x1125, 0x5000 },
+  { 0x8700, 0x111d, 0x4000 },
+  { 0x8700, 0x1119, 0x3000 },
+  { 0x8700, 0x1117, 0x2000 },
+  { 0x0700, 0x1116, 0x0000 },
+  { 0x0700, 0x1118, 0x0000 },
+  { 0x8700, 0x111b, 0x2000 },
+  { 0x0700, 0x111a, 0x0000 },
+  { 0x0700, 0x111c, 0x0000 },
+  { 0x8700, 0x1121, 0x3000 },
+  { 0x8700, 0x111f, 0x2000 },
+  { 0x0700, 0x111e, 0x0000 },
+  { 0x0700, 0x1120, 0x0000 },
+  { 0x8700, 0x1123, 0x2000 },
+  { 0x0700, 0x1122, 0x0000 },
+  { 0x0700, 0x1124, 0x0000 },
+  { 0x8700, 0x112d, 0x4000 },
+  { 0x8700, 0x1129, 0x3000 },
+  { 0x8700, 0x1127, 0x2000 },
+  { 0x0700, 0x1126, 0x0000 },
+  { 0x0700, 0x1128, 0x0000 },
+  { 0x8700, 0x112b, 0x2000 },
+  { 0x0700, 0x112a, 0x0000 },
+  { 0x0700, 0x112c, 0x0000 },
+  { 0x8700, 0x1131, 0x3000 },
+  { 0x8700, 0x112f, 0x2000 },
+  { 0x0700, 0x112e, 0x0000 },
+  { 0x0700, 0x1130, 0x0000 },
+  { 0x8700, 0x1133, 0x2000 },
+  { 0x0700, 0x1132, 0x0000 },
+  { 0x0700, 0x1134, 0x0000 },
+  { 0x8700, 0x1155, 0x6000 },
+  { 0x8700, 0x1145, 0x5000 },
+  { 0x8700, 0x113d, 0x4000 },
+  { 0x8700, 0x1139, 0x3000 },
+  { 0x8700, 0x1137, 0x2000 },
+  { 0x0700, 0x1136, 0x0000 },
+  { 0x0700, 0x1138, 0x0000 },
+  { 0x8700, 0x113b, 0x2000 },
+  { 0x0700, 0x113a, 0x0000 },
+  { 0x0700, 0x113c, 0x0000 },
+  { 0x8700, 0x1141, 0x3000 },
+  { 0x8700, 0x113f, 0x2000 },
+  { 0x0700, 0x113e, 0x0000 },
+  { 0x0700, 0x1140, 0x0000 },
+  { 0x8700, 0x1143, 0x2000 },
+  { 0x0700, 0x1142, 0x0000 },
+  { 0x0700, 0x1144, 0x0000 },
+  { 0x8700, 0x114d, 0x4000 },
+  { 0x8700, 0x1149, 0x3000 },
+  { 0x8700, 0x1147, 0x2000 },
+  { 0x0700, 0x1146, 0x0000 },
+  { 0x0700, 0x1148, 0x0000 },
+  { 0x8700, 0x114b, 0x2000 },
+  { 0x0700, 0x114a, 0x0000 },
+  { 0x0700, 0x114c, 0x0000 },
+  { 0x8700, 0x1151, 0x3000 },
+  { 0x8700, 0x114f, 0x2000 },
+  { 0x0700, 0x114e, 0x0000 },
+  { 0x0700, 0x1150, 0x0000 },
+  { 0x8700, 0x1153, 0x2000 },
+  { 0x0700, 0x1152, 0x0000 },
+  { 0x0700, 0x1154, 0x0000 },
+  { 0x8700, 0x116a, 0x5000 },
+  { 0x8700, 0x1162, 0x4000 },
+  { 0x8700, 0x1159, 0x3000 },
+  { 0x8700, 0x1157, 0x2000 },
+  { 0x0700, 0x1156, 0x0000 },
+  { 0x0700, 0x1158, 0x0000 },
+  { 0x8700, 0x1160, 0x2000 },
+  { 0x0700, 0x115f, 0x0000 },
+  { 0x0700, 0x1161, 0x0000 },
+  { 0x8700, 0x1166, 0x3000 },
+  { 0x8700, 0x1164, 0x2000 },
+  { 0x0700, 0x1163, 0x0000 },
+  { 0x0700, 0x1165, 0x0000 },
+  { 0x8700, 0x1168, 0x2000 },
+  { 0x0700, 0x1167, 0x0000 },
+  { 0x0700, 0x1169, 0x0000 },
+  { 0x8700, 0x1172, 0x4000 },
+  { 0x8700, 0x116e, 0x3000 },
+  { 0x8700, 0x116c, 0x2000 },
+  { 0x0700, 0x116b, 0x0000 },
+  { 0x0700, 0x116d, 0x0000 },
+  { 0x8700, 0x1170, 0x2000 },
+  { 0x0700, 0x116f, 0x0000 },
+  { 0x0700, 0x1171, 0x0000 },
+  { 0x8700, 0x1176, 0x3000 },
+  { 0x8700, 0x1174, 0x2000 },
+  { 0x0700, 0x1173, 0x0000 },
+  { 0x0700, 0x1175, 0x0000 },
+  { 0x8700, 0x1178, 0x2000 },
+  { 0x0700, 0x1177, 0x0000 },
+  { 0x0700, 0x1179, 0x0000 },
+  { 0x8700, 0x11bf, 0x7000 },
+  { 0x8700, 0x119a, 0x6000 },
+  { 0x8700, 0x118a, 0x5000 },
+  { 0x8700, 0x1182, 0x4000 },
+  { 0x8700, 0x117e, 0x3000 },
+  { 0x8700, 0x117c, 0x2000 },
+  { 0x0700, 0x117b, 0x0000 },
+  { 0x0700, 0x117d, 0x0000 },
+  { 0x8700, 0x1180, 0x2000 },
+  { 0x0700, 0x117f, 0x0000 },
+  { 0x0700, 0x1181, 0x0000 },
+  { 0x8700, 0x1186, 0x3000 },
+  { 0x8700, 0x1184, 0x2000 },
+  { 0x0700, 0x1183, 0x0000 },
+  { 0x0700, 0x1185, 0x0000 },
+  { 0x8700, 0x1188, 0x2000 },
+  { 0x0700, 0x1187, 0x0000 },
+  { 0x0700, 0x1189, 0x0000 },
+  { 0x8700, 0x1192, 0x4000 },
+  { 0x8700, 0x118e, 0x3000 },
+  { 0x8700, 0x118c, 0x2000 },
+  { 0x0700, 0x118b, 0x0000 },
+  { 0x0700, 0x118d, 0x0000 },
+  { 0x8700, 0x1190, 0x2000 },
+  { 0x0700, 0x118f, 0x0000 },
+  { 0x0700, 0x1191, 0x0000 },
+  { 0x8700, 0x1196, 0x3000 },
+  { 0x8700, 0x1194, 0x2000 },
+  { 0x0700, 0x1193, 0x0000 },
+  { 0x0700, 0x1195, 0x0000 },
+  { 0x8700, 0x1198, 0x2000 },
+  { 0x0700, 0x1197, 0x0000 },
+  { 0x0700, 0x1199, 0x0000 },
+  { 0x8700, 0x11af, 0x5000 },
+  { 0x8700, 0x11a2, 0x4000 },
+  { 0x8700, 0x119e, 0x3000 },
+  { 0x8700, 0x119c, 0x2000 },
+  { 0x0700, 0x119b, 0x0000 },
+  { 0x0700, 0x119d, 0x0000 },
+  { 0x8700, 0x11a0, 0x2000 },
+  { 0x0700, 0x119f, 0x0000 },
+  { 0x0700, 0x11a1, 0x0000 },
+  { 0x8700, 0x11ab, 0x3000 },
+  { 0x8700, 0x11a9, 0x2000 },
+  { 0x0700, 0x11a8, 0x0000 },
+  { 0x0700, 0x11aa, 0x0000 },
+  { 0x8700, 0x11ad, 0x2000 },
+  { 0x0700, 0x11ac, 0x0000 },
+  { 0x0700, 0x11ae, 0x0000 },
+  { 0x8700, 0x11b7, 0x4000 },
+  { 0x8700, 0x11b3, 0x3000 },
+  { 0x8700, 0x11b1, 0x2000 },
+  { 0x0700, 0x11b0, 0x0000 },
+  { 0x0700, 0x11b2, 0x0000 },
+  { 0x8700, 0x11b5, 0x2000 },
+  { 0x0700, 0x11b4, 0x0000 },
+  { 0x0700, 0x11b6, 0x0000 },
+  { 0x8700, 0x11bb, 0x3000 },
+  { 0x8700, 0x11b9, 0x2000 },
+  { 0x0700, 0x11b8, 0x0000 },
+  { 0x0700, 0x11ba, 0x0000 },
+  { 0x8700, 0x11bd, 0x2000 },
+  { 0x0700, 0x11bc, 0x0000 },
+  { 0x0700, 0x11be, 0x0000 },
+  { 0x8700, 0x11df, 0x6000 },
+  { 0x8700, 0x11cf, 0x5000 },
+  { 0x8700, 0x11c7, 0x4000 },
+  { 0x8700, 0x11c3, 0x3000 },
+  { 0x8700, 0x11c1, 0x2000 },
+  { 0x0700, 0x11c0, 0x0000 },
+  { 0x0700, 0x11c2, 0x0000 },
+  { 0x8700, 0x11c5, 0x2000 },
+  { 0x0700, 0x11c4, 0x0000 },
+  { 0x0700, 0x11c6, 0x0000 },
+  { 0x8700, 0x11cb, 0x3000 },
+  { 0x8700, 0x11c9, 0x2000 },
+  { 0x0700, 0x11c8, 0x0000 },
+  { 0x0700, 0x11ca, 0x0000 },
+  { 0x8700, 0x11cd, 0x2000 },
+  { 0x0700, 0x11cc, 0x0000 },
+  { 0x0700, 0x11ce, 0x0000 },
+  { 0x8700, 0x11d7, 0x4000 },
+  { 0x8700, 0x11d3, 0x3000 },
+  { 0x8700, 0x11d1, 0x2000 },
+  { 0x0700, 0x11d0, 0x0000 },
+  { 0x0700, 0x11d2, 0x0000 },
+  { 0x8700, 0x11d5, 0x2000 },
+  { 0x0700, 0x11d4, 0x0000 },
+  { 0x0700, 0x11d6, 0x0000 },
+  { 0x8700, 0x11db, 0x3000 },
+  { 0x8700, 0x11d9, 0x2000 },
+  { 0x0700, 0x11d8, 0x0000 },
+  { 0x0700, 0x11da, 0x0000 },
+  { 0x8700, 0x11dd, 0x2000 },
+  { 0x0700, 0x11dc, 0x0000 },
+  { 0x0700, 0x11de, 0x0000 },
+  { 0x8700, 0x11ef, 0x5000 },
+  { 0x8700, 0x11e7, 0x4000 },
+  { 0x8700, 0x11e3, 0x3000 },
+  { 0x8700, 0x11e1, 0x2000 },
+  { 0x0700, 0x11e0, 0x0000 },
+  { 0x0700, 0x11e2, 0x0000 },
+  { 0x8700, 0x11e5, 0x2000 },
+  { 0x0700, 0x11e4, 0x0000 },
+  { 0x0700, 0x11e6, 0x0000 },
+  { 0x8700, 0x11eb, 0x3000 },
+  { 0x8700, 0x11e9, 0x2000 },
+  { 0x0700, 0x11e8, 0x0000 },
+  { 0x0700, 0x11ea, 0x0000 },
+  { 0x8700, 0x11ed, 0x2000 },
+  { 0x0700, 0x11ec, 0x0000 },
+  { 0x0700, 0x11ee, 0x0000 },
+  { 0x8700, 0x11f7, 0x4000 },
+  { 0x8700, 0x11f3, 0x3000 },
+  { 0x8700, 0x11f1, 0x2000 },
+  { 0x0700, 0x11f0, 0x0000 },
+  { 0x0700, 0x11f2, 0x0000 },
+  { 0x8700, 0x11f5, 0x2000 },
+  { 0x0700, 0x11f4, 0x0000 },
+  { 0x0700, 0x11f6, 0x0000 },
+  { 0x8700, 0x1201, 0x3000 },
+  { 0x8700, 0x11f9, 0x2000 },
+  { 0x0700, 0x11f8, 0x0000 },
+  { 0x0700, 0x1200, 0x0000 },
+  { 0x8700, 0x1203, 0x2000 },
+  { 0x0700, 0x1202, 0x0000 },
+  { 0x0700, 0x1204, 0x0000 },
+  { 0x8700, 0x1292, 0x8000 },
+  { 0x8700, 0x1246, 0x7000 },
+  { 0x8700, 0x1226, 0x6000 },
+  { 0x8700, 0x1216, 0x5000 },
+  { 0x8700, 0x120e, 0x4000 },
+  { 0x8700, 0x120a, 0x3000 },
+  { 0x8700, 0x1208, 0x2000 },
+  { 0x0700, 0x1206, 0x0000 },
+  { 0x0700, 0x1209, 0x0000 },
+  { 0x8700, 0x120c, 0x2000 },
+  { 0x0700, 0x120b, 0x0000 },
+  { 0x0700, 0x120d, 0x0000 },
+  { 0x8700, 0x1212, 0x3000 },
+  { 0x8700, 0x1210, 0x2000 },
+  { 0x0700, 0x120f, 0x0000 },
+  { 0x0700, 0x1211, 0x0000 },
+  { 0x8700, 0x1214, 0x2000 },
+  { 0x0700, 0x1213, 0x0000 },
+  { 0x0700, 0x1215, 0x0000 },
+  { 0x8700, 0x121e, 0x4000 },
+  { 0x8700, 0x121a, 0x3000 },
+  { 0x8700, 0x1218, 0x2000 },
+  { 0x0700, 0x1217, 0x0000 },
+  { 0x0700, 0x1219, 0x0000 },
+  { 0x8700, 0x121c, 0x2000 },
+  { 0x0700, 0x121b, 0x0000 },
+  { 0x0700, 0x121d, 0x0000 },
+  { 0x8700, 0x1222, 0x3000 },
+  { 0x8700, 0x1220, 0x2000 },
+  { 0x0700, 0x121f, 0x0000 },
+  { 0x0700, 0x1221, 0x0000 },
+  { 0x8700, 0x1224, 0x2000 },
+  { 0x0700, 0x1223, 0x0000 },
+  { 0x0700, 0x1225, 0x0000 },
+  { 0x8700, 0x1236, 0x5000 },
+  { 0x8700, 0x122e, 0x4000 },
+  { 0x8700, 0x122a, 0x3000 },
+  { 0x8700, 0x1228, 0x2000 },
+  { 0x0700, 0x1227, 0x0000 },
+  { 0x0700, 0x1229, 0x0000 },
+  { 0x8700, 0x122c, 0x2000 },
+  { 0x0700, 0x122b, 0x0000 },
+  { 0x0700, 0x122d, 0x0000 },
+  { 0x8700, 0x1232, 0x3000 },
+  { 0x8700, 0x1230, 0x2000 },
+  { 0x0700, 0x122f, 0x0000 },
+  { 0x0700, 0x1231, 0x0000 },
+  { 0x8700, 0x1234, 0x2000 },
+  { 0x0700, 0x1233, 0x0000 },
+  { 0x0700, 0x1235, 0x0000 },
+  { 0x8700, 0x123e, 0x4000 },
+  { 0x8700, 0x123a, 0x3000 },
+  { 0x8700, 0x1238, 0x2000 },
+  { 0x0700, 0x1237, 0x0000 },
+  { 0x0700, 0x1239, 0x0000 },
+  { 0x8700, 0x123c, 0x2000 },
+  { 0x0700, 0x123b, 0x0000 },
+  { 0x0700, 0x123d, 0x0000 },
+  { 0x8700, 0x1242, 0x3000 },
+  { 0x8700, 0x1240, 0x2000 },
+  { 0x0700, 0x123f, 0x0000 },
+  { 0x0700, 0x1241, 0x0000 },
+  { 0x8700, 0x1244, 0x2000 },
+  { 0x0700, 0x1243, 0x0000 },
+  { 0x0700, 0x1245, 0x0000 },
+  { 0x8700, 0x126e, 0x6000 },
+  { 0x8700, 0x125c, 0x5000 },
+  { 0x8700, 0x1252, 0x4000 },
+  { 0x8700, 0x124c, 0x3000 },
+  { 0x8700, 0x124a, 0x2000 },
+  { 0x0700, 0x1248, 0x0000 },
+  { 0x0700, 0x124b, 0x0000 },
+  { 0x8700, 0x1250, 0x2000 },
+  { 0x0700, 0x124d, 0x0000 },
+  { 0x0700, 0x1251, 0x0000 },
+  { 0x8700, 0x1256, 0x3000 },
+  { 0x8700, 0x1254, 0x2000 },
+  { 0x0700, 0x1253, 0x0000 },
+  { 0x0700, 0x1255, 0x0000 },
+  { 0x8700, 0x125a, 0x2000 },
+  { 0x0700, 0x1258, 0x0000 },
+  { 0x0700, 0x125b, 0x0000 },
+  { 0x8700, 0x1266, 0x4000 },
+  { 0x8700, 0x1262, 0x3000 },
+  { 0x8700, 0x1260, 0x2000 },
+  { 0x0700, 0x125d, 0x0000 },
+  { 0x0700, 0x1261, 0x0000 },
+  { 0x8700, 0x1264, 0x2000 },
+  { 0x0700, 0x1263, 0x0000 },
+  { 0x0700, 0x1265, 0x0000 },
+  { 0x8700, 0x126a, 0x3000 },
+  { 0x8700, 0x1268, 0x2000 },
+  { 0x0700, 0x1267, 0x0000 },
+  { 0x0700, 0x1269, 0x0000 },
+  { 0x8700, 0x126c, 0x2000 },
+  { 0x0700, 0x126b, 0x0000 },
+  { 0x0700, 0x126d, 0x0000 },
+  { 0x8700, 0x127e, 0x5000 },
+  { 0x8700, 0x1276, 0x4000 },
+  { 0x8700, 0x1272, 0x3000 },
+  { 0x8700, 0x1270, 0x2000 },
+  { 0x0700, 0x126f, 0x0000 },
+  { 0x0700, 0x1271, 0x0000 },
+  { 0x8700, 0x1274, 0x2000 },
+  { 0x0700, 0x1273, 0x0000 },
+  { 0x0700, 0x1275, 0x0000 },
+  { 0x8700, 0x127a, 0x3000 },
+  { 0x8700, 0x1278, 0x2000 },
+  { 0x0700, 0x1277, 0x0000 },
+  { 0x0700, 0x1279, 0x0000 },
+  { 0x8700, 0x127c, 0x2000 },
+  { 0x0700, 0x127b, 0x0000 },
+  { 0x0700, 0x127d, 0x0000 },
+  { 0x8700, 0x1286, 0x4000 },
+  { 0x8700, 0x1282, 0x3000 },
+  { 0x8700, 0x1280, 0x2000 },
+  { 0x0700, 0x127f, 0x0000 },
+  { 0x0700, 0x1281, 0x0000 },
+  { 0x8700, 0x1284, 0x2000 },
+  { 0x0700, 0x1283, 0x0000 },
+  { 0x0700, 0x1285, 0x0000 },
+  { 0x8700, 0x128c, 0x3000 },
+  { 0x8700, 0x128a, 0x2000 },
+  { 0x0700, 0x1288, 0x0000 },
+  { 0x0700, 0x128b, 0x0000 },
+  { 0x8700, 0x1290, 0x2000 },
+  { 0x0700, 0x128d, 0x0000 },
+  { 0x0700, 0x1291, 0x0000 },
+  { 0x8700, 0x12dc, 0x7000 },
+  { 0x8700, 0x12b4, 0x6000 },
+  { 0x8700, 0x12a2, 0x5000 },
+  { 0x8700, 0x129a, 0x4000 },
+  { 0x8700, 0x1296, 0x3000 },
+  { 0x8700, 0x1294, 0x2000 },
+  { 0x0700, 0x1293, 0x0000 },
+  { 0x0700, 0x1295, 0x0000 },
+  { 0x8700, 0x1298, 0x2000 },
+  { 0x0700, 0x1297, 0x0000 },
+  { 0x0700, 0x1299, 0x0000 },
+  { 0x8700, 0x129e, 0x3000 },
+  { 0x8700, 0x129c, 0x2000 },
+  { 0x0700, 0x129b, 0x0000 },
+  { 0x0700, 0x129d, 0x0000 },
+  { 0x8700, 0x12a0, 0x2000 },
+  { 0x0700, 0x129f, 0x0000 },
+  { 0x0700, 0x12a1, 0x0000 },
+  { 0x8700, 0x12aa, 0x4000 },
+  { 0x8700, 0x12a6, 0x3000 },
+  { 0x8700, 0x12a4, 0x2000 },
+  { 0x0700, 0x12a3, 0x0000 },
+  { 0x0700, 0x12a5, 0x0000 },
+  { 0x8700, 0x12a8, 0x2000 },
+  { 0x0700, 0x12a7, 0x0000 },
+  { 0x0700, 0x12a9, 0x0000 },
+  { 0x8700, 0x12ae, 0x3000 },
+  { 0x8700, 0x12ac, 0x2000 },
+  { 0x0700, 0x12ab, 0x0000 },
+  { 0x0700, 0x12ad, 0x0000 },
+  { 0x8700, 0x12b2, 0x2000 },
+  { 0x0700, 0x12b0, 0x0000 },
+  { 0x0700, 0x12b3, 0x0000 },
+  { 0x8700, 0x12ca, 0x5000 },
+  { 0x8700, 0x12be, 0x4000 },
+  { 0x8700, 0x12ba, 0x3000 },
+  { 0x8700, 0x12b8, 0x2000 },
+  { 0x0700, 0x12b5, 0x0000 },
+  { 0x0700, 0x12b9, 0x0000 },
+  { 0x8700, 0x12bc, 0x2000 },
+  { 0x0700, 0x12bb, 0x0000 },
+  { 0x0700, 0x12bd, 0x0000 },
+  { 0x8700, 0x12c4, 0x3000 },
+  { 0x8700, 0x12c2, 0x2000 },
+  { 0x0700, 0x12c0, 0x0000 },
+  { 0x0700, 0x12c3, 0x0000 },
+  { 0x8700, 0x12c8, 0x2000 },
+  { 0x0700, 0x12c5, 0x0000 },
+  { 0x0700, 0x12c9, 0x0000 },
+  { 0x8700, 0x12d3, 0x4000 },
+  { 0x8700, 0x12ce, 0x3000 },
+  { 0x8700, 0x12cc, 0x2000 },
+  { 0x0700, 0x12cb, 0x0000 },
+  { 0x0700, 0x12cd, 0x0000 },
+  { 0x8700, 0x12d1, 0x2000 },
+  { 0x0700, 0x12d0, 0x0000 },
+  { 0x0700, 0x12d2, 0x0000 },
+  { 0x8700, 0x12d8, 0x3000 },
+  { 0x8700, 0x12d5, 0x2000 },
+  { 0x0700, 0x12d4, 0x0000 },
+  { 0x0700, 0x12d6, 0x0000 },
+  { 0x8700, 0x12da, 0x2000 },
+  { 0x0700, 0x12d9, 0x0000 },
+  { 0x0700, 0x12db, 0x0000 },
+  { 0x8700, 0x12fd, 0x6000 },
+  { 0x8700, 0x12ec, 0x5000 },
+  { 0x8700, 0x12e4, 0x4000 },
+  { 0x8700, 0x12e0, 0x3000 },
+  { 0x8700, 0x12de, 0x2000 },
+  { 0x0700, 0x12dd, 0x0000 },
+  { 0x0700, 0x12df, 0x0000 },
+  { 0x8700, 0x12e2, 0x2000 },
+  { 0x0700, 0x12e1, 0x0000 },
+  { 0x0700, 0x12e3, 0x0000 },
+  { 0x8700, 0x12e8, 0x3000 },
+  { 0x8700, 0x12e6, 0x2000 },
+  { 0x0700, 0x12e5, 0x0000 },
+  { 0x0700, 0x12e7, 0x0000 },
+  { 0x8700, 0x12ea, 0x2000 },
+  { 0x0700, 0x12e9, 0x0000 },
+  { 0x0700, 0x12eb, 0x0000 },
+  { 0x8700, 0x12f5, 0x4000 },
+  { 0x8700, 0x12f1, 0x3000 },
+  { 0x8700, 0x12ee, 0x2000 },
+  { 0x0700, 0x12ed, 0x0000 },
+  { 0x0700, 0x12f0, 0x0000 },
+  { 0x8700, 0x12f3, 0x2000 },
+  { 0x0700, 0x12f2, 0x0000 },
+  { 0x0700, 0x12f4, 0x0000 },
+  { 0x8700, 0x12f9, 0x3000 },
+  { 0x8700, 0x12f7, 0x2000 },
+  { 0x0700, 0x12f6, 0x0000 },
+  { 0x0700, 0x12f8, 0x0000 },
+  { 0x8700, 0x12fb, 0x2000 },
+  { 0x0700, 0x12fa, 0x0000 },
+  { 0x0700, 0x12fc, 0x0000 },
+  { 0x8700, 0x130d, 0x5000 },
+  { 0x8700, 0x1305, 0x4000 },
+  { 0x8700, 0x1301, 0x3000 },
+  { 0x8700, 0x12ff, 0x2000 },
+  { 0x0700, 0x12fe, 0x0000 },
+  { 0x0700, 0x1300, 0x0000 },
+  { 0x8700, 0x1303, 0x2000 },
+  { 0x0700, 0x1302, 0x0000 },
+  { 0x0700, 0x1304, 0x0000 },
+  { 0x8700, 0x1309, 0x3000 },
+  { 0x8700, 0x1307, 0x2000 },
+  { 0x0700, 0x1306, 0x0000 },
+  { 0x0700, 0x1308, 0x0000 },
+  { 0x8700, 0x130b, 0x2000 },
+  { 0x0700, 0x130a, 0x0000 },
+  { 0x0700, 0x130c, 0x0000 },
+  { 0x8700, 0x1319, 0x4000 },
+  { 0x8700, 0x1313, 0x3000 },
+  { 0x8700, 0x1310, 0x2000 },
+  { 0x0700, 0x130e, 0x0000 },
+  { 0x0700, 0x1312, 0x0000 },
+  { 0x8700, 0x1315, 0x2000 },
+  { 0x0700, 0x1314, 0x0000 },
+  { 0x0700, 0x1318, 0x0000 },
+  { 0x8700, 0x131d, 0x3000 },
+  { 0x8700, 0x131b, 0x2000 },
+  { 0x0700, 0x131a, 0x0000 },
+  { 0x0700, 0x131c, 0x0000 },
+  { 0x8700, 0x1320, 0x2000 },
+  { 0x0700, 0x131e, 0x0000 },
+  { 0x0700, 0x1321, 0x0000 },
+  { 0x8700, 0x1458, 0x9000 },
+  { 0x8700, 0x13cc, 0x8000 },
+  { 0x8d00, 0x1369, 0x7000 },
+  { 0x8700, 0x1342, 0x6000 },
+  { 0x8700, 0x1332, 0x5000 },
+  { 0x8700, 0x132a, 0x4000 },
+  { 0x8700, 0x1326, 0x3000 },
+  { 0x8700, 0x1324, 0x2000 },
+  { 0x0700, 0x1323, 0x0000 },
+  { 0x0700, 0x1325, 0x0000 },
+  { 0x8700, 0x1328, 0x2000 },
+  { 0x0700, 0x1327, 0x0000 },
+  { 0x0700, 0x1329, 0x0000 },
+  { 0x8700, 0x132e, 0x3000 },
+  { 0x8700, 0x132c, 0x2000 },
+  { 0x0700, 0x132b, 0x0000 },
+  { 0x0700, 0x132d, 0x0000 },
+  { 0x8700, 0x1330, 0x2000 },
+  { 0x0700, 0x132f, 0x0000 },
+  { 0x0700, 0x1331, 0x0000 },
+  { 0x8700, 0x133a, 0x4000 },
+  { 0x8700, 0x1336, 0x3000 },
+  { 0x8700, 0x1334, 0x2000 },
+  { 0x0700, 0x1333, 0x0000 },
+  { 0x0700, 0x1335, 0x0000 },
+  { 0x8700, 0x1338, 0x2000 },
+  { 0x0700, 0x1337, 0x0000 },
+  { 0x0700, 0x1339, 0x0000 },
+  { 0x8700, 0x133e, 0x3000 },
+  { 0x8700, 0x133c, 0x2000 },
+  { 0x0700, 0x133b, 0x0000 },
+  { 0x0700, 0x133d, 0x0000 },
+  { 0x8700, 0x1340, 0x2000 },
+  { 0x0700, 0x133f, 0x0000 },
+  { 0x0700, 0x1341, 0x0000 },
+  { 0x8700, 0x1353, 0x5000 },
+  { 0x8700, 0x134b, 0x4000 },
+  { 0x8700, 0x1346, 0x3000 },
+  { 0x8700, 0x1344, 0x2000 },
+  { 0x0700, 0x1343, 0x0000 },
+  { 0x0700, 0x1345, 0x0000 },
+  { 0x8700, 0x1349, 0x2000 },
+  { 0x0700, 0x1348, 0x0000 },
+  { 0x0700, 0x134a, 0x0000 },
+  { 0x8700, 0x134f, 0x3000 },
+  { 0x8700, 0x134d, 0x2000 },
+  { 0x0700, 0x134c, 0x0000 },
+  { 0x0700, 0x134e, 0x0000 },
+  { 0x8700, 0x1351, 0x2000 },
+  { 0x0700, 0x1350, 0x0000 },
+  { 0x0700, 0x1352, 0x0000 },
+  { 0x9500, 0x1361, 0x4000 },
+  { 0x8700, 0x1357, 0x3000 },
+  { 0x8700, 0x1355, 0x2000 },
+  { 0x0700, 0x1354, 0x0000 },
+  { 0x0700, 0x1356, 0x0000 },
+  { 0x8700, 0x1359, 0x2000 },
+  { 0x0700, 0x1358, 0x0000 },
+  { 0x0700, 0x135a, 0x0000 },
+  { 0x9500, 0x1365, 0x3000 },
+  { 0x9500, 0x1363, 0x2000 },
+  { 0x1500, 0x1362, 0x0000 },
+  { 0x1500, 0x1364, 0x0000 },
+  { 0x9500, 0x1367, 0x2000 },
+  { 0x1500, 0x1366, 0x0000 },
+  { 0x1500, 0x1368, 0x0000 },
+  { 0x8700, 0x13ac, 0x6000 },
+  { 0x8f00, 0x1379, 0x5000 },
+  { 0x8d00, 0x1371, 0x4000 },
+  { 0x8d00, 0x136d, 0x3000 },
+  { 0x8d00, 0x136b, 0x2000 },
+  { 0x0d00, 0x136a, 0x0000 },
+  { 0x0d00, 0x136c, 0x0000 },
+  { 0x8d00, 0x136f, 0x2000 },
+  { 0x0d00, 0x136e, 0x0000 },
+  { 0x0d00, 0x1370, 0x0000 },
+  { 0x8f00, 0x1375, 0x3000 },
+  { 0x8f00, 0x1373, 0x2000 },
+  { 0x0f00, 0x1372, 0x0000 },
+  { 0x0f00, 0x1374, 0x0000 },
+  { 0x8f00, 0x1377, 0x2000 },
+  { 0x0f00, 0x1376, 0x0000 },
+  { 0x0f00, 0x1378, 0x0000 },
+  { 0x8700, 0x13a4, 0x4000 },
+  { 0x8700, 0x13a0, 0x3000 },
+  { 0x8f00, 0x137b, 0x2000 },
+  { 0x0f00, 0x137a, 0x0000 },
+  { 0x0f00, 0x137c, 0x0000 },
+  { 0x8700, 0x13a2, 0x2000 },
+  { 0x0700, 0x13a1, 0x0000 },
+  { 0x0700, 0x13a3, 0x0000 },
+  { 0x8700, 0x13a8, 0x3000 },
+  { 0x8700, 0x13a6, 0x2000 },
+  { 0x0700, 0x13a5, 0x0000 },
+  { 0x0700, 0x13a7, 0x0000 },
+  { 0x8700, 0x13aa, 0x2000 },
+  { 0x0700, 0x13a9, 0x0000 },
+  { 0x0700, 0x13ab, 0x0000 },
+  { 0x8700, 0x13bc, 0x5000 },
+  { 0x8700, 0x13b4, 0x4000 },
+  { 0x8700, 0x13b0, 0x3000 },
+  { 0x8700, 0x13ae, 0x2000 },
+  { 0x0700, 0x13ad, 0x0000 },
+  { 0x0700, 0x13af, 0x0000 },
+  { 0x8700, 0x13b2, 0x2000 },
+  { 0x0700, 0x13b1, 0x0000 },
+  { 0x0700, 0x13b3, 0x0000 },
+  { 0x8700, 0x13b8, 0x3000 },
+  { 0x8700, 0x13b6, 0x2000 },
+  { 0x0700, 0x13b5, 0x0000 },
+  { 0x0700, 0x13b7, 0x0000 },
+  { 0x8700, 0x13ba, 0x2000 },
+  { 0x0700, 0x13b9, 0x0000 },
+  { 0x0700, 0x13bb, 0x0000 },
+  { 0x8700, 0x13c4, 0x4000 },
+  { 0x8700, 0x13c0, 0x3000 },
+  { 0x8700, 0x13be, 0x2000 },
+  { 0x0700, 0x13bd, 0x0000 },
+  { 0x0700, 0x13bf, 0x0000 },
+  { 0x8700, 0x13c2, 0x2000 },
+  { 0x0700, 0x13c1, 0x0000 },
+  { 0x0700, 0x13c3, 0x0000 },
+  { 0x8700, 0x13c8, 0x3000 },
+  { 0x8700, 0x13c6, 0x2000 },
+  { 0x0700, 0x13c5, 0x0000 },
+  { 0x0700, 0x13c7, 0x0000 },
+  { 0x8700, 0x13ca, 0x2000 },
+  { 0x0700, 0x13c9, 0x0000 },
+  { 0x0700, 0x13cb, 0x0000 },
+  { 0x8700, 0x1418, 0x7000 },
+  { 0x8700, 0x13ec, 0x6000 },
+  { 0x8700, 0x13dc, 0x5000 },
+  { 0x8700, 0x13d4, 0x4000 },
+  { 0x8700, 0x13d0, 0x3000 },
+  { 0x8700, 0x13ce, 0x2000 },
+  { 0x0700, 0x13cd, 0x0000 },
+  { 0x0700, 0x13cf, 0x0000 },
+  { 0x8700, 0x13d2, 0x2000 },
+  { 0x0700, 0x13d1, 0x0000 },
+  { 0x0700, 0x13d3, 0x0000 },
+  { 0x8700, 0x13d8, 0x3000 },
+  { 0x8700, 0x13d6, 0x2000 },
+  { 0x0700, 0x13d5, 0x0000 },
+  { 0x0700, 0x13d7, 0x0000 },
+  { 0x8700, 0x13da, 0x2000 },
+  { 0x0700, 0x13d9, 0x0000 },
+  { 0x0700, 0x13db, 0x0000 },
+  { 0x8700, 0x13e4, 0x4000 },
+  { 0x8700, 0x13e0, 0x3000 },
+  { 0x8700, 0x13de, 0x2000 },
+  { 0x0700, 0x13dd, 0x0000 },
+  { 0x0700, 0x13df, 0x0000 },
+  { 0x8700, 0x13e2, 0x2000 },
+  { 0x0700, 0x13e1, 0x0000 },
+  { 0x0700, 0x13e3, 0x0000 },
+  { 0x8700, 0x13e8, 0x3000 },
+  { 0x8700, 0x13e6, 0x2000 },
+  { 0x0700, 0x13e5, 0x0000 },
+  { 0x0700, 0x13e7, 0x0000 },
+  { 0x8700, 0x13ea, 0x2000 },
+  { 0x0700, 0x13e9, 0x0000 },
+  { 0x0700, 0x13eb, 0x0000 },
+  { 0x8700, 0x1408, 0x5000 },
+  { 0x8700, 0x13f4, 0x4000 },
+  { 0x8700, 0x13f0, 0x3000 },
+  { 0x8700, 0x13ee, 0x2000 },
+  { 0x0700, 0x13ed, 0x0000 },
+  { 0x0700, 0x13ef, 0x0000 },
+  { 0x8700, 0x13f2, 0x2000 },
+  { 0x0700, 0x13f1, 0x0000 },
+  { 0x0700, 0x13f3, 0x0000 },
+  { 0x8700, 0x1404, 0x3000 },
+  { 0x8700, 0x1402, 0x2000 },
+  { 0x0700, 0x1401, 0x0000 },
+  { 0x0700, 0x1403, 0x0000 },
+  { 0x8700, 0x1406, 0x2000 },
+  { 0x0700, 0x1405, 0x0000 },
+  { 0x0700, 0x1407, 0x0000 },
+  { 0x8700, 0x1410, 0x4000 },
+  { 0x8700, 0x140c, 0x3000 },
+  { 0x8700, 0x140a, 0x2000 },
+  { 0x0700, 0x1409, 0x0000 },
+  { 0x0700, 0x140b, 0x0000 },
+  { 0x8700, 0x140e, 0x2000 },
+  { 0x0700, 0x140d, 0x0000 },
+  { 0x0700, 0x140f, 0x0000 },
+  { 0x8700, 0x1414, 0x3000 },
+  { 0x8700, 0x1412, 0x2000 },
+  { 0x0700, 0x1411, 0x0000 },
+  { 0x0700, 0x1413, 0x0000 },
+  { 0x8700, 0x1416, 0x2000 },
+  { 0x0700, 0x1415, 0x0000 },
+  { 0x0700, 0x1417, 0x0000 },
+  { 0x8700, 0x1438, 0x6000 },
+  { 0x8700, 0x1428, 0x5000 },
+  { 0x8700, 0x1420, 0x4000 },
+  { 0x8700, 0x141c, 0x3000 },
+  { 0x8700, 0x141a, 0x2000 },
+  { 0x0700, 0x1419, 0x0000 },
+  { 0x0700, 0x141b, 0x0000 },
+  { 0x8700, 0x141e, 0x2000 },
+  { 0x0700, 0x141d, 0x0000 },
+  { 0x0700, 0x141f, 0x0000 },
+  { 0x8700, 0x1424, 0x3000 },
+  { 0x8700, 0x1422, 0x2000 },
+  { 0x0700, 0x1421, 0x0000 },
+  { 0x0700, 0x1423, 0x0000 },
+  { 0x8700, 0x1426, 0x2000 },
+  { 0x0700, 0x1425, 0x0000 },
+  { 0x0700, 0x1427, 0x0000 },
+  { 0x8700, 0x1430, 0x4000 },
+  { 0x8700, 0x142c, 0x3000 },
+  { 0x8700, 0x142a, 0x2000 },
+  { 0x0700, 0x1429, 0x0000 },
+  { 0x0700, 0x142b, 0x0000 },
+  { 0x8700, 0x142e, 0x2000 },
+  { 0x0700, 0x142d, 0x0000 },
+  { 0x0700, 0x142f, 0x0000 },
+  { 0x8700, 0x1434, 0x3000 },
+  { 0x8700, 0x1432, 0x2000 },
+  { 0x0700, 0x1431, 0x0000 },
+  { 0x0700, 0x1433, 0x0000 },
+  { 0x8700, 0x1436, 0x2000 },
+  { 0x0700, 0x1435, 0x0000 },
+  { 0x0700, 0x1437, 0x0000 },
+  { 0x8700, 0x1448, 0x5000 },
+  { 0x8700, 0x1440, 0x4000 },
+  { 0x8700, 0x143c, 0x3000 },
+  { 0x8700, 0x143a, 0x2000 },
+  { 0x0700, 0x1439, 0x0000 },
+  { 0x0700, 0x143b, 0x0000 },
+  { 0x8700, 0x143e, 0x2000 },
+  { 0x0700, 0x143d, 0x0000 },
+  { 0x0700, 0x143f, 0x0000 },
+  { 0x8700, 0x1444, 0x3000 },
+  { 0x8700, 0x1442, 0x2000 },
+  { 0x0700, 0x1441, 0x0000 },
+  { 0x0700, 0x1443, 0x0000 },
+  { 0x8700, 0x1446, 0x2000 },
+  { 0x0700, 0x1445, 0x0000 },
+  { 0x0700, 0x1447, 0x0000 },
+  { 0x8700, 0x1450, 0x4000 },
+  { 0x8700, 0x144c, 0x3000 },
+  { 0x8700, 0x144a, 0x2000 },
+  { 0x0700, 0x1449, 0x0000 },
+  { 0x0700, 0x144b, 0x0000 },
+  { 0x8700, 0x144e, 0x2000 },
+  { 0x0700, 0x144d, 0x0000 },
+  { 0x0700, 0x144f, 0x0000 },
+  { 0x8700, 0x1454, 0x3000 },
+  { 0x8700, 0x1452, 0x2000 },
+  { 0x0700, 0x1451, 0x0000 },
+  { 0x0700, 0x1453, 0x0000 },
+  { 0x8700, 0x1456, 0x2000 },
+  { 0x0700, 0x1455, 0x0000 },
+  { 0x0700, 0x1457, 0x0000 },
+  { 0x8700, 0x14d8, 0x8000 },
+  { 0x8700, 0x1498, 0x7000 },
+  { 0x8700, 0x1478, 0x6000 },
+  { 0x8700, 0x1468, 0x5000 },
+  { 0x8700, 0x1460, 0x4000 },
+  { 0x8700, 0x145c, 0x3000 },
+  { 0x8700, 0x145a, 0x2000 },
+  { 0x0700, 0x1459, 0x0000 },
+  { 0x0700, 0x145b, 0x0000 },
+  { 0x8700, 0x145e, 0x2000 },
+  { 0x0700, 0x145d, 0x0000 },
+  { 0x0700, 0x145f, 0x0000 },
+  { 0x8700, 0x1464, 0x3000 },
+  { 0x8700, 0x1462, 0x2000 },
+  { 0x0700, 0x1461, 0x0000 },
+  { 0x0700, 0x1463, 0x0000 },
+  { 0x8700, 0x1466, 0x2000 },
+  { 0x0700, 0x1465, 0x0000 },
+  { 0x0700, 0x1467, 0x0000 },
+  { 0x8700, 0x1470, 0x4000 },
+  { 0x8700, 0x146c, 0x3000 },
+  { 0x8700, 0x146a, 0x2000 },
+  { 0x0700, 0x1469, 0x0000 },
+  { 0x0700, 0x146b, 0x0000 },
+  { 0x8700, 0x146e, 0x2000 },
+  { 0x0700, 0x146d, 0x0000 },
+  { 0x0700, 0x146f, 0x0000 },
+  { 0x8700, 0x1474, 0x3000 },
+  { 0x8700, 0x1472, 0x2000 },
+  { 0x0700, 0x1471, 0x0000 },
+  { 0x0700, 0x1473, 0x0000 },
+  { 0x8700, 0x1476, 0x2000 },
+  { 0x0700, 0x1475, 0x0000 },
+  { 0x0700, 0x1477, 0x0000 },
+  { 0x8700, 0x1488, 0x5000 },
+  { 0x8700, 0x1480, 0x4000 },
+  { 0x8700, 0x147c, 0x3000 },
+  { 0x8700, 0x147a, 0x2000 },
+  { 0x0700, 0x1479, 0x0000 },
+  { 0x0700, 0x147b, 0x0000 },
+  { 0x8700, 0x147e, 0x2000 },
+  { 0x0700, 0x147d, 0x0000 },
+  { 0x0700, 0x147f, 0x0000 },
+  { 0x8700, 0x1484, 0x3000 },
+  { 0x8700, 0x1482, 0x2000 },
+  { 0x0700, 0x1481, 0x0000 },
+  { 0x0700, 0x1483, 0x0000 },
+  { 0x8700, 0x1486, 0x2000 },
+  { 0x0700, 0x1485, 0x0000 },
+  { 0x0700, 0x1487, 0x0000 },
+  { 0x8700, 0x1490, 0x4000 },
+  { 0x8700, 0x148c, 0x3000 },
+  { 0x8700, 0x148a, 0x2000 },
+  { 0x0700, 0x1489, 0x0000 },
+  { 0x0700, 0x148b, 0x0000 },
+  { 0x8700, 0x148e, 0x2000 },
+  { 0x0700, 0x148d, 0x0000 },
+  { 0x0700, 0x148f, 0x0000 },
+  { 0x8700, 0x1494, 0x3000 },
+  { 0x8700, 0x1492, 0x2000 },
+  { 0x0700, 0x1491, 0x0000 },
+  { 0x0700, 0x1493, 0x0000 },
+  { 0x8700, 0x1496, 0x2000 },
+  { 0x0700, 0x1495, 0x0000 },
+  { 0x0700, 0x1497, 0x0000 },
+  { 0x8700, 0x14b8, 0x6000 },
+  { 0x8700, 0x14a8, 0x5000 },
+  { 0x8700, 0x14a0, 0x4000 },
+  { 0x8700, 0x149c, 0x3000 },
+  { 0x8700, 0x149a, 0x2000 },
+  { 0x0700, 0x1499, 0x0000 },
+  { 0x0700, 0x149b, 0x0000 },
+  { 0x8700, 0x149e, 0x2000 },
+  { 0x0700, 0x149d, 0x0000 },
+  { 0x0700, 0x149f, 0x0000 },
+  { 0x8700, 0x14a4, 0x3000 },
+  { 0x8700, 0x14a2, 0x2000 },
+  { 0x0700, 0x14a1, 0x0000 },
+  { 0x0700, 0x14a3, 0x0000 },
+  { 0x8700, 0x14a6, 0x2000 },
+  { 0x0700, 0x14a5, 0x0000 },
+  { 0x0700, 0x14a7, 0x0000 },
+  { 0x8700, 0x14b0, 0x4000 },
+  { 0x8700, 0x14ac, 0x3000 },
+  { 0x8700, 0x14aa, 0x2000 },
+  { 0x0700, 0x14a9, 0x0000 },
+  { 0x0700, 0x14ab, 0x0000 },
+  { 0x8700, 0x14ae, 0x2000 },
+  { 0x0700, 0x14ad, 0x0000 },
+  { 0x0700, 0x14af, 0x0000 },
+  { 0x8700, 0x14b4, 0x3000 },
+  { 0x8700, 0x14b2, 0x2000 },
+  { 0x0700, 0x14b1, 0x0000 },
+  { 0x0700, 0x14b3, 0x0000 },
+  { 0x8700, 0x14b6, 0x2000 },
+  { 0x0700, 0x14b5, 0x0000 },
+  { 0x0700, 0x14b7, 0x0000 },
+  { 0x8700, 0x14c8, 0x5000 },
+  { 0x8700, 0x14c0, 0x4000 },
+  { 0x8700, 0x14bc, 0x3000 },
+  { 0x8700, 0x14ba, 0x2000 },
+  { 0x0700, 0x14b9, 0x0000 },
+  { 0x0700, 0x14bb, 0x0000 },
+  { 0x8700, 0x14be, 0x2000 },
+  { 0x0700, 0x14bd, 0x0000 },
+  { 0x0700, 0x14bf, 0x0000 },
+  { 0x8700, 0x14c4, 0x3000 },
+  { 0x8700, 0x14c2, 0x2000 },
+  { 0x0700, 0x14c1, 0x0000 },
+  { 0x0700, 0x14c3, 0x0000 },
+  { 0x8700, 0x14c6, 0x2000 },
+  { 0x0700, 0x14c5, 0x0000 },
+  { 0x0700, 0x14c7, 0x0000 },
+  { 0x8700, 0x14d0, 0x4000 },
+  { 0x8700, 0x14cc, 0x3000 },
+  { 0x8700, 0x14ca, 0x2000 },
+  { 0x0700, 0x14c9, 0x0000 },
+  { 0x0700, 0x14cb, 0x0000 },
+  { 0x8700, 0x14ce, 0x2000 },
+  { 0x0700, 0x14cd, 0x0000 },
+  { 0x0700, 0x14cf, 0x0000 },
+  { 0x8700, 0x14d4, 0x3000 },
+  { 0x8700, 0x14d2, 0x2000 },
+  { 0x0700, 0x14d1, 0x0000 },
+  { 0x0700, 0x14d3, 0x0000 },
+  { 0x8700, 0x14d6, 0x2000 },
+  { 0x0700, 0x14d5, 0x0000 },
+  { 0x0700, 0x14d7, 0x0000 },
+  { 0x8700, 0x1518, 0x7000 },
+  { 0x8700, 0x14f8, 0x6000 },
+  { 0x8700, 0x14e8, 0x5000 },
+  { 0x8700, 0x14e0, 0x4000 },
+  { 0x8700, 0x14dc, 0x3000 },
+  { 0x8700, 0x14da, 0x2000 },
+  { 0x0700, 0x14d9, 0x0000 },
+  { 0x0700, 0x14db, 0x0000 },
+  { 0x8700, 0x14de, 0x2000 },
+  { 0x0700, 0x14dd, 0x0000 },
+  { 0x0700, 0x14df, 0x0000 },
+  { 0x8700, 0x14e4, 0x3000 },
+  { 0x8700, 0x14e2, 0x2000 },
+  { 0x0700, 0x14e1, 0x0000 },
+  { 0x0700, 0x14e3, 0x0000 },
+  { 0x8700, 0x14e6, 0x2000 },
+  { 0x0700, 0x14e5, 0x0000 },
+  { 0x0700, 0x14e7, 0x0000 },
+  { 0x8700, 0x14f0, 0x4000 },
+  { 0x8700, 0x14ec, 0x3000 },
+  { 0x8700, 0x14ea, 0x2000 },
+  { 0x0700, 0x14e9, 0x0000 },
+  { 0x0700, 0x14eb, 0x0000 },
+  { 0x8700, 0x14ee, 0x2000 },
+  { 0x0700, 0x14ed, 0x0000 },
+  { 0x0700, 0x14ef, 0x0000 },
+  { 0x8700, 0x14f4, 0x3000 },
+  { 0x8700, 0x14f2, 0x2000 },
+  { 0x0700, 0x14f1, 0x0000 },
+  { 0x0700, 0x14f3, 0x0000 },
+  { 0x8700, 0x14f6, 0x2000 },
+  { 0x0700, 0x14f5, 0x0000 },
+  { 0x0700, 0x14f7, 0x0000 },
+  { 0x8700, 0x1508, 0x5000 },
+  { 0x8700, 0x1500, 0x4000 },
+  { 0x8700, 0x14fc, 0x3000 },
+  { 0x8700, 0x14fa, 0x2000 },
+  { 0x0700, 0x14f9, 0x0000 },
+  { 0x0700, 0x14fb, 0x0000 },
+  { 0x8700, 0x14fe, 0x2000 },
+  { 0x0700, 0x14fd, 0x0000 },
+  { 0x0700, 0x14ff, 0x0000 },
+  { 0x8700, 0x1504, 0x3000 },
+  { 0x8700, 0x1502, 0x2000 },
+  { 0x0700, 0x1501, 0x0000 },
+  { 0x0700, 0x1503, 0x0000 },
+  { 0x8700, 0x1506, 0x2000 },
+  { 0x0700, 0x1505, 0x0000 },
+  { 0x0700, 0x1507, 0x0000 },
+  { 0x8700, 0x1510, 0x4000 },
+  { 0x8700, 0x150c, 0x3000 },
+  { 0x8700, 0x150a, 0x2000 },
+  { 0x0700, 0x1509, 0x0000 },
+  { 0x0700, 0x150b, 0x0000 },
+  { 0x8700, 0x150e, 0x2000 },
+  { 0x0700, 0x150d, 0x0000 },
+  { 0x0700, 0x150f, 0x0000 },
+  { 0x8700, 0x1514, 0x3000 },
+  { 0x8700, 0x1512, 0x2000 },
+  { 0x0700, 0x1511, 0x0000 },
+  { 0x0700, 0x1513, 0x0000 },
+  { 0x8700, 0x1516, 0x2000 },
+  { 0x0700, 0x1515, 0x0000 },
+  { 0x0700, 0x1517, 0x0000 },
+  { 0x8700, 0x1538, 0x6000 },
+  { 0x8700, 0x1528, 0x5000 },
+  { 0x8700, 0x1520, 0x4000 },
+  { 0x8700, 0x151c, 0x3000 },
+  { 0x8700, 0x151a, 0x2000 },
+  { 0x0700, 0x1519, 0x0000 },
+  { 0x0700, 0x151b, 0x0000 },
+  { 0x8700, 0x151e, 0x2000 },
+  { 0x0700, 0x151d, 0x0000 },
+  { 0x0700, 0x151f, 0x0000 },
+  { 0x8700, 0x1524, 0x3000 },
+  { 0x8700, 0x1522, 0x2000 },
+  { 0x0700, 0x1521, 0x0000 },
+  { 0x0700, 0x1523, 0x0000 },
+  { 0x8700, 0x1526, 0x2000 },
+  { 0x0700, 0x1525, 0x0000 },
+  { 0x0700, 0x1527, 0x0000 },
+  { 0x8700, 0x1530, 0x4000 },
+  { 0x8700, 0x152c, 0x3000 },
+  { 0x8700, 0x152a, 0x2000 },
+  { 0x0700, 0x1529, 0x0000 },
+  { 0x0700, 0x152b, 0x0000 },
+  { 0x8700, 0x152e, 0x2000 },
+  { 0x0700, 0x152d, 0x0000 },
+  { 0x0700, 0x152f, 0x0000 },
+  { 0x8700, 0x1534, 0x3000 },
+  { 0x8700, 0x1532, 0x2000 },
+  { 0x0700, 0x1531, 0x0000 },
+  { 0x0700, 0x1533, 0x0000 },
+  { 0x8700, 0x1536, 0x2000 },
+  { 0x0700, 0x1535, 0x0000 },
+  { 0x0700, 0x1537, 0x0000 },
+  { 0x8700, 0x1548, 0x5000 },
+  { 0x8700, 0x1540, 0x4000 },
+  { 0x8700, 0x153c, 0x3000 },
+  { 0x8700, 0x153a, 0x2000 },
+  { 0x0700, 0x1539, 0x0000 },
+  { 0x0700, 0x153b, 0x0000 },
+  { 0x8700, 0x153e, 0x2000 },
+  { 0x0700, 0x153d, 0x0000 },
+  { 0x0700, 0x153f, 0x0000 },
+  { 0x8700, 0x1544, 0x3000 },
+  { 0x8700, 0x1542, 0x2000 },
+  { 0x0700, 0x1541, 0x0000 },
+  { 0x0700, 0x1543, 0x0000 },
+  { 0x8700, 0x1546, 0x2000 },
+  { 0x0700, 0x1545, 0x0000 },
+  { 0x0700, 0x1547, 0x0000 },
+  { 0x8700, 0x1550, 0x4000 },
+  { 0x8700, 0x154c, 0x3000 },
+  { 0x8700, 0x154a, 0x2000 },
+  { 0x0700, 0x1549, 0x0000 },
+  { 0x0700, 0x154b, 0x0000 },
+  { 0x8700, 0x154e, 0x2000 },
+  { 0x0700, 0x154d, 0x0000 },
+  { 0x0700, 0x154f, 0x0000 },
+  { 0x8700, 0x1554, 0x3000 },
+  { 0x8700, 0x1552, 0x2000 },
+  { 0x0700, 0x1551, 0x0000 },
+  { 0x0700, 0x1553, 0x0000 },
+  { 0x8700, 0x1556, 0x2000 },
+  { 0x0700, 0x1555, 0x0000 },
+  { 0x0700, 0x1557, 0x0000 },
+  { 0x9900, 0x22ae, 0xc000 },
+  { 0x8900, 0x1e24, 0xb001 },
+  { 0x8700, 0x17a2, 0xa000 },
+  { 0x8700, 0x1658, 0x9000 },
+  { 0x8700, 0x15d8, 0x8000 },
+  { 0x8700, 0x1598, 0x7000 },
+  { 0x8700, 0x1578, 0x6000 },
+  { 0x8700, 0x1568, 0x5000 },
+  { 0x8700, 0x1560, 0x4000 },
+  { 0x8700, 0x155c, 0x3000 },
+  { 0x8700, 0x155a, 0x2000 },
+  { 0x0700, 0x1559, 0x0000 },
+  { 0x0700, 0x155b, 0x0000 },
+  { 0x8700, 0x155e, 0x2000 },
+  { 0x0700, 0x155d, 0x0000 },
+  { 0x0700, 0x155f, 0x0000 },
+  { 0x8700, 0x1564, 0x3000 },
+  { 0x8700, 0x1562, 0x2000 },
+  { 0x0700, 0x1561, 0x0000 },
+  { 0x0700, 0x1563, 0x0000 },
+  { 0x8700, 0x1566, 0x2000 },
+  { 0x0700, 0x1565, 0x0000 },
+  { 0x0700, 0x1567, 0x0000 },
+  { 0x8700, 0x1570, 0x4000 },
+  { 0x8700, 0x156c, 0x3000 },
+  { 0x8700, 0x156a, 0x2000 },
+  { 0x0700, 0x1569, 0x0000 },
+  { 0x0700, 0x156b, 0x0000 },
+  { 0x8700, 0x156e, 0x2000 },
+  { 0x0700, 0x156d, 0x0000 },
+  { 0x0700, 0x156f, 0x0000 },
+  { 0x8700, 0x1574, 0x3000 },
+  { 0x8700, 0x1572, 0x2000 },
+  { 0x0700, 0x1571, 0x0000 },
+  { 0x0700, 0x1573, 0x0000 },
+  { 0x8700, 0x1576, 0x2000 },
+  { 0x0700, 0x1575, 0x0000 },
+  { 0x0700, 0x1577, 0x0000 },
+  { 0x8700, 0x1588, 0x5000 },
+  { 0x8700, 0x1580, 0x4000 },
+  { 0x8700, 0x157c, 0x3000 },
+  { 0x8700, 0x157a, 0x2000 },
+  { 0x0700, 0x1579, 0x0000 },
+  { 0x0700, 0x157b, 0x0000 },
+  { 0x8700, 0x157e, 0x2000 },
+  { 0x0700, 0x157d, 0x0000 },
+  { 0x0700, 0x157f, 0x0000 },
+  { 0x8700, 0x1584, 0x3000 },
+  { 0x8700, 0x1582, 0x2000 },
+  { 0x0700, 0x1581, 0x0000 },
+  { 0x0700, 0x1583, 0x0000 },
+  { 0x8700, 0x1586, 0x2000 },
+  { 0x0700, 0x1585, 0x0000 },
+  { 0x0700, 0x1587, 0x0000 },
+  { 0x8700, 0x1590, 0x4000 },
+  { 0x8700, 0x158c, 0x3000 },
+  { 0x8700, 0x158a, 0x2000 },
+  { 0x0700, 0x1589, 0x0000 },
+  { 0x0700, 0x158b, 0x0000 },
+  { 0x8700, 0x158e, 0x2000 },
+  { 0x0700, 0x158d, 0x0000 },
+  { 0x0700, 0x158f, 0x0000 },
+  { 0x8700, 0x1594, 0x3000 },
+  { 0x8700, 0x1592, 0x2000 },
+  { 0x0700, 0x1591, 0x0000 },
+  { 0x0700, 0x1593, 0x0000 },
+  { 0x8700, 0x1596, 0x2000 },
+  { 0x0700, 0x1595, 0x0000 },
+  { 0x0700, 0x1597, 0x0000 },
+  { 0x8700, 0x15b8, 0x6000 },
+  { 0x8700, 0x15a8, 0x5000 },
+  { 0x8700, 0x15a0, 0x4000 },
+  { 0x8700, 0x159c, 0x3000 },
+  { 0x8700, 0x159a, 0x2000 },
+  { 0x0700, 0x1599, 0x0000 },
+  { 0x0700, 0x159b, 0x0000 },
+  { 0x8700, 0x159e, 0x2000 },
+  { 0x0700, 0x159d, 0x0000 },
+  { 0x0700, 0x159f, 0x0000 },
+  { 0x8700, 0x15a4, 0x3000 },
+  { 0x8700, 0x15a2, 0x2000 },
+  { 0x0700, 0x15a1, 0x0000 },
+  { 0x0700, 0x15a3, 0x0000 },
+  { 0x8700, 0x15a6, 0x2000 },
+  { 0x0700, 0x15a5, 0x0000 },
+  { 0x0700, 0x15a7, 0x0000 },
+  { 0x8700, 0x15b0, 0x4000 },
+  { 0x8700, 0x15ac, 0x3000 },
+  { 0x8700, 0x15aa, 0x2000 },
+  { 0x0700, 0x15a9, 0x0000 },
+  { 0x0700, 0x15ab, 0x0000 },
+  { 0x8700, 0x15ae, 0x2000 },
+  { 0x0700, 0x15ad, 0x0000 },
+  { 0x0700, 0x15af, 0x0000 },
+  { 0x8700, 0x15b4, 0x3000 },
+  { 0x8700, 0x15b2, 0x2000 },
+  { 0x0700, 0x15b1, 0x0000 },
+  { 0x0700, 0x15b3, 0x0000 },
+  { 0x8700, 0x15b6, 0x2000 },
+  { 0x0700, 0x15b5, 0x0000 },
+  { 0x0700, 0x15b7, 0x0000 },
+  { 0x8700, 0x15c8, 0x5000 },
+  { 0x8700, 0x15c0, 0x4000 },
+  { 0x8700, 0x15bc, 0x3000 },
+  { 0x8700, 0x15ba, 0x2000 },
+  { 0x0700, 0x15b9, 0x0000 },
+  { 0x0700, 0x15bb, 0x0000 },
+  { 0x8700, 0x15be, 0x2000 },
+  { 0x0700, 0x15bd, 0x0000 },
+  { 0x0700, 0x15bf, 0x0000 },
+  { 0x8700, 0x15c4, 0x3000 },
+  { 0x8700, 0x15c2, 0x2000 },
+  { 0x0700, 0x15c1, 0x0000 },
+  { 0x0700, 0x15c3, 0x0000 },
+  { 0x8700, 0x15c6, 0x2000 },
+  { 0x0700, 0x15c5, 0x0000 },
+  { 0x0700, 0x15c7, 0x0000 },
+  { 0x8700, 0x15d0, 0x4000 },
+  { 0x8700, 0x15cc, 0x3000 },
+  { 0x8700, 0x15ca, 0x2000 },
+  { 0x0700, 0x15c9, 0x0000 },
+  { 0x0700, 0x15cb, 0x0000 },
+  { 0x8700, 0x15ce, 0x2000 },
+  { 0x0700, 0x15cd, 0x0000 },
+  { 0x0700, 0x15cf, 0x0000 },
+  { 0x8700, 0x15d4, 0x3000 },
+  { 0x8700, 0x15d2, 0x2000 },
+  { 0x0700, 0x15d1, 0x0000 },
+  { 0x0700, 0x15d3, 0x0000 },
+  { 0x8700, 0x15d6, 0x2000 },
+  { 0x0700, 0x15d5, 0x0000 },
+  { 0x0700, 0x15d7, 0x0000 },
+  { 0x8700, 0x1618, 0x7000 },
+  { 0x8700, 0x15f8, 0x6000 },
+  { 0x8700, 0x15e8, 0x5000 },
+  { 0x8700, 0x15e0, 0x4000 },
+  { 0x8700, 0x15dc, 0x3000 },
+  { 0x8700, 0x15da, 0x2000 },
+  { 0x0700, 0x15d9, 0x0000 },
+  { 0x0700, 0x15db, 0x0000 },
+  { 0x8700, 0x15de, 0x2000 },
+  { 0x0700, 0x15dd, 0x0000 },
+  { 0x0700, 0x15df, 0x0000 },
+  { 0x8700, 0x15e4, 0x3000 },
+  { 0x8700, 0x15e2, 0x2000 },
+  { 0x0700, 0x15e1, 0x0000 },
+  { 0x0700, 0x15e3, 0x0000 },
+  { 0x8700, 0x15e6, 0x2000 },
+  { 0x0700, 0x15e5, 0x0000 },
+  { 0x0700, 0x15e7, 0x0000 },
+  { 0x8700, 0x15f0, 0x4000 },
+  { 0x8700, 0x15ec, 0x3000 },
+  { 0x8700, 0x15ea, 0x2000 },
+  { 0x0700, 0x15e9, 0x0000 },
+  { 0x0700, 0x15eb, 0x0000 },
+  { 0x8700, 0x15ee, 0x2000 },
+  { 0x0700, 0x15ed, 0x0000 },
+  { 0x0700, 0x15ef, 0x0000 },
+  { 0x8700, 0x15f4, 0x3000 },
+  { 0x8700, 0x15f2, 0x2000 },
+  { 0x0700, 0x15f1, 0x0000 },
+  { 0x0700, 0x15f3, 0x0000 },
+  { 0x8700, 0x15f6, 0x2000 },
+  { 0x0700, 0x15f5, 0x0000 },
+  { 0x0700, 0x15f7, 0x0000 },
+  { 0x8700, 0x1608, 0x5000 },
+  { 0x8700, 0x1600, 0x4000 },
+  { 0x8700, 0x15fc, 0x3000 },
+  { 0x8700, 0x15fa, 0x2000 },
+  { 0x0700, 0x15f9, 0x0000 },
+  { 0x0700, 0x15fb, 0x0000 },
+  { 0x8700, 0x15fe, 0x2000 },
+  { 0x0700, 0x15fd, 0x0000 },
+  { 0x0700, 0x15ff, 0x0000 },
+  { 0x8700, 0x1604, 0x3000 },
+  { 0x8700, 0x1602, 0x2000 },
+  { 0x0700, 0x1601, 0x0000 },
+  { 0x0700, 0x1603, 0x0000 },
+  { 0x8700, 0x1606, 0x2000 },
+  { 0x0700, 0x1605, 0x0000 },
+  { 0x0700, 0x1607, 0x0000 },
+  { 0x8700, 0x1610, 0x4000 },
+  { 0x8700, 0x160c, 0x3000 },
+  { 0x8700, 0x160a, 0x2000 },
+  { 0x0700, 0x1609, 0x0000 },
+  { 0x0700, 0x160b, 0x0000 },
+  { 0x8700, 0x160e, 0x2000 },
+  { 0x0700, 0x160d, 0x0000 },
+  { 0x0700, 0x160f, 0x0000 },
+  { 0x8700, 0x1614, 0x3000 },
+  { 0x8700, 0x1612, 0x2000 },
+  { 0x0700, 0x1611, 0x0000 },
+  { 0x0700, 0x1613, 0x0000 },
+  { 0x8700, 0x1616, 0x2000 },
+  { 0x0700, 0x1615, 0x0000 },
+  { 0x0700, 0x1617, 0x0000 },
+  { 0x8700, 0x1638, 0x6000 },
+  { 0x8700, 0x1628, 0x5000 },
+  { 0x8700, 0x1620, 0x4000 },
+  { 0x8700, 0x161c, 0x3000 },
+  { 0x8700, 0x161a, 0x2000 },
+  { 0x0700, 0x1619, 0x0000 },
+  { 0x0700, 0x161b, 0x0000 },
+  { 0x8700, 0x161e, 0x2000 },
+  { 0x0700, 0x161d, 0x0000 },
+  { 0x0700, 0x161f, 0x0000 },
+  { 0x8700, 0x1624, 0x3000 },
+  { 0x8700, 0x1622, 0x2000 },
+  { 0x0700, 0x1621, 0x0000 },
+  { 0x0700, 0x1623, 0x0000 },
+  { 0x8700, 0x1626, 0x2000 },
+  { 0x0700, 0x1625, 0x0000 },
+  { 0x0700, 0x1627, 0x0000 },
+  { 0x8700, 0x1630, 0x4000 },
+  { 0x8700, 0x162c, 0x3000 },
+  { 0x8700, 0x162a, 0x2000 },
+  { 0x0700, 0x1629, 0x0000 },
+  { 0x0700, 0x162b, 0x0000 },
+  { 0x8700, 0x162e, 0x2000 },
+  { 0x0700, 0x162d, 0x0000 },
+  { 0x0700, 0x162f, 0x0000 },
+  { 0x8700, 0x1634, 0x3000 },
+  { 0x8700, 0x1632, 0x2000 },
+  { 0x0700, 0x1631, 0x0000 },
+  { 0x0700, 0x1633, 0x0000 },
+  { 0x8700, 0x1636, 0x2000 },
+  { 0x0700, 0x1635, 0x0000 },
+  { 0x0700, 0x1637, 0x0000 },
+  { 0x8700, 0x1648, 0x5000 },
+  { 0x8700, 0x1640, 0x4000 },
+  { 0x8700, 0x163c, 0x3000 },
+  { 0x8700, 0x163a, 0x2000 },
+  { 0x0700, 0x1639, 0x0000 },
+  { 0x0700, 0x163b, 0x0000 },
+  { 0x8700, 0x163e, 0x2000 },
+  { 0x0700, 0x163d, 0x0000 },
+  { 0x0700, 0x163f, 0x0000 },
+  { 0x8700, 0x1644, 0x3000 },
+  { 0x8700, 0x1642, 0x2000 },
+  { 0x0700, 0x1641, 0x0000 },
+  { 0x0700, 0x1643, 0x0000 },
+  { 0x8700, 0x1646, 0x2000 },
+  { 0x0700, 0x1645, 0x0000 },
+  { 0x0700, 0x1647, 0x0000 },
+  { 0x8700, 0x1650, 0x4000 },
+  { 0x8700, 0x164c, 0x3000 },
+  { 0x8700, 0x164a, 0x2000 },
+  { 0x0700, 0x1649, 0x0000 },
+  { 0x0700, 0x164b, 0x0000 },
+  { 0x8700, 0x164e, 0x2000 },
+  { 0x0700, 0x164d, 0x0000 },
+  { 0x0700, 0x164f, 0x0000 },
+  { 0x8700, 0x1654, 0x3000 },
+  { 0x8700, 0x1652, 0x2000 },
+  { 0x0700, 0x1651, 0x0000 },
+  { 0x0700, 0x1653, 0x0000 },
+  { 0x8700, 0x1656, 0x2000 },
+  { 0x0700, 0x1655, 0x0000 },
+  { 0x0700, 0x1657, 0x0000 },
+  { 0x8700, 0x16e4, 0x8000 },
+  { 0x8700, 0x16a4, 0x7000 },
+  { 0x8700, 0x1681, 0x6000 },
+  { 0x8700, 0x1668, 0x5000 },
+  { 0x8700, 0x1660, 0x4000 },
+  { 0x8700, 0x165c, 0x3000 },
+  { 0x8700, 0x165a, 0x2000 },
+  { 0x0700, 0x1659, 0x0000 },
+  { 0x0700, 0x165b, 0x0000 },
+  { 0x8700, 0x165e, 0x2000 },
+  { 0x0700, 0x165d, 0x0000 },
+  { 0x0700, 0x165f, 0x0000 },
+  { 0x8700, 0x1664, 0x3000 },
+  { 0x8700, 0x1662, 0x2000 },
+  { 0x0700, 0x1661, 0x0000 },
+  { 0x0700, 0x1663, 0x0000 },
+  { 0x8700, 0x1666, 0x2000 },
+  { 0x0700, 0x1665, 0x0000 },
+  { 0x0700, 0x1667, 0x0000 },
+  { 0x8700, 0x1670, 0x4000 },
+  { 0x8700, 0x166c, 0x3000 },
+  { 0x8700, 0x166a, 0x2000 },
+  { 0x0700, 0x1669, 0x0000 },
+  { 0x0700, 0x166b, 0x0000 },
+  { 0x9500, 0x166e, 0x2000 },
+  { 0x1500, 0x166d, 0x0000 },
+  { 0x0700, 0x166f, 0x0000 },
+  { 0x8700, 0x1674, 0x3000 },
+  { 0x8700, 0x1672, 0x2000 },
+  { 0x0700, 0x1671, 0x0000 },
+  { 0x0700, 0x1673, 0x0000 },
+  { 0x8700, 0x1676, 0x2000 },
+  { 0x0700, 0x1675, 0x0000 },
+  { 0x1d00, 0x1680, 0x0000 },
+  { 0x8700, 0x1691, 0x5000 },
+  { 0x8700, 0x1689, 0x4000 },
+  { 0x8700, 0x1685, 0x3000 },
+  { 0x8700, 0x1683, 0x2000 },
+  { 0x0700, 0x1682, 0x0000 },
+  { 0x0700, 0x1684, 0x0000 },
+  { 0x8700, 0x1687, 0x2000 },
+  { 0x0700, 0x1686, 0x0000 },
+  { 0x0700, 0x1688, 0x0000 },
+  { 0x8700, 0x168d, 0x3000 },
+  { 0x8700, 0x168b, 0x2000 },
+  { 0x0700, 0x168a, 0x0000 },
+  { 0x0700, 0x168c, 0x0000 },
+  { 0x8700, 0x168f, 0x2000 },
+  { 0x0700, 0x168e, 0x0000 },
+  { 0x0700, 0x1690, 0x0000 },
+  { 0x8700, 0x1699, 0x4000 },
+  { 0x8700, 0x1695, 0x3000 },
+  { 0x8700, 0x1693, 0x2000 },
+  { 0x0700, 0x1692, 0x0000 },
+  { 0x0700, 0x1694, 0x0000 },
+  { 0x8700, 0x1697, 0x2000 },
+  { 0x0700, 0x1696, 0x0000 },
+  { 0x0700, 0x1698, 0x0000 },
+  { 0x8700, 0x16a0, 0x3000 },
+  { 0x9600, 0x169b, 0x2000 },
+  { 0x0700, 0x169a, 0x0000 },
+  { 0x1200, 0x169c, 0x0000 },
+  { 0x8700, 0x16a2, 0x2000 },
+  { 0x0700, 0x16a1, 0x0000 },
+  { 0x0700, 0x16a3, 0x0000 },
+  { 0x8700, 0x16c4, 0x6000 },
+  { 0x8700, 0x16b4, 0x5000 },
+  { 0x8700, 0x16ac, 0x4000 },
+  { 0x8700, 0x16a8, 0x3000 },
+  { 0x8700, 0x16a6, 0x2000 },
+  { 0x0700, 0x16a5, 0x0000 },
+  { 0x0700, 0x16a7, 0x0000 },
+  { 0x8700, 0x16aa, 0x2000 },
+  { 0x0700, 0x16a9, 0x0000 },
+  { 0x0700, 0x16ab, 0x0000 },
+  { 0x8700, 0x16b0, 0x3000 },
+  { 0x8700, 0x16ae, 0x2000 },
+  { 0x0700, 0x16ad, 0x0000 },
+  { 0x0700, 0x16af, 0x0000 },
+  { 0x8700, 0x16b2, 0x2000 },
+  { 0x0700, 0x16b1, 0x0000 },
+  { 0x0700, 0x16b3, 0x0000 },
+  { 0x8700, 0x16bc, 0x4000 },
+  { 0x8700, 0x16b8, 0x3000 },
+  { 0x8700, 0x16b6, 0x2000 },
+  { 0x0700, 0x16b5, 0x0000 },
+  { 0x0700, 0x16b7, 0x0000 },
+  { 0x8700, 0x16ba, 0x2000 },
+  { 0x0700, 0x16b9, 0x0000 },
+  { 0x0700, 0x16bb, 0x0000 },
+  { 0x8700, 0x16c0, 0x3000 },
+  { 0x8700, 0x16be, 0x2000 },
+  { 0x0700, 0x16bd, 0x0000 },
+  { 0x0700, 0x16bf, 0x0000 },
+  { 0x8700, 0x16c2, 0x2000 },
+  { 0x0700, 0x16c1, 0x0000 },
+  { 0x0700, 0x16c3, 0x0000 },
+  { 0x8700, 0x16d4, 0x5000 },
+  { 0x8700, 0x16cc, 0x4000 },
+  { 0x8700, 0x16c8, 0x3000 },
+  { 0x8700, 0x16c6, 0x2000 },
+  { 0x0700, 0x16c5, 0x0000 },
+  { 0x0700, 0x16c7, 0x0000 },
+  { 0x8700, 0x16ca, 0x2000 },
+  { 0x0700, 0x16c9, 0x0000 },
+  { 0x0700, 0x16cb, 0x0000 },
+  { 0x8700, 0x16d0, 0x3000 },
+  { 0x8700, 0x16ce, 0x2000 },
+  { 0x0700, 0x16cd, 0x0000 },
+  { 0x0700, 0x16cf, 0x0000 },
+  { 0x8700, 0x16d2, 0x2000 },
+  { 0x0700, 0x16d1, 0x0000 },
+  { 0x0700, 0x16d3, 0x0000 },
+  { 0x8700, 0x16dc, 0x4000 },
+  { 0x8700, 0x16d8, 0x3000 },
+  { 0x8700, 0x16d6, 0x2000 },
+  { 0x0700, 0x16d5, 0x0000 },
+  { 0x0700, 0x16d7, 0x0000 },
+  { 0x8700, 0x16da, 0x2000 },
+  { 0x0700, 0x16d9, 0x0000 },
+  { 0x0700, 0x16db, 0x0000 },
+  { 0x8700, 0x16e0, 0x3000 },
+  { 0x8700, 0x16de, 0x2000 },
+  { 0x0700, 0x16dd, 0x0000 },
+  { 0x0700, 0x16df, 0x0000 },
+  { 0x8700, 0x16e2, 0x2000 },
+  { 0x0700, 0x16e1, 0x0000 },
+  { 0x0700, 0x16e3, 0x0000 },
+  { 0x8700, 0x1748, 0x7000 },
+  { 0x8c00, 0x1714, 0x6000 },
+  { 0x8700, 0x1703, 0x5000 },
+  { 0x9500, 0x16ec, 0x4000 },
+  { 0x8700, 0x16e8, 0x3000 },
+  { 0x8700, 0x16e6, 0x2000 },
+  { 0x0700, 0x16e5, 0x0000 },
+  { 0x0700, 0x16e7, 0x0000 },
+  { 0x8700, 0x16ea, 0x2000 },
+  { 0x0700, 0x16e9, 0x0000 },
+  { 0x1500, 0x16eb, 0x0000 },
+  { 0x8e00, 0x16f0, 0x3000 },
+  { 0x8e00, 0x16ee, 0x2000 },
+  { 0x1500, 0x16ed, 0x0000 },
+  { 0x0e00, 0x16ef, 0x0000 },
+  { 0x8700, 0x1701, 0x2000 },
+  { 0x0700, 0x1700, 0x0000 },
+  { 0x0700, 0x1702, 0x0000 },
+  { 0x8700, 0x170b, 0x4000 },
+  { 0x8700, 0x1707, 0x3000 },
+  { 0x8700, 0x1705, 0x2000 },
+  { 0x0700, 0x1704, 0x0000 },
+  { 0x0700, 0x1706, 0x0000 },
+  { 0x8700, 0x1709, 0x2000 },
+  { 0x0700, 0x1708, 0x0000 },
+  { 0x0700, 0x170a, 0x0000 },
+  { 0x8700, 0x1710, 0x3000 },
+  { 0x8700, 0x170e, 0x2000 },
+  { 0x0700, 0x170c, 0x0000 },
+  { 0x0700, 0x170f, 0x0000 },
+  { 0x8c00, 0x1712, 0x2000 },
+  { 0x0700, 0x1711, 0x0000 },
+  { 0x0c00, 0x1713, 0x0000 },
+  { 0x8700, 0x172f, 0x5000 },
+  { 0x8700, 0x1727, 0x4000 },
+  { 0x8700, 0x1723, 0x3000 },
+  { 0x8700, 0x1721, 0x2000 },
+  { 0x0700, 0x1720, 0x0000 },
+  { 0x0700, 0x1722, 0x0000 },
+  { 0x8700, 0x1725, 0x2000 },
+  { 0x0700, 0x1724, 0x0000 },
+  { 0x0700, 0x1726, 0x0000 },
+  { 0x8700, 0x172b, 0x3000 },
+  { 0x8700, 0x1729, 0x2000 },
+  { 0x0700, 0x1728, 0x0000 },
+  { 0x0700, 0x172a, 0x0000 },
+  { 0x8700, 0x172d, 0x2000 },
+  { 0x0700, 0x172c, 0x0000 },
+  { 0x0700, 0x172e, 0x0000 },
+  { 0x8700, 0x1740, 0x4000 },
+  { 0x8c00, 0x1733, 0x3000 },
+  { 0x8700, 0x1731, 0x2000 },
+  { 0x0700, 0x1730, 0x0000 },
+  { 0x0c00, 0x1732, 0x0000 },
+  { 0x9500, 0x1735, 0x2000 },
+  { 0x0c00, 0x1734, 0x0000 },
+  { 0x1500, 0x1736, 0x0000 },
+  { 0x8700, 0x1744, 0x3000 },
+  { 0x8700, 0x1742, 0x2000 },
+  { 0x0700, 0x1741, 0x0000 },
+  { 0x0700, 0x1743, 0x0000 },
+  { 0x8700, 0x1746, 0x2000 },
+  { 0x0700, 0x1745, 0x0000 },
+  { 0x0700, 0x1747, 0x0000 },
+  { 0x8700, 0x1782, 0x6000 },
+  { 0x8700, 0x1764, 0x5000 },
+  { 0x8700, 0x1750, 0x4000 },
+  { 0x8700, 0x174c, 0x3000 },
+  { 0x8700, 0x174a, 0x2000 },
+  { 0x0700, 0x1749, 0x0000 },
+  { 0x0700, 0x174b, 0x0000 },
+  { 0x8700, 0x174e, 0x2000 },
+  { 0x0700, 0x174d, 0x0000 },
+  { 0x0700, 0x174f, 0x0000 },
+  { 0x8700, 0x1760, 0x3000 },
+  { 0x8c00, 0x1752, 0x2000 },
+  { 0x0700, 0x1751, 0x0000 },
+  { 0x0c00, 0x1753, 0x0000 },
+  { 0x8700, 0x1762, 0x2000 },
+  { 0x0700, 0x1761, 0x0000 },
+  { 0x0700, 0x1763, 0x0000 },
+  { 0x8700, 0x176c, 0x4000 },
+  { 0x8700, 0x1768, 0x3000 },
+  { 0x8700, 0x1766, 0x2000 },
+  { 0x0700, 0x1765, 0x0000 },
+  { 0x0700, 0x1767, 0x0000 },
+  { 0x8700, 0x176a, 0x2000 },
+  { 0x0700, 0x1769, 0x0000 },
+  { 0x0700, 0x176b, 0x0000 },
+  { 0x8c00, 0x1772, 0x3000 },
+  { 0x8700, 0x176f, 0x2000 },
+  { 0x0700, 0x176e, 0x0000 },
+  { 0x0700, 0x1770, 0x0000 },
+  { 0x8700, 0x1780, 0x2000 },
+  { 0x0c00, 0x1773, 0x0000 },
+  { 0x0700, 0x1781, 0x0000 },
+  { 0x8700, 0x1792, 0x5000 },
+  { 0x8700, 0x178a, 0x4000 },
+  { 0x8700, 0x1786, 0x3000 },
+  { 0x8700, 0x1784, 0x2000 },
+  { 0x0700, 0x1783, 0x0000 },
+  { 0x0700, 0x1785, 0x0000 },
+  { 0x8700, 0x1788, 0x2000 },
+  { 0x0700, 0x1787, 0x0000 },
+  { 0x0700, 0x1789, 0x0000 },
+  { 0x8700, 0x178e, 0x3000 },
+  { 0x8700, 0x178c, 0x2000 },
+  { 0x0700, 0x178b, 0x0000 },
+  { 0x0700, 0x178d, 0x0000 },
+  { 0x8700, 0x1790, 0x2000 },
+  { 0x0700, 0x178f, 0x0000 },
+  { 0x0700, 0x1791, 0x0000 },
+  { 0x8700, 0x179a, 0x4000 },
+  { 0x8700, 0x1796, 0x3000 },
+  { 0x8700, 0x1794, 0x2000 },
+  { 0x0700, 0x1793, 0x0000 },
+  { 0x0700, 0x1795, 0x0000 },
+  { 0x8700, 0x1798, 0x2000 },
+  { 0x0700, 0x1797, 0x0000 },
+  { 0x0700, 0x1799, 0x0000 },
+  { 0x8700, 0x179e, 0x3000 },
+  { 0x8700, 0x179c, 0x2000 },
+  { 0x0700, 0x179b, 0x0000 },
+  { 0x0700, 0x179d, 0x0000 },
+  { 0x8700, 0x17a0, 0x2000 },
+  { 0x0700, 0x179f, 0x0000 },
+  { 0x0700, 0x17a1, 0x0000 },
+  { 0x8700, 0x1915, 0x9000 },
+  { 0x8700, 0x1837, 0x8000 },
+  { 0x8d00, 0x17e4, 0x7000 },
+  { 0x8a00, 0x17c2, 0x6000 },
+  { 0x8700, 0x17b2, 0x5000 },
+  { 0x8700, 0x17aa, 0x4000 },
+  { 0x8700, 0x17a6, 0x3000 },
+  { 0x8700, 0x17a4, 0x2000 },
+  { 0x0700, 0x17a3, 0x0000 },
+  { 0x0700, 0x17a5, 0x0000 },
+  { 0x8700, 0x17a8, 0x2000 },
+  { 0x0700, 0x17a7, 0x0000 },
+  { 0x0700, 0x17a9, 0x0000 },
+  { 0x8700, 0x17ae, 0x3000 },
+  { 0x8700, 0x17ac, 0x2000 },
+  { 0x0700, 0x17ab, 0x0000 },
+  { 0x0700, 0x17ad, 0x0000 },
+  { 0x8700, 0x17b0, 0x2000 },
+  { 0x0700, 0x17af, 0x0000 },
+  { 0x0700, 0x17b1, 0x0000 },
+  { 0x8c00, 0x17ba, 0x4000 },
+  { 0x8a00, 0x17b6, 0x3000 },
+  { 0x8100, 0x17b4, 0x2000 },
+  { 0x0700, 0x17b3, 0x0000 },
+  { 0x0100, 0x17b5, 0x0000 },
+  { 0x8c00, 0x17b8, 0x2000 },
+  { 0x0c00, 0x17b7, 0x0000 },
+  { 0x0c00, 0x17b9, 0x0000 },
+  { 0x8a00, 0x17be, 0x3000 },
+  { 0x8c00, 0x17bc, 0x2000 },
+  { 0x0c00, 0x17bb, 0x0000 },
+  { 0x0c00, 0x17bd, 0x0000 },
+  { 0x8a00, 0x17c0, 0x2000 },
+  { 0x0a00, 0x17bf, 0x0000 },
+  { 0x0a00, 0x17c1, 0x0000 },
+  { 0x8c00, 0x17d2, 0x5000 },
+  { 0x8c00, 0x17ca, 0x4000 },
+  { 0x8c00, 0x17c6, 0x3000 },
+  { 0x8a00, 0x17c4, 0x2000 },
+  { 0x0a00, 0x17c3, 0x0000 },
+  { 0x0a00, 0x17c5, 0x0000 },
+  { 0x8a00, 0x17c8, 0x2000 },
+  { 0x0a00, 0x17c7, 0x0000 },
+  { 0x0c00, 0x17c9, 0x0000 },
+  { 0x8c00, 0x17ce, 0x3000 },
+  { 0x8c00, 0x17cc, 0x2000 },
+  { 0x0c00, 0x17cb, 0x0000 },
+  { 0x0c00, 0x17cd, 0x0000 },
+  { 0x8c00, 0x17d0, 0x2000 },
+  { 0x0c00, 0x17cf, 0x0000 },
+  { 0x0c00, 0x17d1, 0x0000 },
+  { 0x9500, 0x17da, 0x4000 },
+  { 0x9500, 0x17d6, 0x3000 },
+  { 0x9500, 0x17d4, 0x2000 },
+  { 0x0c00, 0x17d3, 0x0000 },
+  { 0x1500, 0x17d5, 0x0000 },
+  { 0x9500, 0x17d8, 0x2000 },
+  { 0x0600, 0x17d7, 0x0000 },
+  { 0x1500, 0x17d9, 0x0000 },
+  { 0x8d00, 0x17e0, 0x3000 },
+  { 0x8700, 0x17dc, 0x2000 },
+  { 0x1700, 0x17db, 0x0000 },
+  { 0x0c00, 0x17dd, 0x0000 },
+  { 0x8d00, 0x17e2, 0x2000 },
+  { 0x0d00, 0x17e1, 0x0000 },
+  { 0x0d00, 0x17e3, 0x0000 },
+  { 0x8d00, 0x1811, 0x6000 },
+  { 0x9500, 0x1800, 0x5000 },
+  { 0x8f00, 0x17f2, 0x4000 },
+  { 0x8d00, 0x17e8, 0x3000 },
+  { 0x8d00, 0x17e6, 0x2000 },
+  { 0x0d00, 0x17e5, 0x0000 },
+  { 0x0d00, 0x17e7, 0x0000 },
+  { 0x8f00, 0x17f0, 0x2000 },
+  { 0x0d00, 0x17e9, 0x0000 },
+  { 0x0f00, 0x17f1, 0x0000 },
+  { 0x8f00, 0x17f6, 0x3000 },
+  { 0x8f00, 0x17f4, 0x2000 },
+  { 0x0f00, 0x17f3, 0x0000 },
+  { 0x0f00, 0x17f5, 0x0000 },
+  { 0x8f00, 0x17f8, 0x2000 },
+  { 0x0f00, 0x17f7, 0x0000 },
+  { 0x0f00, 0x17f9, 0x0000 },
+  { 0x9500, 0x1808, 0x4000 },
+  { 0x9500, 0x1804, 0x3000 },
+  { 0x9500, 0x1802, 0x2000 },
+  { 0x1500, 0x1801, 0x0000 },
+  { 0x1500, 0x1803, 0x0000 },
+  { 0x9100, 0x1806, 0x2000 },
+  { 0x1500, 0x1805, 0x0000 },
+  { 0x1500, 0x1807, 0x0000 },
+  { 0x8c00, 0x180c, 0x3000 },
+  { 0x9500, 0x180a, 0x2000 },
+  { 0x1500, 0x1809, 0x0000 },
+  { 0x0c00, 0x180b, 0x0000 },
+  { 0x9d00, 0x180e, 0x2000 },
+  { 0x0c00, 0x180d, 0x0000 },
+  { 0x0d00, 0x1810, 0x0000 },
+  { 0x8700, 0x1827, 0x5000 },
+  { 0x8d00, 0x1819, 0x4000 },
+  { 0x8d00, 0x1815, 0x3000 },
+  { 0x8d00, 0x1813, 0x2000 },
+  { 0x0d00, 0x1812, 0x0000 },
+  { 0x0d00, 0x1814, 0x0000 },
+  { 0x8d00, 0x1817, 0x2000 },
+  { 0x0d00, 0x1816, 0x0000 },
+  { 0x0d00, 0x1818, 0x0000 },
+  { 0x8700, 0x1823, 0x3000 },
+  { 0x8700, 0x1821, 0x2000 },
+  { 0x0700, 0x1820, 0x0000 },
+  { 0x0700, 0x1822, 0x0000 },
+  { 0x8700, 0x1825, 0x2000 },
+  { 0x0700, 0x1824, 0x0000 },
+  { 0x0700, 0x1826, 0x0000 },
+  { 0x8700, 0x182f, 0x4000 },
+  { 0x8700, 0x182b, 0x3000 },
+  { 0x8700, 0x1829, 0x2000 },
+  { 0x0700, 0x1828, 0x0000 },
+  { 0x0700, 0x182a, 0x0000 },
+  { 0x8700, 0x182d, 0x2000 },
+  { 0x0700, 0x182c, 0x0000 },
+  { 0x0700, 0x182e, 0x0000 },
+  { 0x8700, 0x1833, 0x3000 },
+  { 0x8700, 0x1831, 0x2000 },
+  { 0x0700, 0x1830, 0x0000 },
+  { 0x0700, 0x1832, 0x0000 },
+  { 0x8700, 0x1835, 0x2000 },
+  { 0x0700, 0x1834, 0x0000 },
+  { 0x0700, 0x1836, 0x0000 },
+  { 0x8700, 0x1877, 0x7000 },
+  { 0x8700, 0x1857, 0x6000 },
+  { 0x8700, 0x1847, 0x5000 },
+  { 0x8700, 0x183f, 0x4000 },
+  { 0x8700, 0x183b, 0x3000 },
+  { 0x8700, 0x1839, 0x2000 },
+  { 0x0700, 0x1838, 0x0000 },
+  { 0x0700, 0x183a, 0x0000 },
+  { 0x8700, 0x183d, 0x2000 },
+  { 0x0700, 0x183c, 0x0000 },
+  { 0x0700, 0x183e, 0x0000 },
+  { 0x8600, 0x1843, 0x3000 },
+  { 0x8700, 0x1841, 0x2000 },
+  { 0x0700, 0x1840, 0x0000 },
+  { 0x0700, 0x1842, 0x0000 },
+  { 0x8700, 0x1845, 0x2000 },
+  { 0x0700, 0x1844, 0x0000 },
+  { 0x0700, 0x1846, 0x0000 },
+  { 0x8700, 0x184f, 0x4000 },
+  { 0x8700, 0x184b, 0x3000 },
+  { 0x8700, 0x1849, 0x2000 },
+  { 0x0700, 0x1848, 0x0000 },
+  { 0x0700, 0x184a, 0x0000 },
+  { 0x8700, 0x184d, 0x2000 },
+  { 0x0700, 0x184c, 0x0000 },
+  { 0x0700, 0x184e, 0x0000 },
+  { 0x8700, 0x1853, 0x3000 },
+  { 0x8700, 0x1851, 0x2000 },
+  { 0x0700, 0x1850, 0x0000 },
+  { 0x0700, 0x1852, 0x0000 },
+  { 0x8700, 0x1855, 0x2000 },
+  { 0x0700, 0x1854, 0x0000 },
+  { 0x0700, 0x1856, 0x0000 },
+  { 0x8700, 0x1867, 0x5000 },
+  { 0x8700, 0x185f, 0x4000 },
+  { 0x8700, 0x185b, 0x3000 },
+  { 0x8700, 0x1859, 0x2000 },
+  { 0x0700, 0x1858, 0x0000 },
+  { 0x0700, 0x185a, 0x0000 },
+  { 0x8700, 0x185d, 0x2000 },
+  { 0x0700, 0x185c, 0x0000 },
+  { 0x0700, 0x185e, 0x0000 },
+  { 0x8700, 0x1863, 0x3000 },
+  { 0x8700, 0x1861, 0x2000 },
+  { 0x0700, 0x1860, 0x0000 },
+  { 0x0700, 0x1862, 0x0000 },
+  { 0x8700, 0x1865, 0x2000 },
+  { 0x0700, 0x1864, 0x0000 },
+  { 0x0700, 0x1866, 0x0000 },
+  { 0x8700, 0x186f, 0x4000 },
+  { 0x8700, 0x186b, 0x3000 },
+  { 0x8700, 0x1869, 0x2000 },
+  { 0x0700, 0x1868, 0x0000 },
+  { 0x0700, 0x186a, 0x0000 },
+  { 0x8700, 0x186d, 0x2000 },
+  { 0x0700, 0x186c, 0x0000 },
+  { 0x0700, 0x186e, 0x0000 },
+  { 0x8700, 0x1873, 0x3000 },
+  { 0x8700, 0x1871, 0x2000 },
+  { 0x0700, 0x1870, 0x0000 },
+  { 0x0700, 0x1872, 0x0000 },
+  { 0x8700, 0x1875, 0x2000 },
+  { 0x0700, 0x1874, 0x0000 },
+  { 0x0700, 0x1876, 0x0000 },
+  { 0x8700, 0x189f, 0x6000 },
+  { 0x8700, 0x188f, 0x5000 },
+  { 0x8700, 0x1887, 0x4000 },
+  { 0x8700, 0x1883, 0x3000 },
+  { 0x8700, 0x1881, 0x2000 },
+  { 0x0700, 0x1880, 0x0000 },
+  { 0x0700, 0x1882, 0x0000 },
+  { 0x8700, 0x1885, 0x2000 },
+  { 0x0700, 0x1884, 0x0000 },
+  { 0x0700, 0x1886, 0x0000 },
+  { 0x8700, 0x188b, 0x3000 },
+  { 0x8700, 0x1889, 0x2000 },
+  { 0x0700, 0x1888, 0x0000 },
+  { 0x0700, 0x188a, 0x0000 },
+  { 0x8700, 0x188d, 0x2000 },
+  { 0x0700, 0x188c, 0x0000 },
+  { 0x0700, 0x188e, 0x0000 },
+  { 0x8700, 0x1897, 0x4000 },
+  { 0x8700, 0x1893, 0x3000 },
+  { 0x8700, 0x1891, 0x2000 },
+  { 0x0700, 0x1890, 0x0000 },
+  { 0x0700, 0x1892, 0x0000 },
+  { 0x8700, 0x1895, 0x2000 },
+  { 0x0700, 0x1894, 0x0000 },
+  { 0x0700, 0x1896, 0x0000 },
+  { 0x8700, 0x189b, 0x3000 },
+  { 0x8700, 0x1899, 0x2000 },
+  { 0x0700, 0x1898, 0x0000 },
+  { 0x0700, 0x189a, 0x0000 },
+  { 0x8700, 0x189d, 0x2000 },
+  { 0x0700, 0x189c, 0x0000 },
+  { 0x0700, 0x189e, 0x0000 },
+  { 0x8700, 0x1905, 0x5000 },
+  { 0x8700, 0x18a7, 0x4000 },
+  { 0x8700, 0x18a3, 0x3000 },
+  { 0x8700, 0x18a1, 0x2000 },
+  { 0x0700, 0x18a0, 0x0000 },
+  { 0x0700, 0x18a2, 0x0000 },
+  { 0x8700, 0x18a5, 0x2000 },
+  { 0x0700, 0x18a4, 0x0000 },
+  { 0x0700, 0x18a6, 0x0000 },
+  { 0x8700, 0x1901, 0x3000 },
+  { 0x8c00, 0x18a9, 0x2000 },
+  { 0x0700, 0x18a8, 0x0000 },
+  { 0x0700, 0x1900, 0x0000 },
+  { 0x8700, 0x1903, 0x2000 },
+  { 0x0700, 0x1902, 0x0000 },
+  { 0x0700, 0x1904, 0x0000 },
+  { 0x8700, 0x190d, 0x4000 },
+  { 0x8700, 0x1909, 0x3000 },
+  { 0x8700, 0x1907, 0x2000 },
+  { 0x0700, 0x1906, 0x0000 },
+  { 0x0700, 0x1908, 0x0000 },
+  { 0x8700, 0x190b, 0x2000 },
+  { 0x0700, 0x190a, 0x0000 },
+  { 0x0700, 0x190c, 0x0000 },
+  { 0x8700, 0x1911, 0x3000 },
+  { 0x8700, 0x190f, 0x2000 },
+  { 0x0700, 0x190e, 0x0000 },
+  { 0x0700, 0x1910, 0x0000 },
+  { 0x8700, 0x1913, 0x2000 },
+  { 0x0700, 0x1912, 0x0000 },
+  { 0x0700, 0x1914, 0x0000 },
+  { 0x8500, 0x1d10, 0x8000 },
+  { 0x8700, 0x1963, 0x7000 },
+  { 0x9a00, 0x1940, 0x6000 },
+  { 0x8c00, 0x1928, 0x5000 },
+  { 0x8c00, 0x1920, 0x4000 },
+  { 0x8700, 0x1919, 0x3000 },
+  { 0x8700, 0x1917, 0x2000 },
+  { 0x0700, 0x1916, 0x0000 },
+  { 0x0700, 0x1918, 0x0000 },
+  { 0x8700, 0x191b, 0x2000 },
+  { 0x0700, 0x191a, 0x0000 },
+  { 0x0700, 0x191c, 0x0000 },
+  { 0x8a00, 0x1924, 0x3000 },
+  { 0x8c00, 0x1922, 0x2000 },
+  { 0x0c00, 0x1921, 0x0000 },
+  { 0x0a00, 0x1923, 0x0000 },
+  { 0x8a00, 0x1926, 0x2000 },
+  { 0x0a00, 0x1925, 0x0000 },
+  { 0x0c00, 0x1927, 0x0000 },
+  { 0x8a00, 0x1934, 0x4000 },
+  { 0x8a00, 0x1930, 0x3000 },
+  { 0x8a00, 0x192a, 0x2000 },
+  { 0x0a00, 0x1929, 0x0000 },
+  { 0x0a00, 0x192b, 0x0000 },
+  { 0x8c00, 0x1932, 0x2000 },
+  { 0x0a00, 0x1931, 0x0000 },
+  { 0x0a00, 0x1933, 0x0000 },
+  { 0x8a00, 0x1938, 0x3000 },
+  { 0x8a00, 0x1936, 0x2000 },
+  { 0x0a00, 0x1935, 0x0000 },
+  { 0x0a00, 0x1937, 0x0000 },
+  { 0x8c00, 0x193a, 0x2000 },
+  { 0x0c00, 0x1939, 0x0000 },
+  { 0x0c00, 0x193b, 0x0000 },
+  { 0x8700, 0x1953, 0x5000 },
+  { 0x8d00, 0x194b, 0x4000 },
+  { 0x8d00, 0x1947, 0x3000 },
+  { 0x9500, 0x1945, 0x2000 },
+  { 0x1500, 0x1944, 0x0000 },
+  { 0x0d00, 0x1946, 0x0000 },
+  { 0x8d00, 0x1949, 0x2000 },
+  { 0x0d00, 0x1948, 0x0000 },
+  { 0x0d00, 0x194a, 0x0000 },
+  { 0x8d00, 0x194f, 0x3000 },
+  { 0x8d00, 0x194d, 0x2000 },
+  { 0x0d00, 0x194c, 0x0000 },
+  { 0x0d00, 0x194e, 0x0000 },
+  { 0x8700, 0x1951, 0x2000 },
+  { 0x0700, 0x1950, 0x0000 },
+  { 0x0700, 0x1952, 0x0000 },
+  { 0x8700, 0x195b, 0x4000 },
+  { 0x8700, 0x1957, 0x3000 },
+  { 0x8700, 0x1955, 0x2000 },
+  { 0x0700, 0x1954, 0x0000 },
+  { 0x0700, 0x1956, 0x0000 },
+  { 0x8700, 0x1959, 0x2000 },
+  { 0x0700, 0x1958, 0x0000 },
+  { 0x0700, 0x195a, 0x0000 },
+  { 0x8700, 0x195f, 0x3000 },
+  { 0x8700, 0x195d, 0x2000 },
+  { 0x0700, 0x195c, 0x0000 },
+  { 0x0700, 0x195e, 0x0000 },
+  { 0x8700, 0x1961, 0x2000 },
+  { 0x0700, 0x1960, 0x0000 },
+  { 0x0700, 0x1962, 0x0000 },
+  { 0x9a00, 0x19f0, 0x6000 },
+  { 0x9a00, 0x19e0, 0x5000 },
+  { 0x8700, 0x196b, 0x4000 },
+  { 0x8700, 0x1967, 0x3000 },
+  { 0x8700, 0x1965, 0x2000 },
+  { 0x0700, 0x1964, 0x0000 },
+  { 0x0700, 0x1966, 0x0000 },
+  { 0x8700, 0x1969, 0x2000 },
+  { 0x0700, 0x1968, 0x0000 },
+  { 0x0700, 0x196a, 0x0000 },
+  { 0x8700, 0x1971, 0x3000 },
+  { 0x8700, 0x196d, 0x2000 },
+  { 0x0700, 0x196c, 0x0000 },
+  { 0x0700, 0x1970, 0x0000 },
+  { 0x8700, 0x1973, 0x2000 },
+  { 0x0700, 0x1972, 0x0000 },
+  { 0x0700, 0x1974, 0x0000 },
+  { 0x9a00, 0x19e8, 0x4000 },
+  { 0x9a00, 0x19e4, 0x3000 },
+  { 0x9a00, 0x19e2, 0x2000 },
+  { 0x1a00, 0x19e1, 0x0000 },
+  { 0x1a00, 0x19e3, 0x0000 },
+  { 0x9a00, 0x19e6, 0x2000 },
+  { 0x1a00, 0x19e5, 0x0000 },
+  { 0x1a00, 0x19e7, 0x0000 },
+  { 0x9a00, 0x19ec, 0x3000 },
+  { 0x9a00, 0x19ea, 0x2000 },
+  { 0x1a00, 0x19e9, 0x0000 },
+  { 0x1a00, 0x19eb, 0x0000 },
+  { 0x9a00, 0x19ee, 0x2000 },
+  { 0x1a00, 0x19ed, 0x0000 },
+  { 0x1a00, 0x19ef, 0x0000 },
+  { 0x8500, 0x1d00, 0x5000 },
+  { 0x9a00, 0x19f8, 0x4000 },
+  { 0x9a00, 0x19f4, 0x3000 },
+  { 0x9a00, 0x19f2, 0x2000 },
+  { 0x1a00, 0x19f1, 0x0000 },
+  { 0x1a00, 0x19f3, 0x0000 },
+  { 0x9a00, 0x19f6, 0x2000 },
+  { 0x1a00, 0x19f5, 0x0000 },
+  { 0x1a00, 0x19f7, 0x0000 },
+  { 0x9a00, 0x19fc, 0x3000 },
+  { 0x9a00, 0x19fa, 0x2000 },
+  { 0x1a00, 0x19f9, 0x0000 },
+  { 0x1a00, 0x19fb, 0x0000 },
+  { 0x9a00, 0x19fe, 0x2000 },
+  { 0x1a00, 0x19fd, 0x0000 },
+  { 0x1a00, 0x19ff, 0x0000 },
+  { 0x8500, 0x1d08, 0x4000 },
+  { 0x8500, 0x1d04, 0x3000 },
+  { 0x8500, 0x1d02, 0x2000 },
+  { 0x0500, 0x1d01, 0x0000 },
+  { 0x0500, 0x1d03, 0x0000 },
+  { 0x8500, 0x1d06, 0x2000 },
+  { 0x0500, 0x1d05, 0x0000 },
+  { 0x0500, 0x1d07, 0x0000 },
+  { 0x8500, 0x1d0c, 0x3000 },
+  { 0x8500, 0x1d0a, 0x2000 },
+  { 0x0500, 0x1d09, 0x0000 },
+  { 0x0500, 0x1d0b, 0x0000 },
+  { 0x8500, 0x1d0e, 0x2000 },
+  { 0x0500, 0x1d0d, 0x0000 },
+  { 0x0500, 0x1d0f, 0x0000 },
+  { 0x8600, 0x1d50, 0x7000 },
+  { 0x8600, 0x1d30, 0x6000 },
+  { 0x8500, 0x1d20, 0x5000 },
+  { 0x8500, 0x1d18, 0x4000 },
+  { 0x8500, 0x1d14, 0x3000 },
+  { 0x8500, 0x1d12, 0x2000 },
+  { 0x0500, 0x1d11, 0x0000 },
+  { 0x0500, 0x1d13, 0x0000 },
+  { 0x8500, 0x1d16, 0x2000 },
+  { 0x0500, 0x1d15, 0x0000 },
+  { 0x0500, 0x1d17, 0x0000 },
+  { 0x8500, 0x1d1c, 0x3000 },
+  { 0x8500, 0x1d1a, 0x2000 },
+  { 0x0500, 0x1d19, 0x0000 },
+  { 0x0500, 0x1d1b, 0x0000 },
+  { 0x8500, 0x1d1e, 0x2000 },
+  { 0x0500, 0x1d1d, 0x0000 },
+  { 0x0500, 0x1d1f, 0x0000 },
+  { 0x8500, 0x1d28, 0x4000 },
+  { 0x8500, 0x1d24, 0x3000 },
+  { 0x8500, 0x1d22, 0x2000 },
+  { 0x0500, 0x1d21, 0x0000 },
+  { 0x0500, 0x1d23, 0x0000 },
+  { 0x8500, 0x1d26, 0x2000 },
+  { 0x0500, 0x1d25, 0x0000 },
+  { 0x0500, 0x1d27, 0x0000 },
+  { 0x8600, 0x1d2c, 0x3000 },
+  { 0x8500, 0x1d2a, 0x2000 },
+  { 0x0500, 0x1d29, 0x0000 },
+  { 0x0500, 0x1d2b, 0x0000 },
+  { 0x8600, 0x1d2e, 0x2000 },
+  { 0x0600, 0x1d2d, 0x0000 },
+  { 0x0600, 0x1d2f, 0x0000 },
+  { 0x8600, 0x1d40, 0x5000 },
+  { 0x8600, 0x1d38, 0x4000 },
+  { 0x8600, 0x1d34, 0x3000 },
+  { 0x8600, 0x1d32, 0x2000 },
+  { 0x0600, 0x1d31, 0x0000 },
+  { 0x0600, 0x1d33, 0x0000 },
+  { 0x8600, 0x1d36, 0x2000 },
+  { 0x0600, 0x1d35, 0x0000 },
+  { 0x0600, 0x1d37, 0x0000 },
+  { 0x8600, 0x1d3c, 0x3000 },
+  { 0x8600, 0x1d3a, 0x2000 },
+  { 0x0600, 0x1d39, 0x0000 },
+  { 0x0600, 0x1d3b, 0x0000 },
+  { 0x8600, 0x1d3e, 0x2000 },
+  { 0x0600, 0x1d3d, 0x0000 },
+  { 0x0600, 0x1d3f, 0x0000 },
+  { 0x8600, 0x1d48, 0x4000 },
+  { 0x8600, 0x1d44, 0x3000 },
+  { 0x8600, 0x1d42, 0x2000 },
+  { 0x0600, 0x1d41, 0x0000 },
+  { 0x0600, 0x1d43, 0x0000 },
+  { 0x8600, 0x1d46, 0x2000 },
+  { 0x0600, 0x1d45, 0x0000 },
+  { 0x0600, 0x1d47, 0x0000 },
+  { 0x8600, 0x1d4c, 0x3000 },
+  { 0x8600, 0x1d4a, 0x2000 },
+  { 0x0600, 0x1d49, 0x0000 },
+  { 0x0600, 0x1d4b, 0x0000 },
+  { 0x8600, 0x1d4e, 0x2000 },
+  { 0x0600, 0x1d4d, 0x0000 },
+  { 0x0600, 0x1d4f, 0x0000 },
+  { 0x8900, 0x1e04, 0x6001 },
+  { 0x8600, 0x1d60, 0x5000 },
+  { 0x8600, 0x1d58, 0x4000 },
+  { 0x8600, 0x1d54, 0x3000 },
+  { 0x8600, 0x1d52, 0x2000 },
+  { 0x0600, 0x1d51, 0x0000 },
+  { 0x0600, 0x1d53, 0x0000 },
+  { 0x8600, 0x1d56, 0x2000 },
+  { 0x0600, 0x1d55, 0x0000 },
+  { 0x0600, 0x1d57, 0x0000 },
+  { 0x8600, 0x1d5c, 0x3000 },
+  { 0x8600, 0x1d5a, 0x2000 },
+  { 0x0600, 0x1d59, 0x0000 },
+  { 0x0600, 0x1d5b, 0x0000 },
+  { 0x8600, 0x1d5e, 0x2000 },
+  { 0x0600, 0x1d5d, 0x0000 },
+  { 0x0600, 0x1d5f, 0x0000 },
+  { 0x8500, 0x1d68, 0x4000 },
+  { 0x8500, 0x1d64, 0x3000 },
+  { 0x8500, 0x1d62, 0x2000 },
+  { 0x0600, 0x1d61, 0x0000 },
+  { 0x0500, 0x1d63, 0x0000 },
+  { 0x8500, 0x1d66, 0x2000 },
+  { 0x0500, 0x1d65, 0x0000 },
+  { 0x0500, 0x1d67, 0x0000 },
+  { 0x8900, 0x1e00, 0x3001 },
+  { 0x8500, 0x1d6a, 0x2000 },
+  { 0x0500, 0x1d69, 0x0000 },
+  { 0x0500, 0x1d6b, 0x0000 },
+  { 0x8900, 0x1e02, 0x2001 },
+  { 0x0500, 0x1e01, 0x0fff },
+  { 0x0500, 0x1e03, 0x0fff },
+  { 0x8900, 0x1e14, 0x5001 },
+  { 0x8900, 0x1e0c, 0x4001 },
+  { 0x8900, 0x1e08, 0x3001 },
+  { 0x8900, 0x1e06, 0x2001 },
+  { 0x0500, 0x1e05, 0x0fff },
+  { 0x0500, 0x1e07, 0x0fff },
+  { 0x8900, 0x1e0a, 0x2001 },
+  { 0x0500, 0x1e09, 0x0fff },
+  { 0x0500, 0x1e0b, 0x0fff },
+  { 0x8900, 0x1e10, 0x3001 },
+  { 0x8900, 0x1e0e, 0x2001 },
+  { 0x0500, 0x1e0d, 0x0fff },
+  { 0x0500, 0x1e0f, 0x0fff },
+  { 0x8900, 0x1e12, 0x2001 },
+  { 0x0500, 0x1e11, 0x0fff },
+  { 0x0500, 0x1e13, 0x0fff },
+  { 0x8900, 0x1e1c, 0x4001 },
+  { 0x8900, 0x1e18, 0x3001 },
+  { 0x8900, 0x1e16, 0x2001 },
+  { 0x0500, 0x1e15, 0x0fff },
+  { 0x0500, 0x1e17, 0x0fff },
+  { 0x8900, 0x1e1a, 0x2001 },
+  { 0x0500, 0x1e19, 0x0fff },
+  { 0x0500, 0x1e1b, 0x0fff },
+  { 0x8900, 0x1e20, 0x3001 },
+  { 0x8900, 0x1e1e, 0x2001 },
+  { 0x0500, 0x1e1d, 0x0fff },
+  { 0x0500, 0x1e1f, 0x0fff },
+  { 0x8900, 0x1e22, 0x2001 },
+  { 0x0500, 0x1e21, 0x0fff },
+  { 0x0500, 0x1e23, 0x0fff },
+  { 0x9600, 0x2045, 0xa000 },
+  { 0x8500, 0x1f32, 0x9008 },
+  { 0x8900, 0x1ea8, 0x8001 },
+  { 0x8900, 0x1e64, 0x7001 },
+  { 0x8900, 0x1e44, 0x6001 },
+  { 0x8900, 0x1e34, 0x5001 },
+  { 0x8900, 0x1e2c, 0x4001 },
+  { 0x8900, 0x1e28, 0x3001 },
+  { 0x8900, 0x1e26, 0x2001 },
+  { 0x0500, 0x1e25, 0x0fff },
+  { 0x0500, 0x1e27, 0x0fff },
+  { 0x8900, 0x1e2a, 0x2001 },
+  { 0x0500, 0x1e29, 0x0fff },
+  { 0x0500, 0x1e2b, 0x0fff },
+  { 0x8900, 0x1e30, 0x3001 },
+  { 0x8900, 0x1e2e, 0x2001 },
+  { 0x0500, 0x1e2d, 0x0fff },
+  { 0x0500, 0x1e2f, 0x0fff },
+  { 0x8900, 0x1e32, 0x2001 },
+  { 0x0500, 0x1e31, 0x0fff },
+  { 0x0500, 0x1e33, 0x0fff },
+  { 0x8900, 0x1e3c, 0x4001 },
+  { 0x8900, 0x1e38, 0x3001 },
+  { 0x8900, 0x1e36, 0x2001 },
+  { 0x0500, 0x1e35, 0x0fff },
+  { 0x0500, 0x1e37, 0x0fff },
+  { 0x8900, 0x1e3a, 0x2001 },
+  { 0x0500, 0x1e39, 0x0fff },
+  { 0x0500, 0x1e3b, 0x0fff },
+  { 0x8900, 0x1e40, 0x3001 },
+  { 0x8900, 0x1e3e, 0x2001 },
+  { 0x0500, 0x1e3d, 0x0fff },
+  { 0x0500, 0x1e3f, 0x0fff },
+  { 0x8900, 0x1e42, 0x2001 },
+  { 0x0500, 0x1e41, 0x0fff },
+  { 0x0500, 0x1e43, 0x0fff },
+  { 0x8900, 0x1e54, 0x5001 },
+  { 0x8900, 0x1e4c, 0x4001 },
+  { 0x8900, 0x1e48, 0x3001 },
+  { 0x8900, 0x1e46, 0x2001 },
+  { 0x0500, 0x1e45, 0x0fff },
+  { 0x0500, 0x1e47, 0x0fff },
+  { 0x8900, 0x1e4a, 0x2001 },
+  { 0x0500, 0x1e49, 0x0fff },
+  { 0x0500, 0x1e4b, 0x0fff },
+  { 0x8900, 0x1e50, 0x3001 },
+  { 0x8900, 0x1e4e, 0x2001 },
+  { 0x0500, 0x1e4d, 0x0fff },
+  { 0x0500, 0x1e4f, 0x0fff },
+  { 0x8900, 0x1e52, 0x2001 },
+  { 0x0500, 0x1e51, 0x0fff },
+  { 0x0500, 0x1e53, 0x0fff },
+  { 0x8900, 0x1e5c, 0x4001 },
+  { 0x8900, 0x1e58, 0x3001 },
+  { 0x8900, 0x1e56, 0x2001 },
+  { 0x0500, 0x1e55, 0x0fff },
+  { 0x0500, 0x1e57, 0x0fff },
+  { 0x8900, 0x1e5a, 0x2001 },
+  { 0x0500, 0x1e59, 0x0fff },
+  { 0x0500, 0x1e5b, 0x0fff },
+  { 0x8900, 0x1e60, 0x3001 },
+  { 0x8900, 0x1e5e, 0x2001 },
+  { 0x0500, 0x1e5d, 0x0fff },
+  { 0x0500, 0x1e5f, 0x0fff },
+  { 0x8900, 0x1e62, 0x2001 },
+  { 0x0500, 0x1e61, 0x0fff },
+  { 0x0500, 0x1e63, 0x0fff },
+  { 0x8900, 0x1e84, 0x6001 },
+  { 0x8900, 0x1e74, 0x5001 },
+  { 0x8900, 0x1e6c, 0x4001 },
+  { 0x8900, 0x1e68, 0x3001 },
+  { 0x8900, 0x1e66, 0x2001 },
+  { 0x0500, 0x1e65, 0x0fff },
+  { 0x0500, 0x1e67, 0x0fff },
+  { 0x8900, 0x1e6a, 0x2001 },
+  { 0x0500, 0x1e69, 0x0fff },
+  { 0x0500, 0x1e6b, 0x0fff },
+  { 0x8900, 0x1e70, 0x3001 },
+  { 0x8900, 0x1e6e, 0x2001 },
+  { 0x0500, 0x1e6d, 0x0fff },
+  { 0x0500, 0x1e6f, 0x0fff },
+  { 0x8900, 0x1e72, 0x2001 },
+  { 0x0500, 0x1e71, 0x0fff },
+  { 0x0500, 0x1e73, 0x0fff },
+  { 0x8900, 0x1e7c, 0x4001 },
+  { 0x8900, 0x1e78, 0x3001 },
+  { 0x8900, 0x1e76, 0x2001 },
+  { 0x0500, 0x1e75, 0x0fff },
+  { 0x0500, 0x1e77, 0x0fff },
+  { 0x8900, 0x1e7a, 0x2001 },
+  { 0x0500, 0x1e79, 0x0fff },
+  { 0x0500, 0x1e7b, 0x0fff },
+  { 0x8900, 0x1e80, 0x3001 },
+  { 0x8900, 0x1e7e, 0x2001 },
+  { 0x0500, 0x1e7d, 0x0fff },
+  { 0x0500, 0x1e7f, 0x0fff },
+  { 0x8900, 0x1e82, 0x2001 },
+  { 0x0500, 0x1e81, 0x0fff },
+  { 0x0500, 0x1e83, 0x0fff },
+  { 0x8900, 0x1e94, 0x5001 },
+  { 0x8900, 0x1e8c, 0x4001 },
+  { 0x8900, 0x1e88, 0x3001 },
+  { 0x8900, 0x1e86, 0x2001 },
+  { 0x0500, 0x1e85, 0x0fff },
+  { 0x0500, 0x1e87, 0x0fff },
+  { 0x8900, 0x1e8a, 0x2001 },
+  { 0x0500, 0x1e89, 0x0fff },
+  { 0x0500, 0x1e8b, 0x0fff },
+  { 0x8900, 0x1e90, 0x3001 },
+  { 0x8900, 0x1e8e, 0x2001 },
+  { 0x0500, 0x1e8d, 0x0fff },
+  { 0x0500, 0x1e8f, 0x0fff },
+  { 0x8900, 0x1e92, 0x2001 },
+  { 0x0500, 0x1e91, 0x0fff },
+  { 0x0500, 0x1e93, 0x0fff },
+  { 0x8900, 0x1ea0, 0x4001 },
+  { 0x8500, 0x1e98, 0x3000 },
+  { 0x8500, 0x1e96, 0x2000 },
+  { 0x0500, 0x1e95, 0x0fff },
+  { 0x0500, 0x1e97, 0x0000 },
+  { 0x8500, 0x1e9a, 0x2000 },
+  { 0x0500, 0x1e99, 0x0000 },
+  { 0x0500, 0x1e9b, 0x0fc5 },
+  { 0x8900, 0x1ea4, 0x3001 },
+  { 0x8900, 0x1ea2, 0x2001 },
+  { 0x0500, 0x1ea1, 0x0fff },
+  { 0x0500, 0x1ea3, 0x0fff },
+  { 0x8900, 0x1ea6, 0x2001 },
+  { 0x0500, 0x1ea5, 0x0fff },
+  { 0x0500, 0x1ea7, 0x0fff },
+  { 0x8900, 0x1ee8, 0x7001 },
+  { 0x8900, 0x1ec8, 0x6001 },
+  { 0x8900, 0x1eb8, 0x5001 },
+  { 0x8900, 0x1eb0, 0x4001 },
+  { 0x8900, 0x1eac, 0x3001 },
+  { 0x8900, 0x1eaa, 0x2001 },
+  { 0x0500, 0x1ea9, 0x0fff },
+  { 0x0500, 0x1eab, 0x0fff },
+  { 0x8900, 0x1eae, 0x2001 },
+  { 0x0500, 0x1ead, 0x0fff },
+  { 0x0500, 0x1eaf, 0x0fff },
+  { 0x8900, 0x1eb4, 0x3001 },
+  { 0x8900, 0x1eb2, 0x2001 },
+  { 0x0500, 0x1eb1, 0x0fff },
+  { 0x0500, 0x1eb3, 0x0fff },
+  { 0x8900, 0x1eb6, 0x2001 },
+  { 0x0500, 0x1eb5, 0x0fff },
+  { 0x0500, 0x1eb7, 0x0fff },
+  { 0x8900, 0x1ec0, 0x4001 },
+  { 0x8900, 0x1ebc, 0x3001 },
+  { 0x8900, 0x1eba, 0x2001 },
+  { 0x0500, 0x1eb9, 0x0fff },
+  { 0x0500, 0x1ebb, 0x0fff },
+  { 0x8900, 0x1ebe, 0x2001 },
+  { 0x0500, 0x1ebd, 0x0fff },
+  { 0x0500, 0x1ebf, 0x0fff },
+  { 0x8900, 0x1ec4, 0x3001 },
+  { 0x8900, 0x1ec2, 0x2001 },
+  { 0x0500, 0x1ec1, 0x0fff },
+  { 0x0500, 0x1ec3, 0x0fff },
+  { 0x8900, 0x1ec6, 0x2001 },
+  { 0x0500, 0x1ec5, 0x0fff },
+  { 0x0500, 0x1ec7, 0x0fff },
+  { 0x8900, 0x1ed8, 0x5001 },
+  { 0x8900, 0x1ed0, 0x4001 },
+  { 0x8900, 0x1ecc, 0x3001 },
+  { 0x8900, 0x1eca, 0x2001 },
+  { 0x0500, 0x1ec9, 0x0fff },
+  { 0x0500, 0x1ecb, 0x0fff },
+  { 0x8900, 0x1ece, 0x2001 },
+  { 0x0500, 0x1ecd, 0x0fff },
+  { 0x0500, 0x1ecf, 0x0fff },
+  { 0x8900, 0x1ed4, 0x3001 },
+  { 0x8900, 0x1ed2, 0x2001 },
+  { 0x0500, 0x1ed1, 0x0fff },
+  { 0x0500, 0x1ed3, 0x0fff },
+  { 0x8900, 0x1ed6, 0x2001 },
+  { 0x0500, 0x1ed5, 0x0fff },
+  { 0x0500, 0x1ed7, 0x0fff },
+  { 0x8900, 0x1ee0, 0x4001 },
+  { 0x8900, 0x1edc, 0x3001 },
+  { 0x8900, 0x1eda, 0x2001 },
+  { 0x0500, 0x1ed9, 0x0fff },
+  { 0x0500, 0x1edb, 0x0fff },
+  { 0x8900, 0x1ede, 0x2001 },
+  { 0x0500, 0x1edd, 0x0fff },
+  { 0x0500, 0x1edf, 0x0fff },
+  { 0x8900, 0x1ee4, 0x3001 },
+  { 0x8900, 0x1ee2, 0x2001 },
+  { 0x0500, 0x1ee1, 0x0fff },
+  { 0x0500, 0x1ee3, 0x0fff },
+  { 0x8900, 0x1ee6, 0x2001 },
+  { 0x0500, 0x1ee5, 0x0fff },
+  { 0x0500, 0x1ee7, 0x0fff },
+  { 0x8900, 0x1f0e, 0x6ff8 },
+  { 0x8900, 0x1ef8, 0x5001 },
+  { 0x8900, 0x1ef0, 0x4001 },
+  { 0x8900, 0x1eec, 0x3001 },
+  { 0x8900, 0x1eea, 0x2001 },
+  { 0x0500, 0x1ee9, 0x0fff },
+  { 0x0500, 0x1eeb, 0x0fff },
+  { 0x8900, 0x1eee, 0x2001 },
+  { 0x0500, 0x1eed, 0x0fff },
+  { 0x0500, 0x1eef, 0x0fff },
+  { 0x8900, 0x1ef4, 0x3001 },
+  { 0x8900, 0x1ef2, 0x2001 },
+  { 0x0500, 0x1ef1, 0x0fff },
+  { 0x0500, 0x1ef3, 0x0fff },
+  { 0x8900, 0x1ef6, 0x2001 },
+  { 0x0500, 0x1ef5, 0x0fff },
+  { 0x0500, 0x1ef7, 0x0fff },
+  { 0x8500, 0x1f06, 0x4008 },
+  { 0x8500, 0x1f02, 0x3008 },
+  { 0x8500, 0x1f00, 0x2008 },
+  { 0x0500, 0x1ef9, 0x0fff },
+  { 0x0500, 0x1f01, 0x0008 },
+  { 0x8500, 0x1f04, 0x2008 },
+  { 0x0500, 0x1f03, 0x0008 },
+  { 0x0500, 0x1f05, 0x0008 },
+  { 0x8900, 0x1f0a, 0x3ff8 },
+  { 0x8900, 0x1f08, 0x2ff8 },
+  { 0x0500, 0x1f07, 0x0008 },
+  { 0x0900, 0x1f09, 0x0ff8 },
+  { 0x8900, 0x1f0c, 0x2ff8 },
+  { 0x0900, 0x1f0b, 0x0ff8 },
+  { 0x0900, 0x1f0d, 0x0ff8 },
+  { 0x8500, 0x1f22, 0x5008 },
+  { 0x8900, 0x1f18, 0x4ff8 },
+  { 0x8500, 0x1f12, 0x3008 },
+  { 0x8500, 0x1f10, 0x2008 },
+  { 0x0900, 0x1f0f, 0x0ff8 },
+  { 0x0500, 0x1f11, 0x0008 },
+  { 0x8500, 0x1f14, 0x2008 },
+  { 0x0500, 0x1f13, 0x0008 },
+  { 0x0500, 0x1f15, 0x0008 },
+  { 0x8900, 0x1f1c, 0x3ff8 },
+  { 0x8900, 0x1f1a, 0x2ff8 },
+  { 0x0900, 0x1f19, 0x0ff8 },
+  { 0x0900, 0x1f1b, 0x0ff8 },
+  { 0x8500, 0x1f20, 0x2008 },
+  { 0x0900, 0x1f1d, 0x0ff8 },
+  { 0x0500, 0x1f21, 0x0008 },
+  { 0x8900, 0x1f2a, 0x4ff8 },
+  { 0x8500, 0x1f26, 0x3008 },
+  { 0x8500, 0x1f24, 0x2008 },
+  { 0x0500, 0x1f23, 0x0008 },
+  { 0x0500, 0x1f25, 0x0008 },
+  { 0x8900, 0x1f28, 0x2ff8 },
+  { 0x0500, 0x1f27, 0x0008 },
+  { 0x0900, 0x1f29, 0x0ff8 },
+  { 0x8900, 0x1f2e, 0x3ff8 },
+  { 0x8900, 0x1f2c, 0x2ff8 },
+  { 0x0900, 0x1f2b, 0x0ff8 },
+  { 0x0900, 0x1f2d, 0x0ff8 },
+  { 0x8500, 0x1f30, 0x2008 },
+  { 0x0900, 0x1f2f, 0x0ff8 },
+  { 0x0500, 0x1f31, 0x0008 },
+  { 0x9800, 0x1fbd, 0x8000 },
+  { 0x8500, 0x1f7a, 0x7070 },
+  { 0x8500, 0x1f56, 0x6000 },
+  { 0x8500, 0x1f42, 0x5008 },
+  { 0x8900, 0x1f3a, 0x4ff8 },
+  { 0x8500, 0x1f36, 0x3008 },
+  { 0x8500, 0x1f34, 0x2008 },
+  { 0x0500, 0x1f33, 0x0008 },
+  { 0x0500, 0x1f35, 0x0008 },
+  { 0x8900, 0x1f38, 0x2ff8 },
+  { 0x0500, 0x1f37, 0x0008 },
+  { 0x0900, 0x1f39, 0x0ff8 },
+  { 0x8900, 0x1f3e, 0x3ff8 },
+  { 0x8900, 0x1f3c, 0x2ff8 },
+  { 0x0900, 0x1f3b, 0x0ff8 },
+  { 0x0900, 0x1f3d, 0x0ff8 },
+  { 0x8500, 0x1f40, 0x2008 },
+  { 0x0900, 0x1f3f, 0x0ff8 },
+  { 0x0500, 0x1f41, 0x0008 },
+  { 0x8900, 0x1f4c, 0x4ff8 },
+  { 0x8900, 0x1f48, 0x3ff8 },
+  { 0x8500, 0x1f44, 0x2008 },
+  { 0x0500, 0x1f43, 0x0008 },
+  { 0x0500, 0x1f45, 0x0008 },
+  { 0x8900, 0x1f4a, 0x2ff8 },
+  { 0x0900, 0x1f49, 0x0ff8 },
+  { 0x0900, 0x1f4b, 0x0ff8 },
+  { 0x8500, 0x1f52, 0x3000 },
+  { 0x8500, 0x1f50, 0x2000 },
+  { 0x0900, 0x1f4d, 0x0ff8 },
+  { 0x0500, 0x1f51, 0x0008 },
+  { 0x8500, 0x1f54, 0x2000 },
+  { 0x0500, 0x1f53, 0x0008 },
+  { 0x0500, 0x1f55, 0x0008 },
+  { 0x8900, 0x1f6a, 0x5ff8 },
+  { 0x8500, 0x1f62, 0x4008 },
+  { 0x8900, 0x1f5d, 0x3ff8 },
+  { 0x8900, 0x1f59, 0x2ff8 },
+  { 0x0500, 0x1f57, 0x0008 },
+  { 0x0900, 0x1f5b, 0x0ff8 },
+  { 0x8500, 0x1f60, 0x2008 },
+  { 0x0900, 0x1f5f, 0x0ff8 },
+  { 0x0500, 0x1f61, 0x0008 },
+  { 0x8500, 0x1f66, 0x3008 },
+  { 0x8500, 0x1f64, 0x2008 },
+  { 0x0500, 0x1f63, 0x0008 },
+  { 0x0500, 0x1f65, 0x0008 },
+  { 0x8900, 0x1f68, 0x2ff8 },
+  { 0x0500, 0x1f67, 0x0008 },
+  { 0x0900, 0x1f69, 0x0ff8 },
+  { 0x8500, 0x1f72, 0x4056 },
+  { 0x8900, 0x1f6e, 0x3ff8 },
+  { 0x8900, 0x1f6c, 0x2ff8 },
+  { 0x0900, 0x1f6b, 0x0ff8 },
+  { 0x0900, 0x1f6d, 0x0ff8 },
+  { 0x8500, 0x1f70, 0x204a },
+  { 0x0900, 0x1f6f, 0x0ff8 },
+  { 0x0500, 0x1f71, 0x004a },
+  { 0x8500, 0x1f76, 0x3064 },
+  { 0x8500, 0x1f74, 0x2056 },
+  { 0x0500, 0x1f73, 0x0056 },
+  { 0x0500, 0x1f75, 0x0056 },
+  { 0x8500, 0x1f78, 0x2080 },
+  { 0x0500, 0x1f77, 0x0064 },
+  { 0x0500, 0x1f79, 0x0080 },
+  { 0x8800, 0x1f9c, 0x6000 },
+  { 0x8800, 0x1f8c, 0x5000 },
+  { 0x8500, 0x1f84, 0x4008 },
+  { 0x8500, 0x1f80, 0x3008 },
+  { 0x8500, 0x1f7c, 0x207e },
+  { 0x0500, 0x1f7b, 0x0070 },
+  { 0x0500, 0x1f7d, 0x007e },
+  { 0x8500, 0x1f82, 0x2008 },
+  { 0x0500, 0x1f81, 0x0008 },
+  { 0x0500, 0x1f83, 0x0008 },
+  { 0x8800, 0x1f88, 0x3000 },
+  { 0x8500, 0x1f86, 0x2008 },
+  { 0x0500, 0x1f85, 0x0008 },
+  { 0x0500, 0x1f87, 0x0008 },
+  { 0x8800, 0x1f8a, 0x2000 },
+  { 0x0800, 0x1f89, 0x0000 },
+  { 0x0800, 0x1f8b, 0x0000 },
+  { 0x8500, 0x1f94, 0x4008 },
+  { 0x8500, 0x1f90, 0x3008 },
+  { 0x8800, 0x1f8e, 0x2000 },
+  { 0x0800, 0x1f8d, 0x0000 },
+  { 0x0800, 0x1f8f, 0x0000 },
+  { 0x8500, 0x1f92, 0x2008 },
+  { 0x0500, 0x1f91, 0x0008 },
+  { 0x0500, 0x1f93, 0x0008 },
+  { 0x8800, 0x1f98, 0x3000 },
+  { 0x8500, 0x1f96, 0x2008 },
+  { 0x0500, 0x1f95, 0x0008 },
+  { 0x0500, 0x1f97, 0x0008 },
+  { 0x8800, 0x1f9a, 0x2000 },
+  { 0x0800, 0x1f99, 0x0000 },
+  { 0x0800, 0x1f9b, 0x0000 },
+  { 0x8800, 0x1fac, 0x5000 },
+  { 0x8500, 0x1fa4, 0x4008 },
+  { 0x8500, 0x1fa0, 0x3008 },
+  { 0x8800, 0x1f9e, 0x2000 },
+  { 0x0800, 0x1f9d, 0x0000 },
+  { 0x0800, 0x1f9f, 0x0000 },
+  { 0x8500, 0x1fa2, 0x2008 },
+  { 0x0500, 0x1fa1, 0x0008 },
+  { 0x0500, 0x1fa3, 0x0008 },
+  { 0x8800, 0x1fa8, 0x3000 },
+  { 0x8500, 0x1fa6, 0x2008 },
+  { 0x0500, 0x1fa5, 0x0008 },
+  { 0x0500, 0x1fa7, 0x0008 },
+  { 0x8800, 0x1faa, 0x2000 },
+  { 0x0800, 0x1fa9, 0x0000 },
+  { 0x0800, 0x1fab, 0x0000 },
+  { 0x8500, 0x1fb4, 0x4000 },
+  { 0x8500, 0x1fb0, 0x3008 },
+  { 0x8800, 0x1fae, 0x2000 },
+  { 0x0800, 0x1fad, 0x0000 },
+  { 0x0800, 0x1faf, 0x0000 },
+  { 0x8500, 0x1fb2, 0x2000 },
+  { 0x0500, 0x1fb1, 0x0008 },
+  { 0x0500, 0x1fb3, 0x0009 },
+  { 0x8900, 0x1fb9, 0x3ff8 },
+  { 0x8500, 0x1fb7, 0x2000 },
+  { 0x0500, 0x1fb6, 0x0000 },
+  { 0x0900, 0x1fb8, 0x0ff8 },
+  { 0x8900, 0x1fbb, 0x2fb6 },
+  { 0x0900, 0x1fba, 0x0fb6 },
+  { 0x0800, 0x1fbc, 0x0000 },
+  { 0x9d00, 0x2005, 0x7000 },
+  { 0x8500, 0x1fe1, 0x6008 },
+  { 0x9800, 0x1fce, 0x5000 },
+  { 0x8500, 0x1fc6, 0x4000 },
+  { 0x9800, 0x1fc1, 0x3000 },
+  { 0x9800, 0x1fbf, 0x2000 },
+  { 0x0500, 0x1fbe, 0x0000 },
+  { 0x1800, 0x1fc0, 0x0000 },
+  { 0x8500, 0x1fc3, 0x2009 },
+  { 0x0500, 0x1fc2, 0x0000 },
+  { 0x0500, 0x1fc4, 0x0000 },
+  { 0x8900, 0x1fca, 0x3faa },
+  { 0x8900, 0x1fc8, 0x2faa },
+  { 0x0500, 0x1fc7, 0x0000 },
+  { 0x0900, 0x1fc9, 0x0faa },
+  { 0x8800, 0x1fcc, 0x2000 },
+  { 0x0900, 0x1fcb, 0x0faa },
+  { 0x1800, 0x1fcd, 0x0000 },
+  { 0x8900, 0x1fd8, 0x4ff8 },
+  { 0x8500, 0x1fd2, 0x3000 },
+  { 0x8500, 0x1fd0, 0x2008 },
+  { 0x1800, 0x1fcf, 0x0000 },
+  { 0x0500, 0x1fd1, 0x0008 },
+  { 0x8500, 0x1fd6, 0x2000 },
+  { 0x0500, 0x1fd3, 0x0000 },
+  { 0x0500, 0x1fd7, 0x0000 },
+  { 0x9800, 0x1fdd, 0x3000 },
+  { 0x8900, 0x1fda, 0x2f9c },
+  { 0x0900, 0x1fd9, 0x0ff8 },
+  { 0x0900, 0x1fdb, 0x0f9c },
+  { 0x9800, 0x1fdf, 0x2000 },
+  { 0x1800, 0x1fde, 0x0000 },
+  { 0x0500, 0x1fe0, 0x0008 },
+  { 0x8500, 0x1ff3, 0x5009 },
+  { 0x8900, 0x1fe9, 0x4ff8 },
+  { 0x8500, 0x1fe5, 0x3007 },
+  { 0x8500, 0x1fe3, 0x2000 },
+  { 0x0500, 0x1fe2, 0x0000 },
+  { 0x0500, 0x1fe4, 0x0000 },
+  { 0x8500, 0x1fe7, 0x2000 },
+  { 0x0500, 0x1fe6, 0x0000 },
+  { 0x0900, 0x1fe8, 0x0ff8 },
+  { 0x9800, 0x1fed, 0x3000 },
+  { 0x8900, 0x1feb, 0x2f90 },
+  { 0x0900, 0x1fea, 0x0f90 },
+  { 0x0900, 0x1fec, 0x0ff9 },
+  { 0x9800, 0x1fef, 0x2000 },
+  { 0x1800, 0x1fee, 0x0000 },
+  { 0x0500, 0x1ff2, 0x0000 },
+  { 0x8800, 0x1ffc, 0x4000 },
+  { 0x8900, 0x1ff8, 0x3f80 },
+  { 0x8500, 0x1ff6, 0x2000 },
+  { 0x0500, 0x1ff4, 0x0000 },
+  { 0x0500, 0x1ff7, 0x0000 },
+  { 0x8900, 0x1ffa, 0x2f82 },
+  { 0x0900, 0x1ff9, 0x0f80 },
+  { 0x0900, 0x1ffb, 0x0f82 },
+  { 0x9d00, 0x2001, 0x3000 },
+  { 0x9800, 0x1ffe, 0x2000 },
+  { 0x1800, 0x1ffd, 0x0000 },
+  { 0x1d00, 0x2000, 0x0000 },
+  { 0x9d00, 0x2003, 0x2000 },
+  { 0x1d00, 0x2002, 0x0000 },
+  { 0x1d00, 0x2004, 0x0000 },
+  { 0x9500, 0x2025, 0x6000 },
+  { 0x9100, 0x2015, 0x5000 },
+  { 0x8100, 0x200d, 0x4000 },
+  { 0x9d00, 0x2009, 0x3000 },
+  { 0x9d00, 0x2007, 0x2000 },
+  { 0x1d00, 0x2006, 0x0000 },
+  { 0x1d00, 0x2008, 0x0000 },
+  { 0x9d00, 0x200b, 0x2000 },
+  { 0x1d00, 0x200a, 0x0000 },
+  { 0x0100, 0x200c, 0x0000 },
+  { 0x9100, 0x2011, 0x3000 },
+  { 0x8100, 0x200f, 0x2000 },
+  { 0x0100, 0x200e, 0x0000 },
+  { 0x1100, 0x2010, 0x0000 },
+  { 0x9100, 0x2013, 0x2000 },
+  { 0x1100, 0x2012, 0x0000 },
+  { 0x1100, 0x2014, 0x0000 },
+  { 0x9300, 0x201d, 0x4000 },
+  { 0x9300, 0x2019, 0x3000 },
+  { 0x9500, 0x2017, 0x2000 },
+  { 0x1500, 0x2016, 0x0000 },
+  { 0x1400, 0x2018, 0x0000 },
+  { 0x9400, 0x201b, 0x2000 },
+  { 0x1600, 0x201a, 0x0000 },
+  { 0x1400, 0x201c, 0x0000 },
+  { 0x9500, 0x2021, 0x3000 },
+  { 0x9400, 0x201f, 0x2000 },
+  { 0x1600, 0x201e, 0x0000 },
+  { 0x1500, 0x2020, 0x0000 },
+  { 0x9500, 0x2023, 0x2000 },
+  { 0x1500, 0x2022, 0x0000 },
+  { 0x1500, 0x2024, 0x0000 },
+  { 0x9500, 0x2035, 0x5000 },
+  { 0x8100, 0x202d, 0x4000 },
+  { 0x9c00, 0x2029, 0x3000 },
+  { 0x9500, 0x2027, 0x2000 },
+  { 0x1500, 0x2026, 0x0000 },
+  { 0x1b00, 0x2028, 0x0000 },
+  { 0x8100, 0x202b, 0x2000 },
+  { 0x0100, 0x202a, 0x0000 },
+  { 0x0100, 0x202c, 0x0000 },
+  { 0x9500, 0x2031, 0x3000 },
+  { 0x9d00, 0x202f, 0x2000 },
+  { 0x0100, 0x202e, 0x0000 },
+  { 0x1500, 0x2030, 0x0000 },
+  { 0x9500, 0x2033, 0x2000 },
+  { 0x1500, 0x2032, 0x0000 },
+  { 0x1500, 0x2034, 0x0000 },
+  { 0x9500, 0x203d, 0x4000 },
+  { 0x9400, 0x2039, 0x3000 },
+  { 0x9500, 0x2037, 0x2000 },
+  { 0x1500, 0x2036, 0x0000 },
+  { 0x1500, 0x2038, 0x0000 },
+  { 0x9500, 0x203b, 0x2000 },
+  { 0x1300, 0x203a, 0x0000 },
+  { 0x1500, 0x203c, 0x0000 },
+  { 0x9500, 0x2041, 0x3000 },
+  { 0x9000, 0x203f, 0x2000 },
+  { 0x1500, 0x203e, 0x0000 },
+  { 0x1000, 0x2040, 0x0000 },
+  { 0x9500, 0x2043, 0x2000 },
+  { 0x1500, 0x2042, 0x0000 },
+  { 0x1900, 0x2044, 0x0000 },
+  { 0x9900, 0x21ae, 0x9000 },
+  { 0x8900, 0x211a, 0x8000 },
+  { 0x9700, 0x20a7, 0x7000 },
+  { 0x8f00, 0x2076, 0x6000 },
+  { 0x9500, 0x2057, 0x5000 },
+  { 0x9500, 0x204d, 0x4000 },
+  { 0x9500, 0x2049, 0x3000 },
+  { 0x9500, 0x2047, 0x2000 },
+  { 0x1200, 0x2046, 0x0000 },
+  { 0x1500, 0x2048, 0x0000 },
+  { 0x9500, 0x204b, 0x2000 },
+  { 0x1500, 0x204a, 0x0000 },
+  { 0x1500, 0x204c, 0x0000 },
+  { 0x9500, 0x2051, 0x3000 },
+  { 0x9500, 0x204f, 0x2000 },
+  { 0x1500, 0x204e, 0x0000 },
+  { 0x1500, 0x2050, 0x0000 },
+  { 0x9500, 0x2053, 0x2000 },
+  { 0x1900, 0x2052, 0x0000 },
+  { 0x1000, 0x2054, 0x0000 },
+  { 0x8100, 0x206c, 0x4000 },
+  { 0x8100, 0x2062, 0x3000 },
+  { 0x8100, 0x2060, 0x2000 },
+  { 0x1d00, 0x205f, 0x0000 },
+  { 0x0100, 0x2061, 0x0000 },
+  { 0x8100, 0x206a, 0x2000 },
+  { 0x0100, 0x2063, 0x0000 },
+  { 0x0100, 0x206b, 0x0000 },
+  { 0x8f00, 0x2070, 0x3000 },
+  { 0x8100, 0x206e, 0x2000 },
+  { 0x0100, 0x206d, 0x0000 },
+  { 0x0100, 0x206f, 0x0000 },
+  { 0x8f00, 0x2074, 0x2000 },
+  { 0x0500, 0x2071, 0x0000 },
+  { 0x0f00, 0x2075, 0x0000 },
+  { 0x8f00, 0x2086, 0x5000 },
+  { 0x9200, 0x207e, 0x4000 },
+  { 0x9900, 0x207a, 0x3000 },
+  { 0x8f00, 0x2078, 0x2000 },
+  { 0x0f00, 0x2077, 0x0000 },
+  { 0x0f00, 0x2079, 0x0000 },
+  { 0x9900, 0x207c, 0x2000 },
+  { 0x1900, 0x207b, 0x0000 },
+  { 0x1600, 0x207d, 0x0000 },
+  { 0x8f00, 0x2082, 0x3000 },
+  { 0x8f00, 0x2080, 0x2000 },
+  { 0x0500, 0x207f, 0x0000 },
+  { 0x0f00, 0x2081, 0x0000 },
+  { 0x8f00, 0x2084, 0x2000 },
+  { 0x0f00, 0x2083, 0x0000 },
+  { 0x0f00, 0x2085, 0x0000 },
+  { 0x9200, 0x208e, 0x4000 },
+  { 0x9900, 0x208a, 0x3000 },
+  { 0x8f00, 0x2088, 0x2000 },
+  { 0x0f00, 0x2087, 0x0000 },
+  { 0x0f00, 0x2089, 0x0000 },
+  { 0x9900, 0x208c, 0x2000 },
+  { 0x1900, 0x208b, 0x0000 },
+  { 0x1600, 0x208d, 0x0000 },
+  { 0x9700, 0x20a3, 0x3000 },
+  { 0x9700, 0x20a1, 0x2000 },
+  { 0x1700, 0x20a0, 0x0000 },
+  { 0x1700, 0x20a2, 0x0000 },
+  { 0x9700, 0x20a5, 0x2000 },
+  { 0x1700, 0x20a4, 0x0000 },
+  { 0x1700, 0x20a6, 0x0000 },
+  { 0x8c00, 0x20e5, 0x6000 },
+  { 0x8c00, 0x20d5, 0x5000 },
+  { 0x9700, 0x20af, 0x4000 },
+  { 0x9700, 0x20ab, 0x3000 },
+  { 0x9700, 0x20a9, 0x2000 },
+  { 0x1700, 0x20a8, 0x0000 },
+  { 0x1700, 0x20aa, 0x0000 },
+  { 0x9700, 0x20ad, 0x2000 },
+  { 0x1700, 0x20ac, 0x0000 },
+  { 0x1700, 0x20ae, 0x0000 },
+  { 0x8c00, 0x20d1, 0x3000 },
+  { 0x9700, 0x20b1, 0x2000 },
+  { 0x1700, 0x20b0, 0x0000 },
+  { 0x0c00, 0x20d0, 0x0000 },
+  { 0x8c00, 0x20d3, 0x2000 },
+  { 0x0c00, 0x20d2, 0x0000 },
+  { 0x0c00, 0x20d4, 0x0000 },
+  { 0x8b00, 0x20dd, 0x4000 },
+  { 0x8c00, 0x20d9, 0x3000 },
+  { 0x8c00, 0x20d7, 0x2000 },
+  { 0x0c00, 0x20d6, 0x0000 },
+  { 0x0c00, 0x20d8, 0x0000 },
+  { 0x8c00, 0x20db, 0x2000 },
+  { 0x0c00, 0x20da, 0x0000 },
+  { 0x0c00, 0x20dc, 0x0000 },
+  { 0x8c00, 0x20e1, 0x3000 },
+  { 0x8b00, 0x20df, 0x2000 },
+  { 0x0b00, 0x20de, 0x0000 },
+  { 0x0b00, 0x20e0, 0x0000 },
+  { 0x8b00, 0x20e3, 0x2000 },
+  { 0x0b00, 0x20e2, 0x0000 },
+  { 0x0b00, 0x20e4, 0x0000 },
+  { 0x8500, 0x210a, 0x5000 },
+  { 0x8900, 0x2102, 0x4000 },
+  { 0x8c00, 0x20e9, 0x3000 },
+  { 0x8c00, 0x20e7, 0x2000 },
+  { 0x0c00, 0x20e6, 0x0000 },
+  { 0x0c00, 0x20e8, 0x0000 },
+  { 0x9a00, 0x2100, 0x2000 },
+  { 0x0c00, 0x20ea, 0x0000 },
+  { 0x1a00, 0x2101, 0x0000 },
+  { 0x9a00, 0x2106, 0x3000 },
+  { 0x9a00, 0x2104, 0x2000 },
+  { 0x1a00, 0x2103, 0x0000 },
+  { 0x1a00, 0x2105, 0x0000 },
+  { 0x9a00, 0x2108, 0x2000 },
+  { 0x0900, 0x2107, 0x0000 },
+  { 0x1a00, 0x2109, 0x0000 },
+  { 0x8900, 0x2112, 0x4000 },
+  { 0x8500, 0x210e, 0x3000 },
+  { 0x8900, 0x210c, 0x2000 },
+  { 0x0900, 0x210b, 0x0000 },
+  { 0x0900, 0x210d, 0x0000 },
+  { 0x8900, 0x2110, 0x2000 },
+  { 0x0500, 0x210f, 0x0000 },
+  { 0x0900, 0x2111, 0x0000 },
+  { 0x9a00, 0x2116, 0x3000 },
+  { 0x9a00, 0x2114, 0x2000 },
+  { 0x0500, 0x2113, 0x0000 },
+  { 0x0900, 0x2115, 0x0000 },
+  { 0x9a00, 0x2118, 0x2000 },
+  { 0x1a00, 0x2117, 0x0000 },
+  { 0x0900, 0x2119, 0x0000 },
+  { 0x8e00, 0x2162, 0x7000 },
+  { 0x9a00, 0x213a, 0x6000 },
+  { 0x8900, 0x212a, 0x5000 },
+  { 0x9a00, 0x2122, 0x4000 },
+  { 0x9a00, 0x211e, 0x3000 },
+  { 0x8900, 0x211c, 0x2000 },
+  { 0x0900, 0x211b, 0x0000 },
+  { 0x0900, 0x211d, 0x0000 },
+  { 0x9a00, 0x2120, 0x2000 },
+  { 0x1a00, 0x211f, 0x0000 },
+  { 0x1a00, 0x2121, 0x0000 },
+  { 0x8900, 0x2126, 0x3000 },
+  { 0x8900, 0x2124, 0x2000 },
+  { 0x1a00, 0x2123, 0x0000 },
+  { 0x1a00, 0x2125, 0x0000 },
+  { 0x8900, 0x2128, 0x2000 },
+  { 0x1a00, 0x2127, 0x0000 },
+  { 0x1a00, 0x2129, 0x0000 },
+  { 0x9a00, 0x2132, 0x4000 },
+  { 0x9a00, 0x212e, 0x3000 },
+  { 0x8900, 0x212c, 0x2000 },
+  { 0x0900, 0x212b, 0x0000 },
+  { 0x0900, 0x212d, 0x0000 },
+  { 0x8900, 0x2130, 0x2000 },
+  { 0x0500, 0x212f, 0x0000 },
+  { 0x0900, 0x2131, 0x0000 },
+  { 0x8700, 0x2136, 0x3000 },
+  { 0x8500, 0x2134, 0x2000 },
+  { 0x0900, 0x2133, 0x0000 },
+  { 0x0700, 0x2135, 0x0000 },
+  { 0x8700, 0x2138, 0x2000 },
+  { 0x0700, 0x2137, 0x0000 },
+  { 0x0500, 0x2139, 0x0000 },
+  { 0x9900, 0x214b, 0x5000 },
+  { 0x9900, 0x2143, 0x4000 },
+  { 0x8900, 0x213f, 0x3000 },
+  { 0x8500, 0x213d, 0x2000 },
+  { 0x1a00, 0x213b, 0x0000 },
+  { 0x0900, 0x213e, 0x0000 },
+  { 0x9900, 0x2141, 0x2000 },
+  { 0x1900, 0x2140, 0x0000 },
+  { 0x1900, 0x2142, 0x0000 },
+  { 0x8500, 0x2147, 0x3000 },
+  { 0x8900, 0x2145, 0x2000 },
+  { 0x1900, 0x2144, 0x0000 },
+  { 0x0500, 0x2146, 0x0000 },
+  { 0x8500, 0x2149, 0x2000 },
+  { 0x0500, 0x2148, 0x0000 },
+  { 0x1a00, 0x214a, 0x0000 },
+  { 0x8f00, 0x215a, 0x4000 },
+  { 0x8f00, 0x2156, 0x3000 },
+  { 0x8f00, 0x2154, 0x2000 },
+  { 0x0f00, 0x2153, 0x0000 },
+  { 0x0f00, 0x2155, 0x0000 },
+  { 0x8f00, 0x2158, 0x2000 },
+  { 0x0f00, 0x2157, 0x0000 },
+  { 0x0f00, 0x2159, 0x0000 },
+  { 0x8f00, 0x215e, 0x3000 },
+  { 0x8f00, 0x215c, 0x2000 },
+  { 0x0f00, 0x215b, 0x0000 },
+  { 0x0f00, 0x215d, 0x0000 },
+  { 0x8e00, 0x2160, 0x2000 },
+  { 0x0f00, 0x215f, 0x0000 },
+  { 0x0e00, 0x2161, 0x0000 },
+  { 0x8e00, 0x2182, 0x6000 },
+  { 0x8e00, 0x2172, 0x5000 },
+  { 0x8e00, 0x216a, 0x4000 },
+  { 0x8e00, 0x2166, 0x3000 },
+  { 0x8e00, 0x2164, 0x2000 },
+  { 0x0e00, 0x2163, 0x0000 },
+  { 0x0e00, 0x2165, 0x0000 },
+  { 0x8e00, 0x2168, 0x2000 },
+  { 0x0e00, 0x2167, 0x0000 },
+  { 0x0e00, 0x2169, 0x0000 },
+  { 0x8e00, 0x216e, 0x3000 },
+  { 0x8e00, 0x216c, 0x2000 },
+  { 0x0e00, 0x216b, 0x0000 },
+  { 0x0e00, 0x216d, 0x0000 },
+  { 0x8e00, 0x2170, 0x2000 },
+  { 0x0e00, 0x216f, 0x0000 },
+  { 0x0e00, 0x2171, 0x0000 },
+  { 0x8e00, 0x217a, 0x4000 },
+  { 0x8e00, 0x2176, 0x3000 },
+  { 0x8e00, 0x2174, 0x2000 },
+  { 0x0e00, 0x2173, 0x0000 },
+  { 0x0e00, 0x2175, 0x0000 },
+  { 0x8e00, 0x2178, 0x2000 },
+  { 0x0e00, 0x2177, 0x0000 },
+  { 0x0e00, 0x2179, 0x0000 },
+  { 0x8e00, 0x217e, 0x3000 },
+  { 0x8e00, 0x217c, 0x2000 },
+  { 0x0e00, 0x217b, 0x0000 },
+  { 0x0e00, 0x217d, 0x0000 },
+  { 0x8e00, 0x2180, 0x2000 },
+  { 0x0e00, 0x217f, 0x0000 },
+  { 0x0e00, 0x2181, 0x0000 },
+  { 0x9a00, 0x219e, 0x5000 },
+  { 0x9a00, 0x2196, 0x4000 },
+  { 0x9900, 0x2192, 0x3000 },
+  { 0x9900, 0x2190, 0x2000 },
+  { 0x0e00, 0x2183, 0x0000 },
+  { 0x1900, 0x2191, 0x0000 },
+  { 0x9900, 0x2194, 0x2000 },
+  { 0x1900, 0x2193, 0x0000 },
+  { 0x1a00, 0x2195, 0x0000 },
+  { 0x9900, 0x219a, 0x3000 },
+  { 0x9a00, 0x2198, 0x2000 },
+  { 0x1a00, 0x2197, 0x0000 },
+  { 0x1a00, 0x2199, 0x0000 },
+  { 0x9a00, 0x219c, 0x2000 },
+  { 0x1900, 0x219b, 0x0000 },
+  { 0x1a00, 0x219d, 0x0000 },
+  { 0x9900, 0x21a6, 0x4000 },
+  { 0x9a00, 0x21a2, 0x3000 },
+  { 0x9900, 0x21a0, 0x2000 },
+  { 0x1a00, 0x219f, 0x0000 },
+  { 0x1a00, 0x21a1, 0x0000 },
+  { 0x9a00, 0x21a4, 0x2000 },
+  { 0x1900, 0x21a3, 0x0000 },
+  { 0x1a00, 0x21a5, 0x0000 },
+  { 0x9a00, 0x21aa, 0x3000 },
+  { 0x9a00, 0x21a8, 0x2000 },
+  { 0x1a00, 0x21a7, 0x0000 },
+  { 0x1a00, 0x21a9, 0x0000 },
+  { 0x9a00, 0x21ac, 0x2000 },
+  { 0x1a00, 0x21ab, 0x0000 },
+  { 0x1a00, 0x21ad, 0x0000 },
+  { 0x9900, 0x222e, 0x8000 },
+  { 0x9a00, 0x21ee, 0x7000 },
+  { 0x9900, 0x21ce, 0x6000 },
+  { 0x9a00, 0x21be, 0x5000 },
+  { 0x9a00, 0x21b6, 0x4000 },
+  { 0x9a00, 0x21b2, 0x3000 },
+  { 0x9a00, 0x21b0, 0x2000 },
+  { 0x1a00, 0x21af, 0x0000 },
+  { 0x1a00, 0x21b1, 0x0000 },
+  { 0x9a00, 0x21b4, 0x2000 },
+  { 0x1a00, 0x21b3, 0x0000 },
+  { 0x1a00, 0x21b5, 0x0000 },
+  { 0x9a00, 0x21ba, 0x3000 },
+  { 0x9a00, 0x21b8, 0x2000 },
+  { 0x1a00, 0x21b7, 0x0000 },
+  { 0x1a00, 0x21b9, 0x0000 },
+  { 0x9a00, 0x21bc, 0x2000 },
+  { 0x1a00, 0x21bb, 0x0000 },
+  { 0x1a00, 0x21bd, 0x0000 },
+  { 0x9a00, 0x21c6, 0x4000 },
+  { 0x9a00, 0x21c2, 0x3000 },
+  { 0x9a00, 0x21c0, 0x2000 },
+  { 0x1a00, 0x21bf, 0x0000 },
+  { 0x1a00, 0x21c1, 0x0000 },
+  { 0x9a00, 0x21c4, 0x2000 },
+  { 0x1a00, 0x21c3, 0x0000 },
+  { 0x1a00, 0x21c5, 0x0000 },
+  { 0x9a00, 0x21ca, 0x3000 },
+  { 0x9a00, 0x21c8, 0x2000 },
+  { 0x1a00, 0x21c7, 0x0000 },
+  { 0x1a00, 0x21c9, 0x0000 },
+  { 0x9a00, 0x21cc, 0x2000 },
+  { 0x1a00, 0x21cb, 0x0000 },
+  { 0x1a00, 0x21cd, 0x0000 },
+  { 0x9a00, 0x21de, 0x5000 },
+  { 0x9a00, 0x21d6, 0x4000 },
+  { 0x9900, 0x21d2, 0x3000 },
+  { 0x9a00, 0x21d0, 0x2000 },
+  { 0x1900, 0x21cf, 0x0000 },
+  { 0x1a00, 0x21d1, 0x0000 },
+  { 0x9900, 0x21d4, 0x2000 },
+  { 0x1a00, 0x21d3, 0x0000 },
+  { 0x1a00, 0x21d5, 0x0000 },
+  { 0x9a00, 0x21da, 0x3000 },
+  { 0x9a00, 0x21d8, 0x2000 },
+  { 0x1a00, 0x21d7, 0x0000 },
+  { 0x1a00, 0x21d9, 0x0000 },
+  { 0x9a00, 0x21dc, 0x2000 },
+  { 0x1a00, 0x21db, 0x0000 },
+  { 0x1a00, 0x21dd, 0x0000 },
+  { 0x9a00, 0x21e6, 0x4000 },
+  { 0x9a00, 0x21e2, 0x3000 },
+  { 0x9a00, 0x21e0, 0x2000 },
+  { 0x1a00, 0x21df, 0x0000 },
+  { 0x1a00, 0x21e1, 0x0000 },
+  { 0x9a00, 0x21e4, 0x2000 },
+  { 0x1a00, 0x21e3, 0x0000 },
+  { 0x1a00, 0x21e5, 0x0000 },
+  { 0x9a00, 0x21ea, 0x3000 },
+  { 0x9a00, 0x21e8, 0x2000 },
+  { 0x1a00, 0x21e7, 0x0000 },
+  { 0x1a00, 0x21e9, 0x0000 },
+  { 0x9a00, 0x21ec, 0x2000 },
+  { 0x1a00, 0x21eb, 0x0000 },
+  { 0x1a00, 0x21ed, 0x0000 },
+  { 0x9900, 0x220e, 0x6000 },
+  { 0x9900, 0x21fe, 0x5000 },
+  { 0x9900, 0x21f6, 0x4000 },
+  { 0x9a00, 0x21f2, 0x3000 },
+  { 0x9a00, 0x21f0, 0x2000 },
+  { 0x1a00, 0x21ef, 0x0000 },
+  { 0x1a00, 0x21f1, 0x0000 },
+  { 0x9900, 0x21f4, 0x2000 },
+  { 0x1a00, 0x21f3, 0x0000 },
+  { 0x1900, 0x21f5, 0x0000 },
+  { 0x9900, 0x21fa, 0x3000 },
+  { 0x9900, 0x21f8, 0x2000 },
+  { 0x1900, 0x21f7, 0x0000 },
+  { 0x1900, 0x21f9, 0x0000 },
+  { 0x9900, 0x21fc, 0x2000 },
+  { 0x1900, 0x21fb, 0x0000 },
+  { 0x1900, 0x21fd, 0x0000 },
+  { 0x9900, 0x2206, 0x4000 },
+  { 0x9900, 0x2202, 0x3000 },
+  { 0x9900, 0x2200, 0x2000 },
+  { 0x1900, 0x21ff, 0x0000 },
+  { 0x1900, 0x2201, 0x0000 },
+  { 0x9900, 0x2204, 0x2000 },
+  { 0x1900, 0x2203, 0x0000 },
+  { 0x1900, 0x2205, 0x0000 },
+  { 0x9900, 0x220a, 0x3000 },
+  { 0x9900, 0x2208, 0x2000 },
+  { 0x1900, 0x2207, 0x0000 },
+  { 0x1900, 0x2209, 0x0000 },
+  { 0x9900, 0x220c, 0x2000 },
+  { 0x1900, 0x220b, 0x0000 },
+  { 0x1900, 0x220d, 0x0000 },
+  { 0x9900, 0x221e, 0x5000 },
+  { 0x9900, 0x2216, 0x4000 },
+  { 0x9900, 0x2212, 0x3000 },
+  { 0x9900, 0x2210, 0x2000 },
+  { 0x1900, 0x220f, 0x0000 },
+  { 0x1900, 0x2211, 0x0000 },
+  { 0x9900, 0x2214, 0x2000 },
+  { 0x1900, 0x2213, 0x0000 },
+  { 0x1900, 0x2215, 0x0000 },
+  { 0x9900, 0x221a, 0x3000 },
+  { 0x9900, 0x2218, 0x2000 },
+  { 0x1900, 0x2217, 0x0000 },
+  { 0x1900, 0x2219, 0x0000 },
+  { 0x9900, 0x221c, 0x2000 },
+  { 0x1900, 0x221b, 0x0000 },
+  { 0x1900, 0x221d, 0x0000 },
+  { 0x9900, 0x2226, 0x4000 },
+  { 0x9900, 0x2222, 0x3000 },
+  { 0x9900, 0x2220, 0x2000 },
+  { 0x1900, 0x221f, 0x0000 },
+  { 0x1900, 0x2221, 0x0000 },
+  { 0x9900, 0x2224, 0x2000 },
+  { 0x1900, 0x2223, 0x0000 },
+  { 0x1900, 0x2225, 0x0000 },
+  { 0x9900, 0x222a, 0x3000 },
+  { 0x9900, 0x2228, 0x2000 },
+  { 0x1900, 0x2227, 0x0000 },
+  { 0x1900, 0x2229, 0x0000 },
+  { 0x9900, 0x222c, 0x2000 },
+  { 0x1900, 0x222b, 0x0000 },
+  { 0x1900, 0x222d, 0x0000 },
+  { 0x9900, 0x226e, 0x7000 },
+  { 0x9900, 0x224e, 0x6000 },
+  { 0x9900, 0x223e, 0x5000 },
+  { 0x9900, 0x2236, 0x4000 },
+  { 0x9900, 0x2232, 0x3000 },
+  { 0x9900, 0x2230, 0x2000 },
+  { 0x1900, 0x222f, 0x0000 },
+  { 0x1900, 0x2231, 0x0000 },
+  { 0x9900, 0x2234, 0x2000 },
+  { 0x1900, 0x2233, 0x0000 },
+  { 0x1900, 0x2235, 0x0000 },
+  { 0x9900, 0x223a, 0x3000 },
+  { 0x9900, 0x2238, 0x2000 },
+  { 0x1900, 0x2237, 0x0000 },
+  { 0x1900, 0x2239, 0x0000 },
+  { 0x9900, 0x223c, 0x2000 },
+  { 0x1900, 0x223b, 0x0000 },
+  { 0x1900, 0x223d, 0x0000 },
+  { 0x9900, 0x2246, 0x4000 },
+  { 0x9900, 0x2242, 0x3000 },
+  { 0x9900, 0x2240, 0x2000 },
+  { 0x1900, 0x223f, 0x0000 },
+  { 0x1900, 0x2241, 0x0000 },
+  { 0x9900, 0x2244, 0x2000 },
+  { 0x1900, 0x2243, 0x0000 },
+  { 0x1900, 0x2245, 0x0000 },
+  { 0x9900, 0x224a, 0x3000 },
+  { 0x9900, 0x2248, 0x2000 },
+  { 0x1900, 0x2247, 0x0000 },
+  { 0x1900, 0x2249, 0x0000 },
+  { 0x9900, 0x224c, 0x2000 },
+  { 0x1900, 0x224b, 0x0000 },
+  { 0x1900, 0x224d, 0x0000 },
+  { 0x9900, 0x225e, 0x5000 },
+  { 0x9900, 0x2256, 0x4000 },
+  { 0x9900, 0x2252, 0x3000 },
+  { 0x9900, 0x2250, 0x2000 },
+  { 0x1900, 0x224f, 0x0000 },
+  { 0x1900, 0x2251, 0x0000 },
+  { 0x9900, 0x2254, 0x2000 },
+  { 0x1900, 0x2253, 0x0000 },
+  { 0x1900, 0x2255, 0x0000 },
+  { 0x9900, 0x225a, 0x3000 },
+  { 0x9900, 0x2258, 0x2000 },
+  { 0x1900, 0x2257, 0x0000 },
+  { 0x1900, 0x2259, 0x0000 },
+  { 0x9900, 0x225c, 0x2000 },
+  { 0x1900, 0x225b, 0x0000 },
+  { 0x1900, 0x225d, 0x0000 },
+  { 0x9900, 0x2266, 0x4000 },
+  { 0x9900, 0x2262, 0x3000 },
+  { 0x9900, 0x2260, 0x2000 },
+  { 0x1900, 0x225f, 0x0000 },
+  { 0x1900, 0x2261, 0x0000 },
+  { 0x9900, 0x2264, 0x2000 },
+  { 0x1900, 0x2263, 0x0000 },
+  { 0x1900, 0x2265, 0x0000 },
+  { 0x9900, 0x226a, 0x3000 },
+  { 0x9900, 0x2268, 0x2000 },
+  { 0x1900, 0x2267, 0x0000 },
+  { 0x1900, 0x2269, 0x0000 },
+  { 0x9900, 0x226c, 0x2000 },
+  { 0x1900, 0x226b, 0x0000 },
+  { 0x1900, 0x226d, 0x0000 },
+  { 0x9900, 0x228e, 0x6000 },
+  { 0x9900, 0x227e, 0x5000 },
+  { 0x9900, 0x2276, 0x4000 },
+  { 0x9900, 0x2272, 0x3000 },
+  { 0x9900, 0x2270, 0x2000 },
+  { 0x1900, 0x226f, 0x0000 },
+  { 0x1900, 0x2271, 0x0000 },
+  { 0x9900, 0x2274, 0x2000 },
+  { 0x1900, 0x2273, 0x0000 },
+  { 0x1900, 0x2275, 0x0000 },
+  { 0x9900, 0x227a, 0x3000 },
+  { 0x9900, 0x2278, 0x2000 },
+  { 0x1900, 0x2277, 0x0000 },
+  { 0x1900, 0x2279, 0x0000 },
+  { 0x9900, 0x227c, 0x2000 },
+  { 0x1900, 0x227b, 0x0000 },
+  { 0x1900, 0x227d, 0x0000 },
+  { 0x9900, 0x2286, 0x4000 },
+  { 0x9900, 0x2282, 0x3000 },
+  { 0x9900, 0x2280, 0x2000 },
+  { 0x1900, 0x227f, 0x0000 },
+  { 0x1900, 0x2281, 0x0000 },
+  { 0x9900, 0x2284, 0x2000 },
+  { 0x1900, 0x2283, 0x0000 },
+  { 0x1900, 0x2285, 0x0000 },
+  { 0x9900, 0x228a, 0x3000 },
+  { 0x9900, 0x2288, 0x2000 },
+  { 0x1900, 0x2287, 0x0000 },
+  { 0x1900, 0x2289, 0x0000 },
+  { 0x9900, 0x228c, 0x2000 },
+  { 0x1900, 0x228b, 0x0000 },
+  { 0x1900, 0x228d, 0x0000 },
+  { 0x9900, 0x229e, 0x5000 },
+  { 0x9900, 0x2296, 0x4000 },
+  { 0x9900, 0x2292, 0x3000 },
+  { 0x9900, 0x2290, 0x2000 },
+  { 0x1900, 0x228f, 0x0000 },
+  { 0x1900, 0x2291, 0x0000 },
+  { 0x9900, 0x2294, 0x2000 },
+  { 0x1900, 0x2293, 0x0000 },
+  { 0x1900, 0x2295, 0x0000 },
+  { 0x9900, 0x229a, 0x3000 },
+  { 0x9900, 0x2298, 0x2000 },
+  { 0x1900, 0x2297, 0x0000 },
+  { 0x1900, 0x2299, 0x0000 },
+  { 0x9900, 0x229c, 0x2000 },
+  { 0x1900, 0x229b, 0x0000 },
+  { 0x1900, 0x229d, 0x0000 },
+  { 0x9900, 0x22a6, 0x4000 },
+  { 0x9900, 0x22a2, 0x3000 },
+  { 0x9900, 0x22a0, 0x2000 },
+  { 0x1900, 0x229f, 0x0000 },
+  { 0x1900, 0x22a1, 0x0000 },
+  { 0x9900, 0x22a4, 0x2000 },
+  { 0x1900, 0x22a3, 0x0000 },
+  { 0x1900, 0x22a5, 0x0000 },
+  { 0x9900, 0x22aa, 0x3000 },
+  { 0x9900, 0x22a8, 0x2000 },
+  { 0x1900, 0x22a7, 0x0000 },
+  { 0x1900, 0x22a9, 0x0000 },
+  { 0x9900, 0x22ac, 0x2000 },
+  { 0x1900, 0x22ab, 0x0000 },
+  { 0x1900, 0x22ad, 0x0000 },
+  { 0x8f00, 0x2787, 0xb000 },
+  { 0x9a00, 0x250b, 0xa000 },
+  { 0x9900, 0x23ae, 0x9000 },
+  { 0x9a00, 0x232e, 0x8000 },
+  { 0x9900, 0x22ee, 0x7000 },
+  { 0x9900, 0x22ce, 0x6000 },
+  { 0x9900, 0x22be, 0x5000 },
+  { 0x9900, 0x22b6, 0x4000 },
+  { 0x9900, 0x22b2, 0x3000 },
+  { 0x9900, 0x22b0, 0x2000 },
+  { 0x1900, 0x22af, 0x0000 },
+  { 0x1900, 0x22b1, 0x0000 },
+  { 0x9900, 0x22b4, 0x2000 },
+  { 0x1900, 0x22b3, 0x0000 },
+  { 0x1900, 0x22b5, 0x0000 },
+  { 0x9900, 0x22ba, 0x3000 },
+  { 0x9900, 0x22b8, 0x2000 },
+  { 0x1900, 0x22b7, 0x0000 },
+  { 0x1900, 0x22b9, 0x0000 },
+  { 0x9900, 0x22bc, 0x2000 },
+  { 0x1900, 0x22bb, 0x0000 },
+  { 0x1900, 0x22bd, 0x0000 },
+  { 0x9900, 0x22c6, 0x4000 },
+  { 0x9900, 0x22c2, 0x3000 },
+  { 0x9900, 0x22c0, 0x2000 },
+  { 0x1900, 0x22bf, 0x0000 },
+  { 0x1900, 0x22c1, 0x0000 },
+  { 0x9900, 0x22c4, 0x2000 },
+  { 0x1900, 0x22c3, 0x0000 },
+  { 0x1900, 0x22c5, 0x0000 },
+  { 0x9900, 0x22ca, 0x3000 },
+  { 0x9900, 0x22c8, 0x2000 },
+  { 0x1900, 0x22c7, 0x0000 },
+  { 0x1900, 0x22c9, 0x0000 },
+  { 0x9900, 0x22cc, 0x2000 },
+  { 0x1900, 0x22cb, 0x0000 },
+  { 0x1900, 0x22cd, 0x0000 },
+  { 0x9900, 0x22de, 0x5000 },
+  { 0x9900, 0x22d6, 0x4000 },
+  { 0x9900, 0x22d2, 0x3000 },
+  { 0x9900, 0x22d0, 0x2000 },
+  { 0x1900, 0x22cf, 0x0000 },
+  { 0x1900, 0x22d1, 0x0000 },
+  { 0x9900, 0x22d4, 0x2000 },
+  { 0x1900, 0x22d3, 0x0000 },
+  { 0x1900, 0x22d5, 0x0000 },
+  { 0x9900, 0x22da, 0x3000 },
+  { 0x9900, 0x22d8, 0x2000 },
+  { 0x1900, 0x22d7, 0x0000 },
+  { 0x1900, 0x22d9, 0x0000 },
+  { 0x9900, 0x22dc, 0x2000 },
+  { 0x1900, 0x22db, 0x0000 },
+  { 0x1900, 0x22dd, 0x0000 },
+  { 0x9900, 0x22e6, 0x4000 },
+  { 0x9900, 0x22e2, 0x3000 },
+  { 0x9900, 0x22e0, 0x2000 },
+  { 0x1900, 0x22df, 0x0000 },
+  { 0x1900, 0x22e1, 0x0000 },
+  { 0x9900, 0x22e4, 0x2000 },
+  { 0x1900, 0x22e3, 0x0000 },
+  { 0x1900, 0x22e5, 0x0000 },
+  { 0x9900, 0x22ea, 0x3000 },
+  { 0x9900, 0x22e8, 0x2000 },
+  { 0x1900, 0x22e7, 0x0000 },
+  { 0x1900, 0x22e9, 0x0000 },
+  { 0x9900, 0x22ec, 0x2000 },
+  { 0x1900, 0x22eb, 0x0000 },
+  { 0x1900, 0x22ed, 0x0000 },
+  { 0x9a00, 0x230e, 0x6000 },
+  { 0x9900, 0x22fe, 0x5000 },
+  { 0x9900, 0x22f6, 0x4000 },
+  { 0x9900, 0x22f2, 0x3000 },
+  { 0x9900, 0x22f0, 0x2000 },
+  { 0x1900, 0x22ef, 0x0000 },
+  { 0x1900, 0x22f1, 0x0000 },
+  { 0x9900, 0x22f4, 0x2000 },
+  { 0x1900, 0x22f3, 0x0000 },
+  { 0x1900, 0x22f5, 0x0000 },
+  { 0x9900, 0x22fa, 0x3000 },
+  { 0x9900, 0x22f8, 0x2000 },
+  { 0x1900, 0x22f7, 0x0000 },
+  { 0x1900, 0x22f9, 0x0000 },
+  { 0x9900, 0x22fc, 0x2000 },
+  { 0x1900, 0x22fb, 0x0000 },
+  { 0x1900, 0x22fd, 0x0000 },
+  { 0x9a00, 0x2306, 0x4000 },
+  { 0x9a00, 0x2302, 0x3000 },
+  { 0x9a00, 0x2300, 0x2000 },
+  { 0x1900, 0x22ff, 0x0000 },
+  { 0x1a00, 0x2301, 0x0000 },
+  { 0x9a00, 0x2304, 0x2000 },
+  { 0x1a00, 0x2303, 0x0000 },
+  { 0x1a00, 0x2305, 0x0000 },
+  { 0x9900, 0x230a, 0x3000 },
+  { 0x9900, 0x2308, 0x2000 },
+  { 0x1a00, 0x2307, 0x0000 },
+  { 0x1900, 0x2309, 0x0000 },
+  { 0x9a00, 0x230c, 0x2000 },
+  { 0x1900, 0x230b, 0x0000 },
+  { 0x1a00, 0x230d, 0x0000 },
+  { 0x9a00, 0x231e, 0x5000 },
+  { 0x9a00, 0x2316, 0x4000 },
+  { 0x9a00, 0x2312, 0x3000 },
+  { 0x9a00, 0x2310, 0x2000 },
+  { 0x1a00, 0x230f, 0x0000 },
+  { 0x1a00, 0x2311, 0x0000 },
+  { 0x9a00, 0x2314, 0x2000 },
+  { 0x1a00, 0x2313, 0x0000 },
+  { 0x1a00, 0x2315, 0x0000 },
+  { 0x9a00, 0x231a, 0x3000 },
+  { 0x9a00, 0x2318, 0x2000 },
+  { 0x1a00, 0x2317, 0x0000 },
+  { 0x1a00, 0x2319, 0x0000 },
+  { 0x9a00, 0x231c, 0x2000 },
+  { 0x1a00, 0x231b, 0x0000 },
+  { 0x1a00, 0x231d, 0x0000 },
+  { 0x9a00, 0x2326, 0x4000 },
+  { 0x9a00, 0x2322, 0x3000 },
+  { 0x9900, 0x2320, 0x2000 },
+  { 0x1a00, 0x231f, 0x0000 },
+  { 0x1900, 0x2321, 0x0000 },
+  { 0x9a00, 0x2324, 0x2000 },
+  { 0x1a00, 0x2323, 0x0000 },
+  { 0x1a00, 0x2325, 0x0000 },
+  { 0x9200, 0x232a, 0x3000 },
+  { 0x9a00, 0x2328, 0x2000 },
+  { 0x1a00, 0x2327, 0x0000 },
+  { 0x1600, 0x2329, 0x0000 },
+  { 0x9a00, 0x232c, 0x2000 },
+  { 0x1a00, 0x232b, 0x0000 },
+  { 0x1a00, 0x232d, 0x0000 },
+  { 0x9a00, 0x236e, 0x7000 },
+  { 0x9a00, 0x234e, 0x6000 },
+  { 0x9a00, 0x233e, 0x5000 },
+  { 0x9a00, 0x2336, 0x4000 },
+  { 0x9a00, 0x2332, 0x3000 },
+  { 0x9a00, 0x2330, 0x2000 },
+  { 0x1a00, 0x232f, 0x0000 },
+  { 0x1a00, 0x2331, 0x0000 },
+  { 0x9a00, 0x2334, 0x2000 },
+  { 0x1a00, 0x2333, 0x0000 },
+  { 0x1a00, 0x2335, 0x0000 },
+  { 0x9a00, 0x233a, 0x3000 },
+  { 0x9a00, 0x2338, 0x2000 },
+  { 0x1a00, 0x2337, 0x0000 },
+  { 0x1a00, 0x2339, 0x0000 },
+  { 0x9a00, 0x233c, 0x2000 },
+  { 0x1a00, 0x233b, 0x0000 },
+  { 0x1a00, 0x233d, 0x0000 },
+  { 0x9a00, 0x2346, 0x4000 },
+  { 0x9a00, 0x2342, 0x3000 },
+  { 0x9a00, 0x2340, 0x2000 },
+  { 0x1a00, 0x233f, 0x0000 },
+  { 0x1a00, 0x2341, 0x0000 },
+  { 0x9a00, 0x2344, 0x2000 },
+  { 0x1a00, 0x2343, 0x0000 },
+  { 0x1a00, 0x2345, 0x0000 },
+  { 0x9a00, 0x234a, 0x3000 },
+  { 0x9a00, 0x2348, 0x2000 },
+  { 0x1a00, 0x2347, 0x0000 },
+  { 0x1a00, 0x2349, 0x0000 },
+  { 0x9a00, 0x234c, 0x2000 },
+  { 0x1a00, 0x234b, 0x0000 },
+  { 0x1a00, 0x234d, 0x0000 },
+  { 0x9a00, 0x235e, 0x5000 },
+  { 0x9a00, 0x2356, 0x4000 },
+  { 0x9a00, 0x2352, 0x3000 },
+  { 0x9a00, 0x2350, 0x2000 },
+  { 0x1a00, 0x234f, 0x0000 },
+  { 0x1a00, 0x2351, 0x0000 },
+  { 0x9a00, 0x2354, 0x2000 },
+  { 0x1a00, 0x2353, 0x0000 },
+  { 0x1a00, 0x2355, 0x0000 },
+  { 0x9a00, 0x235a, 0x3000 },
+  { 0x9a00, 0x2358, 0x2000 },
+  { 0x1a00, 0x2357, 0x0000 },
+  { 0x1a00, 0x2359, 0x0000 },
+  { 0x9a00, 0x235c, 0x2000 },
+  { 0x1a00, 0x235b, 0x0000 },
+  { 0x1a00, 0x235d, 0x0000 },
+  { 0x9a00, 0x2366, 0x4000 },
+  { 0x9a00, 0x2362, 0x3000 },
+  { 0x9a00, 0x2360, 0x2000 },
+  { 0x1a00, 0x235f, 0x0000 },
+  { 0x1a00, 0x2361, 0x0000 },
+  { 0x9a00, 0x2364, 0x2000 },
+  { 0x1a00, 0x2363, 0x0000 },
+  { 0x1a00, 0x2365, 0x0000 },
+  { 0x9a00, 0x236a, 0x3000 },
+  { 0x9a00, 0x2368, 0x2000 },
+  { 0x1a00, 0x2367, 0x0000 },
+  { 0x1a00, 0x2369, 0x0000 },
+  { 0x9a00, 0x236c, 0x2000 },
+  { 0x1a00, 0x236b, 0x0000 },
+  { 0x1a00, 0x236d, 0x0000 },
+  { 0x9a00, 0x238e, 0x6000 },
+  { 0x9a00, 0x237e, 0x5000 },
+  { 0x9a00, 0x2376, 0x4000 },
+  { 0x9a00, 0x2372, 0x3000 },
+  { 0x9a00, 0x2370, 0x2000 },
+  { 0x1a00, 0x236f, 0x0000 },
+  { 0x1a00, 0x2371, 0x0000 },
+  { 0x9a00, 0x2374, 0x2000 },
+  { 0x1a00, 0x2373, 0x0000 },
+  { 0x1a00, 0x2375, 0x0000 },
+  { 0x9a00, 0x237a, 0x3000 },
+  { 0x9a00, 0x2378, 0x2000 },
+  { 0x1a00, 0x2377, 0x0000 },
+  { 0x1a00, 0x2379, 0x0000 },
+  { 0x9900, 0x237c, 0x2000 },
+  { 0x1a00, 0x237b, 0x0000 },
+  { 0x1a00, 0x237d, 0x0000 },
+  { 0x9a00, 0x2386, 0x4000 },
+  { 0x9a00, 0x2382, 0x3000 },
+  { 0x9a00, 0x2380, 0x2000 },
+  { 0x1a00, 0x237f, 0x0000 },
+  { 0x1a00, 0x2381, 0x0000 },
+  { 0x9a00, 0x2384, 0x2000 },
+  { 0x1a00, 0x2383, 0x0000 },
+  { 0x1a00, 0x2385, 0x0000 },
+  { 0x9a00, 0x238a, 0x3000 },
+  { 0x9a00, 0x2388, 0x2000 },
+  { 0x1a00, 0x2387, 0x0000 },
+  { 0x1a00, 0x2389, 0x0000 },
+  { 0x9a00, 0x238c, 0x2000 },
+  { 0x1a00, 0x238b, 0x0000 },
+  { 0x1a00, 0x238d, 0x0000 },
+  { 0x9900, 0x239e, 0x5000 },
+  { 0x9a00, 0x2396, 0x4000 },
+  { 0x9a00, 0x2392, 0x3000 },
+  { 0x9a00, 0x2390, 0x2000 },
+  { 0x1a00, 0x238f, 0x0000 },
+  { 0x1a00, 0x2391, 0x0000 },
+  { 0x9a00, 0x2394, 0x2000 },
+  { 0x1a00, 0x2393, 0x0000 },
+  { 0x1a00, 0x2395, 0x0000 },
+  { 0x9a00, 0x239a, 0x3000 },
+  { 0x9a00, 0x2398, 0x2000 },
+  { 0x1a00, 0x2397, 0x0000 },
+  { 0x1a00, 0x2399, 0x0000 },
+  { 0x9900, 0x239c, 0x2000 },
+  { 0x1900, 0x239b, 0x0000 },
+  { 0x1900, 0x239d, 0x0000 },
+  { 0x9900, 0x23a6, 0x4000 },
+  { 0x9900, 0x23a2, 0x3000 },
+  { 0x9900, 0x23a0, 0x2000 },
+  { 0x1900, 0x239f, 0x0000 },
+  { 0x1900, 0x23a1, 0x0000 },
+  { 0x9900, 0x23a4, 0x2000 },
+  { 0x1900, 0x23a3, 0x0000 },
+  { 0x1900, 0x23a5, 0x0000 },
+  { 0x9900, 0x23aa, 0x3000 },
+  { 0x9900, 0x23a8, 0x2000 },
+  { 0x1900, 0x23a7, 0x0000 },
+  { 0x1900, 0x23a9, 0x0000 },
+  { 0x9900, 0x23ac, 0x2000 },
+  { 0x1900, 0x23ab, 0x0000 },
+  { 0x1900, 0x23ad, 0x0000 },
+  { 0x8f00, 0x248b, 0x8000 },
+  { 0x9a00, 0x241d, 0x7000 },
+  { 0x9a00, 0x23ce, 0x6000 },
+  { 0x9a00, 0x23be, 0x5000 },
+  { 0x9500, 0x23b6, 0x4000 },
+  { 0x9900, 0x23b2, 0x3000 },
+  { 0x9900, 0x23b0, 0x2000 },
+  { 0x1900, 0x23af, 0x0000 },
+  { 0x1900, 0x23b1, 0x0000 },
+  { 0x9600, 0x23b4, 0x2000 },
+  { 0x1900, 0x23b3, 0x0000 },
+  { 0x1200, 0x23b5, 0x0000 },
+  { 0x9a00, 0x23ba, 0x3000 },
+  { 0x9a00, 0x23b8, 0x2000 },
+  { 0x1a00, 0x23b7, 0x0000 },
+  { 0x1a00, 0x23b9, 0x0000 },
+  { 0x9a00, 0x23bc, 0x2000 },
+  { 0x1a00, 0x23bb, 0x0000 },
+  { 0x1a00, 0x23bd, 0x0000 },
+  { 0x9a00, 0x23c6, 0x4000 },
+  { 0x9a00, 0x23c2, 0x3000 },
+  { 0x9a00, 0x23c0, 0x2000 },
+  { 0x1a00, 0x23bf, 0x0000 },
+  { 0x1a00, 0x23c1, 0x0000 },
+  { 0x9a00, 0x23c4, 0x2000 },
+  { 0x1a00, 0x23c3, 0x0000 },
+  { 0x1a00, 0x23c5, 0x0000 },
+  { 0x9a00, 0x23ca, 0x3000 },
+  { 0x9a00, 0x23c8, 0x2000 },
+  { 0x1a00, 0x23c7, 0x0000 },
+  { 0x1a00, 0x23c9, 0x0000 },
+  { 0x9a00, 0x23cc, 0x2000 },
+  { 0x1a00, 0x23cb, 0x0000 },
+  { 0x1a00, 0x23cd, 0x0000 },
+  { 0x9a00, 0x240d, 0x5000 },
+  { 0x9a00, 0x2405, 0x4000 },
+  { 0x9a00, 0x2401, 0x3000 },
+  { 0x9a00, 0x23d0, 0x2000 },
+  { 0x1a00, 0x23cf, 0x0000 },
+  { 0x1a00, 0x2400, 0x0000 },
+  { 0x9a00, 0x2403, 0x2000 },
+  { 0x1a00, 0x2402, 0x0000 },
+  { 0x1a00, 0x2404, 0x0000 },
+  { 0x9a00, 0x2409, 0x3000 },
+  { 0x9a00, 0x2407, 0x2000 },
+  { 0x1a00, 0x2406, 0x0000 },
+  { 0x1a00, 0x2408, 0x0000 },
+  { 0x9a00, 0x240b, 0x2000 },
+  { 0x1a00, 0x240a, 0x0000 },
+  { 0x1a00, 0x240c, 0x0000 },
+  { 0x9a00, 0x2415, 0x4000 },
+  { 0x9a00, 0x2411, 0x3000 },
+  { 0x9a00, 0x240f, 0x2000 },
+  { 0x1a00, 0x240e, 0x0000 },
+  { 0x1a00, 0x2410, 0x0000 },
+  { 0x9a00, 0x2413, 0x2000 },
+  { 0x1a00, 0x2412, 0x0000 },
+  { 0x1a00, 0x2414, 0x0000 },
+  { 0x9a00, 0x2419, 0x3000 },
+  { 0x9a00, 0x2417, 0x2000 },
+  { 0x1a00, 0x2416, 0x0000 },
+  { 0x1a00, 0x2418, 0x0000 },
+  { 0x9a00, 0x241b, 0x2000 },
+  { 0x1a00, 0x241a, 0x0000 },
+  { 0x1a00, 0x241c, 0x0000 },
+  { 0x8f00, 0x246b, 0x6000 },
+  { 0x9a00, 0x2446, 0x5000 },
+  { 0x9a00, 0x2425, 0x4000 },
+  { 0x9a00, 0x2421, 0x3000 },
+  { 0x9a00, 0x241f, 0x2000 },
+  { 0x1a00, 0x241e, 0x0000 },
+  { 0x1a00, 0x2420, 0x0000 },
+  { 0x9a00, 0x2423, 0x2000 },
+  { 0x1a00, 0x2422, 0x0000 },
+  { 0x1a00, 0x2424, 0x0000 },
+  { 0x9a00, 0x2442, 0x3000 },
+  { 0x9a00, 0x2440, 0x2000 },
+  { 0x1a00, 0x2426, 0x0000 },
+  { 0x1a00, 0x2441, 0x0000 },
+  { 0x9a00, 0x2444, 0x2000 },
+  { 0x1a00, 0x2443, 0x0000 },
+  { 0x1a00, 0x2445, 0x0000 },
+  { 0x8f00, 0x2463, 0x4000 },
+  { 0x9a00, 0x244a, 0x3000 },
+  { 0x9a00, 0x2448, 0x2000 },
+  { 0x1a00, 0x2447, 0x0000 },
+  { 0x1a00, 0x2449, 0x0000 },
+  { 0x8f00, 0x2461, 0x2000 },
+  { 0x0f00, 0x2460, 0x0000 },
+  { 0x0f00, 0x2462, 0x0000 },
+  { 0x8f00, 0x2467, 0x3000 },
+  { 0x8f00, 0x2465, 0x2000 },
+  { 0x0f00, 0x2464, 0x0000 },
+  { 0x0f00, 0x2466, 0x0000 },
+  { 0x8f00, 0x2469, 0x2000 },
+  { 0x0f00, 0x2468, 0x0000 },
+  { 0x0f00, 0x246a, 0x0000 },
+  { 0x8f00, 0x247b, 0x5000 },
+  { 0x8f00, 0x2473, 0x4000 },
+  { 0x8f00, 0x246f, 0x3000 },
+  { 0x8f00, 0x246d, 0x2000 },
+  { 0x0f00, 0x246c, 0x0000 },
+  { 0x0f00, 0x246e, 0x0000 },
+  { 0x8f00, 0x2471, 0x2000 },
+  { 0x0f00, 0x2470, 0x0000 },
+  { 0x0f00, 0x2472, 0x0000 },
+  { 0x8f00, 0x2477, 0x3000 },
+  { 0x8f00, 0x2475, 0x2000 },
+  { 0x0f00, 0x2474, 0x0000 },
+  { 0x0f00, 0x2476, 0x0000 },
+  { 0x8f00, 0x2479, 0x2000 },
+  { 0x0f00, 0x2478, 0x0000 },
+  { 0x0f00, 0x247a, 0x0000 },
+  { 0x8f00, 0x2483, 0x4000 },
+  { 0x8f00, 0x247f, 0x3000 },
+  { 0x8f00, 0x247d, 0x2000 },
+  { 0x0f00, 0x247c, 0x0000 },
+  { 0x0f00, 0x247e, 0x0000 },
+  { 0x8f00, 0x2481, 0x2000 },
+  { 0x0f00, 0x2480, 0x0000 },
+  { 0x0f00, 0x2482, 0x0000 },
+  { 0x8f00, 0x2487, 0x3000 },
+  { 0x8f00, 0x2485, 0x2000 },
+  { 0x0f00, 0x2484, 0x0000 },
+  { 0x0f00, 0x2486, 0x0000 },
+  { 0x8f00, 0x2489, 0x2000 },
+  { 0x0f00, 0x2488, 0x0000 },
+  { 0x0f00, 0x248a, 0x0000 },
+  { 0x9a00, 0x24cb, 0x7000 },
+  { 0x9a00, 0x24ab, 0x6000 },
+  { 0x8f00, 0x249b, 0x5000 },
+  { 0x8f00, 0x2493, 0x4000 },
+  { 0x8f00, 0x248f, 0x3000 },
+  { 0x8f00, 0x248d, 0x2000 },
+  { 0x0f00, 0x248c, 0x0000 },
+  { 0x0f00, 0x248e, 0x0000 },
+  { 0x8f00, 0x2491, 0x2000 },
+  { 0x0f00, 0x2490, 0x0000 },
+  { 0x0f00, 0x2492, 0x0000 },
+  { 0x8f00, 0x2497, 0x3000 },
+  { 0x8f00, 0x2495, 0x2000 },
+  { 0x0f00, 0x2494, 0x0000 },
+  { 0x0f00, 0x2496, 0x0000 },
+  { 0x8f00, 0x2499, 0x2000 },
+  { 0x0f00, 0x2498, 0x0000 },
+  { 0x0f00, 0x249a, 0x0000 },
+  { 0x9a00, 0x24a3, 0x4000 },
+  { 0x9a00, 0x249f, 0x3000 },
+  { 0x9a00, 0x249d, 0x2000 },
+  { 0x1a00, 0x249c, 0x0000 },
+  { 0x1a00, 0x249e, 0x0000 },
+  { 0x9a00, 0x24a1, 0x2000 },
+  { 0x1a00, 0x24a0, 0x0000 },
+  { 0x1a00, 0x24a2, 0x0000 },
+  { 0x9a00, 0x24a7, 0x3000 },
+  { 0x9a00, 0x24a5, 0x2000 },
+  { 0x1a00, 0x24a4, 0x0000 },
+  { 0x1a00, 0x24a6, 0x0000 },
+  { 0x9a00, 0x24a9, 0x2000 },
+  { 0x1a00, 0x24a8, 0x0000 },
+  { 0x1a00, 0x24aa, 0x0000 },
+  { 0x9a00, 0x24bb, 0x5000 },
+  { 0x9a00, 0x24b3, 0x4000 },
+  { 0x9a00, 0x24af, 0x3000 },
+  { 0x9a00, 0x24ad, 0x2000 },
+  { 0x1a00, 0x24ac, 0x0000 },
+  { 0x1a00, 0x24ae, 0x0000 },
+  { 0x9a00, 0x24b1, 0x2000 },
+  { 0x1a00, 0x24b0, 0x0000 },
+  { 0x1a00, 0x24b2, 0x0000 },
+  { 0x9a00, 0x24b7, 0x3000 },
+  { 0x9a00, 0x24b5, 0x2000 },
+  { 0x1a00, 0x24b4, 0x0000 },
+  { 0x1a00, 0x24b6, 0x0000 },
+  { 0x9a00, 0x24b9, 0x2000 },
+  { 0x1a00, 0x24b8, 0x0000 },
+  { 0x1a00, 0x24ba, 0x0000 },
+  { 0x9a00, 0x24c3, 0x4000 },
+  { 0x9a00, 0x24bf, 0x3000 },
+  { 0x9a00, 0x24bd, 0x2000 },
+  { 0x1a00, 0x24bc, 0x0000 },
+  { 0x1a00, 0x24be, 0x0000 },
+  { 0x9a00, 0x24c1, 0x2000 },
+  { 0x1a00, 0x24c0, 0x0000 },
+  { 0x1a00, 0x24c2, 0x0000 },
+  { 0x9a00, 0x24c7, 0x3000 },
+  { 0x9a00, 0x24c5, 0x2000 },
+  { 0x1a00, 0x24c4, 0x0000 },
+  { 0x1a00, 0x24c6, 0x0000 },
+  { 0x9a00, 0x24c9, 0x2000 },
+  { 0x1a00, 0x24c8, 0x0000 },
+  { 0x1a00, 0x24ca, 0x0000 },
+  { 0x8f00, 0x24eb, 0x6000 },
+  { 0x9a00, 0x24db, 0x5000 },
+  { 0x9a00, 0x24d3, 0x4000 },
+  { 0x9a00, 0x24cf, 0x3000 },
+  { 0x9a00, 0x24cd, 0x2000 },
+  { 0x1a00, 0x24cc, 0x0000 },
+  { 0x1a00, 0x24ce, 0x0000 },
+  { 0x9a00, 0x24d1, 0x2000 },
+  { 0x1a00, 0x24d0, 0x0000 },
+  { 0x1a00, 0x24d2, 0x0000 },
+  { 0x9a00, 0x24d7, 0x3000 },
+  { 0x9a00, 0x24d5, 0x2000 },
+  { 0x1a00, 0x24d4, 0x0000 },
+  { 0x1a00, 0x24d6, 0x0000 },
+  { 0x9a00, 0x24d9, 0x2000 },
+  { 0x1a00, 0x24d8, 0x0000 },
+  { 0x1a00, 0x24da, 0x0000 },
+  { 0x9a00, 0x24e3, 0x4000 },
+  { 0x9a00, 0x24df, 0x3000 },
+  { 0x9a00, 0x24dd, 0x2000 },
+  { 0x1a00, 0x24dc, 0x0000 },
+  { 0x1a00, 0x24de, 0x0000 },
+  { 0x9a00, 0x24e1, 0x2000 },
+  { 0x1a00, 0x24e0, 0x0000 },
+  { 0x1a00, 0x24e2, 0x0000 },
+  { 0x9a00, 0x24e7, 0x3000 },
+  { 0x9a00, 0x24e5, 0x2000 },
+  { 0x1a00, 0x24e4, 0x0000 },
+  { 0x1a00, 0x24e6, 0x0000 },
+  { 0x9a00, 0x24e9, 0x2000 },
+  { 0x1a00, 0x24e8, 0x0000 },
+  { 0x0f00, 0x24ea, 0x0000 },
+  { 0x8f00, 0x24fb, 0x5000 },
+  { 0x8f00, 0x24f3, 0x4000 },
+  { 0x8f00, 0x24ef, 0x3000 },
+  { 0x8f00, 0x24ed, 0x2000 },
+  { 0x0f00, 0x24ec, 0x0000 },
+  { 0x0f00, 0x24ee, 0x0000 },
+  { 0x8f00, 0x24f1, 0x2000 },
+  { 0x0f00, 0x24f0, 0x0000 },
+  { 0x0f00, 0x24f2, 0x0000 },
+  { 0x8f00, 0x24f7, 0x3000 },
+  { 0x8f00, 0x24f5, 0x2000 },
+  { 0x0f00, 0x24f4, 0x0000 },
+  { 0x0f00, 0x24f6, 0x0000 },
+  { 0x8f00, 0x24f9, 0x2000 },
+  { 0x0f00, 0x24f8, 0x0000 },
+  { 0x0f00, 0x24fa, 0x0000 },
+  { 0x9a00, 0x2503, 0x4000 },
+  { 0x8f00, 0x24ff, 0x3000 },
+  { 0x8f00, 0x24fd, 0x2000 },
+  { 0x0f00, 0x24fc, 0x0000 },
+  { 0x0f00, 0x24fe, 0x0000 },
+  { 0x9a00, 0x2501, 0x2000 },
+  { 0x1a00, 0x2500, 0x0000 },
+  { 0x1a00, 0x2502, 0x0000 },
+  { 0x9a00, 0x2507, 0x3000 },
+  { 0x9a00, 0x2505, 0x2000 },
+  { 0x1a00, 0x2504, 0x0000 },
+  { 0x1a00, 0x2506, 0x0000 },
+  { 0x9a00, 0x2509, 0x2000 },
+  { 0x1a00, 0x2508, 0x0000 },
+  { 0x1a00, 0x250a, 0x0000 },
+  { 0x9a00, 0x260b, 0x9000 },
+  { 0x9a00, 0x258b, 0x8000 },
+  { 0x9a00, 0x254b, 0x7000 },
+  { 0x9a00, 0x252b, 0x6000 },
+  { 0x9a00, 0x251b, 0x5000 },
+  { 0x9a00, 0x2513, 0x4000 },
+  { 0x9a00, 0x250f, 0x3000 },
+  { 0x9a00, 0x250d, 0x2000 },
+  { 0x1a00, 0x250c, 0x0000 },
+  { 0x1a00, 0x250e, 0x0000 },
+  { 0x9a00, 0x2511, 0x2000 },
+  { 0x1a00, 0x2510, 0x0000 },
+  { 0x1a00, 0x2512, 0x0000 },
+  { 0x9a00, 0x2517, 0x3000 },
+  { 0x9a00, 0x2515, 0x2000 },
+  { 0x1a00, 0x2514, 0x0000 },
+  { 0x1a00, 0x2516, 0x0000 },
+  { 0x9a00, 0x2519, 0x2000 },
+  { 0x1a00, 0x2518, 0x0000 },
+  { 0x1a00, 0x251a, 0x0000 },
+  { 0x9a00, 0x2523, 0x4000 },
+  { 0x9a00, 0x251f, 0x3000 },
+  { 0x9a00, 0x251d, 0x2000 },
+  { 0x1a00, 0x251c, 0x0000 },
+  { 0x1a00, 0x251e, 0x0000 },
+  { 0x9a00, 0x2521, 0x2000 },
+  { 0x1a00, 0x2520, 0x0000 },
+  { 0x1a00, 0x2522, 0x0000 },
+  { 0x9a00, 0x2527, 0x3000 },
+  { 0x9a00, 0x2525, 0x2000 },
+  { 0x1a00, 0x2524, 0x0000 },
+  { 0x1a00, 0x2526, 0x0000 },
+  { 0x9a00, 0x2529, 0x2000 },
+  { 0x1a00, 0x2528, 0x0000 },
+  { 0x1a00, 0x252a, 0x0000 },
+  { 0x9a00, 0x253b, 0x5000 },
+  { 0x9a00, 0x2533, 0x4000 },
+  { 0x9a00, 0x252f, 0x3000 },
+  { 0x9a00, 0x252d, 0x2000 },
+  { 0x1a00, 0x252c, 0x0000 },
+  { 0x1a00, 0x252e, 0x0000 },
+  { 0x9a00, 0x2531, 0x2000 },
+  { 0x1a00, 0x2530, 0x0000 },
+  { 0x1a00, 0x2532, 0x0000 },
+  { 0x9a00, 0x2537, 0x3000 },
+  { 0x9a00, 0x2535, 0x2000 },
+  { 0x1a00, 0x2534, 0x0000 },
+  { 0x1a00, 0x2536, 0x0000 },
+  { 0x9a00, 0x2539, 0x2000 },
+  { 0x1a00, 0x2538, 0x0000 },
+  { 0x1a00, 0x253a, 0x0000 },
+  { 0x9a00, 0x2543, 0x4000 },
+  { 0x9a00, 0x253f, 0x3000 },
+  { 0x9a00, 0x253d, 0x2000 },
+  { 0x1a00, 0x253c, 0x0000 },
+  { 0x1a00, 0x253e, 0x0000 },
+  { 0x9a00, 0x2541, 0x2000 },
+  { 0x1a00, 0x2540, 0x0000 },
+  { 0x1a00, 0x2542, 0x0000 },
+  { 0x9a00, 0x2547, 0x3000 },
+  { 0x9a00, 0x2545, 0x2000 },
+  { 0x1a00, 0x2544, 0x0000 },
+  { 0x1a00, 0x2546, 0x0000 },
+  { 0x9a00, 0x2549, 0x2000 },
+  { 0x1a00, 0x2548, 0x0000 },
+  { 0x1a00, 0x254a, 0x0000 },
+  { 0x9a00, 0x256b, 0x6000 },
+  { 0x9a00, 0x255b, 0x5000 },
+  { 0x9a00, 0x2553, 0x4000 },
+  { 0x9a00, 0x254f, 0x3000 },
+  { 0x9a00, 0x254d, 0x2000 },
+  { 0x1a00, 0x254c, 0x0000 },
+  { 0x1a00, 0x254e, 0x0000 },
+  { 0x9a00, 0x2551, 0x2000 },
+  { 0x1a00, 0x2550, 0x0000 },
+  { 0x1a00, 0x2552, 0x0000 },
+  { 0x9a00, 0x2557, 0x3000 },
+  { 0x9a00, 0x2555, 0x2000 },
+  { 0x1a00, 0x2554, 0x0000 },
+  { 0x1a00, 0x2556, 0x0000 },
+  { 0x9a00, 0x2559, 0x2000 },
+  { 0x1a00, 0x2558, 0x0000 },
+  { 0x1a00, 0x255a, 0x0000 },
+  { 0x9a00, 0x2563, 0x4000 },
+  { 0x9a00, 0x255f, 0x3000 },
+  { 0x9a00, 0x255d, 0x2000 },
+  { 0x1a00, 0x255c, 0x0000 },
+  { 0x1a00, 0x255e, 0x0000 },
+  { 0x9a00, 0x2561, 0x2000 },
+  { 0x1a00, 0x2560, 0x0000 },
+  { 0x1a00, 0x2562, 0x0000 },
+  { 0x9a00, 0x2567, 0x3000 },
+  { 0x9a00, 0x2565, 0x2000 },
+  { 0x1a00, 0x2564, 0x0000 },
+  { 0x1a00, 0x2566, 0x0000 },
+  { 0x9a00, 0x2569, 0x2000 },
+  { 0x1a00, 0x2568, 0x0000 },
+  { 0x1a00, 0x256a, 0x0000 },
+  { 0x9a00, 0x257b, 0x5000 },
+  { 0x9a00, 0x2573, 0x4000 },
+  { 0x9a00, 0x256f, 0x3000 },
+  { 0x9a00, 0x256d, 0x2000 },
+  { 0x1a00, 0x256c, 0x0000 },
+  { 0x1a00, 0x256e, 0x0000 },
+  { 0x9a00, 0x2571, 0x2000 },
+  { 0x1a00, 0x2570, 0x0000 },
+  { 0x1a00, 0x2572, 0x0000 },
+  { 0x9a00, 0x2577, 0x3000 },
+  { 0x9a00, 0x2575, 0x2000 },
+  { 0x1a00, 0x2574, 0x0000 },
+  { 0x1a00, 0x2576, 0x0000 },
+  { 0x9a00, 0x2579, 0x2000 },
+  { 0x1a00, 0x2578, 0x0000 },
+  { 0x1a00, 0x257a, 0x0000 },
+  { 0x9a00, 0x2583, 0x4000 },
+  { 0x9a00, 0x257f, 0x3000 },
+  { 0x9a00, 0x257d, 0x2000 },
+  { 0x1a00, 0x257c, 0x0000 },
+  { 0x1a00, 0x257e, 0x0000 },
+  { 0x9a00, 0x2581, 0x2000 },
+  { 0x1a00, 0x2580, 0x0000 },
+  { 0x1a00, 0x2582, 0x0000 },
+  { 0x9a00, 0x2587, 0x3000 },
+  { 0x9a00, 0x2585, 0x2000 },
+  { 0x1a00, 0x2584, 0x0000 },
+  { 0x1a00, 0x2586, 0x0000 },
+  { 0x9a00, 0x2589, 0x2000 },
+  { 0x1a00, 0x2588, 0x0000 },
+  { 0x1a00, 0x258a, 0x0000 },
+  { 0x9a00, 0x25cb, 0x7000 },
+  { 0x9a00, 0x25ab, 0x6000 },
+  { 0x9a00, 0x259b, 0x5000 },
+  { 0x9a00, 0x2593, 0x4000 },
+  { 0x9a00, 0x258f, 0x3000 },
+  { 0x9a00, 0x258d, 0x2000 },
+  { 0x1a00, 0x258c, 0x0000 },
+  { 0x1a00, 0x258e, 0x0000 },
+  { 0x9a00, 0x2591, 0x2000 },
+  { 0x1a00, 0x2590, 0x0000 },
+  { 0x1a00, 0x2592, 0x0000 },
+  { 0x9a00, 0x2597, 0x3000 },
+  { 0x9a00, 0x2595, 0x2000 },
+  { 0x1a00, 0x2594, 0x0000 },
+  { 0x1a00, 0x2596, 0x0000 },
+  { 0x9a00, 0x2599, 0x2000 },
+  { 0x1a00, 0x2598, 0x0000 },
+  { 0x1a00, 0x259a, 0x0000 },
+  { 0x9a00, 0x25a3, 0x4000 },
+  { 0x9a00, 0x259f, 0x3000 },
+  { 0x9a00, 0x259d, 0x2000 },
+  { 0x1a00, 0x259c, 0x0000 },
+  { 0x1a00, 0x259e, 0x0000 },
+  { 0x9a00, 0x25a1, 0x2000 },
+  { 0x1a00, 0x25a0, 0x0000 },
+  { 0x1a00, 0x25a2, 0x0000 },
+  { 0x9a00, 0x25a7, 0x3000 },
+  { 0x9a00, 0x25a5, 0x2000 },
+  { 0x1a00, 0x25a4, 0x0000 },
+  { 0x1a00, 0x25a6, 0x0000 },
+  { 0x9a00, 0x25a9, 0x2000 },
+  { 0x1a00, 0x25a8, 0x0000 },
+  { 0x1a00, 0x25aa, 0x0000 },
+  { 0x9a00, 0x25bb, 0x5000 },
+  { 0x9a00, 0x25b3, 0x4000 },
+  { 0x9a00, 0x25af, 0x3000 },
+  { 0x9a00, 0x25ad, 0x2000 },
+  { 0x1a00, 0x25ac, 0x0000 },
+  { 0x1a00, 0x25ae, 0x0000 },
+  { 0x9a00, 0x25b1, 0x2000 },
+  { 0x1a00, 0x25b0, 0x0000 },
+  { 0x1a00, 0x25b2, 0x0000 },
+  { 0x9900, 0x25b7, 0x3000 },
+  { 0x9a00, 0x25b5, 0x2000 },
+  { 0x1a00, 0x25b4, 0x0000 },
+  { 0x1a00, 0x25b6, 0x0000 },
+  { 0x9a00, 0x25b9, 0x2000 },
+  { 0x1a00, 0x25b8, 0x0000 },
+  { 0x1a00, 0x25ba, 0x0000 },
+  { 0x9a00, 0x25c3, 0x4000 },
+  { 0x9a00, 0x25bf, 0x3000 },
+  { 0x9a00, 0x25bd, 0x2000 },
+  { 0x1a00, 0x25bc, 0x0000 },
+  { 0x1a00, 0x25be, 0x0000 },
+  { 0x9900, 0x25c1, 0x2000 },
+  { 0x1a00, 0x25c0, 0x0000 },
+  { 0x1a00, 0x25c2, 0x0000 },
+  { 0x9a00, 0x25c7, 0x3000 },
+  { 0x9a00, 0x25c5, 0x2000 },
+  { 0x1a00, 0x25c4, 0x0000 },
+  { 0x1a00, 0x25c6, 0x0000 },
+  { 0x9a00, 0x25c9, 0x2000 },
+  { 0x1a00, 0x25c8, 0x0000 },
+  { 0x1a00, 0x25ca, 0x0000 },
+  { 0x9a00, 0x25eb, 0x6000 },
+  { 0x9a00, 0x25db, 0x5000 },
+  { 0x9a00, 0x25d3, 0x4000 },
+  { 0x9a00, 0x25cf, 0x3000 },
+  { 0x9a00, 0x25cd, 0x2000 },
+  { 0x1a00, 0x25cc, 0x0000 },
+  { 0x1a00, 0x25ce, 0x0000 },
+  { 0x9a00, 0x25d1, 0x2000 },
+  { 0x1a00, 0x25d0, 0x0000 },
+  { 0x1a00, 0x25d2, 0x0000 },
+  { 0x9a00, 0x25d7, 0x3000 },
+  { 0x9a00, 0x25d5, 0x2000 },
+  { 0x1a00, 0x25d4, 0x0000 },
+  { 0x1a00, 0x25d6, 0x0000 },
+  { 0x9a00, 0x25d9, 0x2000 },
+  { 0x1a00, 0x25d8, 0x0000 },
+  { 0x1a00, 0x25da, 0x0000 },
+  { 0x9a00, 0x25e3, 0x4000 },
+  { 0x9a00, 0x25df, 0x3000 },
+  { 0x9a00, 0x25dd, 0x2000 },
+  { 0x1a00, 0x25dc, 0x0000 },
+  { 0x1a00, 0x25de, 0x0000 },
+  { 0x9a00, 0x25e1, 0x2000 },
+  { 0x1a00, 0x25e0, 0x0000 },
+  { 0x1a00, 0x25e2, 0x0000 },
+  { 0x9a00, 0x25e7, 0x3000 },
+  { 0x9a00, 0x25e5, 0x2000 },
+  { 0x1a00, 0x25e4, 0x0000 },
+  { 0x1a00, 0x25e6, 0x0000 },
+  { 0x9a00, 0x25e9, 0x2000 },
+  { 0x1a00, 0x25e8, 0x0000 },
+  { 0x1a00, 0x25ea, 0x0000 },
+  { 0x9900, 0x25fb, 0x5000 },
+  { 0x9a00, 0x25f3, 0x4000 },
+  { 0x9a00, 0x25ef, 0x3000 },
+  { 0x9a00, 0x25ed, 0x2000 },
+  { 0x1a00, 0x25ec, 0x0000 },
+  { 0x1a00, 0x25ee, 0x0000 },
+  { 0x9a00, 0x25f1, 0x2000 },
+  { 0x1a00, 0x25f0, 0x0000 },
+  { 0x1a00, 0x25f2, 0x0000 },
+  { 0x9a00, 0x25f7, 0x3000 },
+  { 0x9a00, 0x25f5, 0x2000 },
+  { 0x1a00, 0x25f4, 0x0000 },
+  { 0x1a00, 0x25f6, 0x0000 },
+  { 0x9900, 0x25f9, 0x2000 },
+  { 0x1900, 0x25f8, 0x0000 },
+  { 0x1900, 0x25fa, 0x0000 },
+  { 0x9a00, 0x2603, 0x4000 },
+  { 0x9900, 0x25ff, 0x3000 },
+  { 0x9900, 0x25fd, 0x2000 },
+  { 0x1900, 0x25fc, 0x0000 },
+  { 0x1900, 0x25fe, 0x0000 },
+  { 0x9a00, 0x2601, 0x2000 },
+  { 0x1a00, 0x2600, 0x0000 },
+  { 0x1a00, 0x2602, 0x0000 },
+  { 0x9a00, 0x2607, 0x3000 },
+  { 0x9a00, 0x2605, 0x2000 },
+  { 0x1a00, 0x2604, 0x0000 },
+  { 0x1a00, 0x2606, 0x0000 },
+  { 0x9a00, 0x2609, 0x2000 },
+  { 0x1a00, 0x2608, 0x0000 },
+  { 0x1a00, 0x260a, 0x0000 },
+  { 0x9a00, 0x268e, 0x8000 },
+  { 0x9a00, 0x264c, 0x7000 },
+  { 0x9a00, 0x262c, 0x6000 },
+  { 0x9a00, 0x261c, 0x5000 },
+  { 0x9a00, 0x2613, 0x4000 },
+  { 0x9a00, 0x260f, 0x3000 },
+  { 0x9a00, 0x260d, 0x2000 },
+  { 0x1a00, 0x260c, 0x0000 },
+  { 0x1a00, 0x260e, 0x0000 },
+  { 0x9a00, 0x2611, 0x2000 },
+  { 0x1a00, 0x2610, 0x0000 },
+  { 0x1a00, 0x2612, 0x0000 },
+  { 0x9a00, 0x2617, 0x3000 },
+  { 0x9a00, 0x2615, 0x2000 },
+  { 0x1a00, 0x2614, 0x0000 },
+  { 0x1a00, 0x2616, 0x0000 },
+  { 0x9a00, 0x261a, 0x2000 },
+  { 0x1a00, 0x2619, 0x0000 },
+  { 0x1a00, 0x261b, 0x0000 },
+  { 0x9a00, 0x2624, 0x4000 },
+  { 0x9a00, 0x2620, 0x3000 },
+  { 0x9a00, 0x261e, 0x2000 },
+  { 0x1a00, 0x261d, 0x0000 },
+  { 0x1a00, 0x261f, 0x0000 },
+  { 0x9a00, 0x2622, 0x2000 },
+  { 0x1a00, 0x2621, 0x0000 },
+  { 0x1a00, 0x2623, 0x0000 },
+  { 0x9a00, 0x2628, 0x3000 },
+  { 0x9a00, 0x2626, 0x2000 },
+  { 0x1a00, 0x2625, 0x0000 },
+  { 0x1a00, 0x2627, 0x0000 },
+  { 0x9a00, 0x262a, 0x2000 },
+  { 0x1a00, 0x2629, 0x0000 },
+  { 0x1a00, 0x262b, 0x0000 },
+  { 0x9a00, 0x263c, 0x5000 },
+  { 0x9a00, 0x2634, 0x4000 },
+  { 0x9a00, 0x2630, 0x3000 },
+  { 0x9a00, 0x262e, 0x2000 },
+  { 0x1a00, 0x262d, 0x0000 },
+  { 0x1a00, 0x262f, 0x0000 },
+  { 0x9a00, 0x2632, 0x2000 },
+  { 0x1a00, 0x2631, 0x0000 },
+  { 0x1a00, 0x2633, 0x0000 },
+  { 0x9a00, 0x2638, 0x3000 },
+  { 0x9a00, 0x2636, 0x2000 },
+  { 0x1a00, 0x2635, 0x0000 },
+  { 0x1a00, 0x2637, 0x0000 },
+  { 0x9a00, 0x263a, 0x2000 },
+  { 0x1a00, 0x2639, 0x0000 },
+  { 0x1a00, 0x263b, 0x0000 },
+  { 0x9a00, 0x2644, 0x4000 },
+  { 0x9a00, 0x2640, 0x3000 },
+  { 0x9a00, 0x263e, 0x2000 },
+  { 0x1a00, 0x263d, 0x0000 },
+  { 0x1a00, 0x263f, 0x0000 },
+  { 0x9a00, 0x2642, 0x2000 },
+  { 0x1a00, 0x2641, 0x0000 },
+  { 0x1a00, 0x2643, 0x0000 },
+  { 0x9a00, 0x2648, 0x3000 },
+  { 0x9a00, 0x2646, 0x2000 },
+  { 0x1a00, 0x2645, 0x0000 },
+  { 0x1a00, 0x2647, 0x0000 },
+  { 0x9a00, 0x264a, 0x2000 },
+  { 0x1a00, 0x2649, 0x0000 },
+  { 0x1a00, 0x264b, 0x0000 },
+  { 0x9a00, 0x266c, 0x6000 },
+  { 0x9a00, 0x265c, 0x5000 },
+  { 0x9a00, 0x2654, 0x4000 },
+  { 0x9a00, 0x2650, 0x3000 },
+  { 0x9a00, 0x264e, 0x2000 },
+  { 0x1a00, 0x264d, 0x0000 },
+  { 0x1a00, 0x264f, 0x0000 },
+  { 0x9a00, 0x2652, 0x2000 },
+  { 0x1a00, 0x2651, 0x0000 },
+  { 0x1a00, 0x2653, 0x0000 },
+  { 0x9a00, 0x2658, 0x3000 },
+  { 0x9a00, 0x2656, 0x2000 },
+  { 0x1a00, 0x2655, 0x0000 },
+  { 0x1a00, 0x2657, 0x0000 },
+  { 0x9a00, 0x265a, 0x2000 },
+  { 0x1a00, 0x2659, 0x0000 },
+  { 0x1a00, 0x265b, 0x0000 },
+  { 0x9a00, 0x2664, 0x4000 },
+  { 0x9a00, 0x2660, 0x3000 },
+  { 0x9a00, 0x265e, 0x2000 },
+  { 0x1a00, 0x265d, 0x0000 },
+  { 0x1a00, 0x265f, 0x0000 },
+  { 0x9a00, 0x2662, 0x2000 },
+  { 0x1a00, 0x2661, 0x0000 },
+  { 0x1a00, 0x2663, 0x0000 },
+  { 0x9a00, 0x2668, 0x3000 },
+  { 0x9a00, 0x2666, 0x2000 },
+  { 0x1a00, 0x2665, 0x0000 },
+  { 0x1a00, 0x2667, 0x0000 },
+  { 0x9a00, 0x266a, 0x2000 },
+  { 0x1a00, 0x2669, 0x0000 },
+  { 0x1a00, 0x266b, 0x0000 },
+  { 0x9a00, 0x267c, 0x5000 },
+  { 0x9a00, 0x2674, 0x4000 },
+  { 0x9a00, 0x2670, 0x3000 },
+  { 0x9a00, 0x266e, 0x2000 },
+  { 0x1a00, 0x266d, 0x0000 },
+  { 0x1900, 0x266f, 0x0000 },
+  { 0x9a00, 0x2672, 0x2000 },
+  { 0x1a00, 0x2671, 0x0000 },
+  { 0x1a00, 0x2673, 0x0000 },
+  { 0x9a00, 0x2678, 0x3000 },
+  { 0x9a00, 0x2676, 0x2000 },
+  { 0x1a00, 0x2675, 0x0000 },
+  { 0x1a00, 0x2677, 0x0000 },
+  { 0x9a00, 0x267a, 0x2000 },
+  { 0x1a00, 0x2679, 0x0000 },
+  { 0x1a00, 0x267b, 0x0000 },
+  { 0x9a00, 0x2686, 0x4000 },
+  { 0x9a00, 0x2682, 0x3000 },
+  { 0x9a00, 0x2680, 0x2000 },
+  { 0x1a00, 0x267d, 0x0000 },
+  { 0x1a00, 0x2681, 0x0000 },
+  { 0x9a00, 0x2684, 0x2000 },
+  { 0x1a00, 0x2683, 0x0000 },
+  { 0x1a00, 0x2685, 0x0000 },
+  { 0x9a00, 0x268a, 0x3000 },
+  { 0x9a00, 0x2688, 0x2000 },
+  { 0x1a00, 0x2687, 0x0000 },
+  { 0x1a00, 0x2689, 0x0000 },
+  { 0x9a00, 0x268c, 0x2000 },
+  { 0x1a00, 0x268b, 0x0000 },
+  { 0x1a00, 0x268d, 0x0000 },
+  { 0x9a00, 0x273f, 0x7000 },
+  { 0x9a00, 0x271e, 0x6000 },
+  { 0x9a00, 0x270e, 0x5000 },
+  { 0x9a00, 0x2703, 0x4000 },
+  { 0x9a00, 0x26a0, 0x3000 },
+  { 0x9a00, 0x2690, 0x2000 },
+  { 0x1a00, 0x268f, 0x0000 },
+  { 0x1a00, 0x2691, 0x0000 },
+  { 0x9a00, 0x2701, 0x2000 },
+  { 0x1a00, 0x26a1, 0x0000 },
+  { 0x1a00, 0x2702, 0x0000 },
+  { 0x9a00, 0x2708, 0x3000 },
+  { 0x9a00, 0x2706, 0x2000 },
+  { 0x1a00, 0x2704, 0x0000 },
+  { 0x1a00, 0x2707, 0x0000 },
+  { 0x9a00, 0x270c, 0x2000 },
+  { 0x1a00, 0x2709, 0x0000 },
+  { 0x1a00, 0x270d, 0x0000 },
+  { 0x9a00, 0x2716, 0x4000 },
+  { 0x9a00, 0x2712, 0x3000 },
+  { 0x9a00, 0x2710, 0x2000 },
+  { 0x1a00, 0x270f, 0x0000 },
+  { 0x1a00, 0x2711, 0x0000 },
+  { 0x9a00, 0x2714, 0x2000 },
+  { 0x1a00, 0x2713, 0x0000 },
+  { 0x1a00, 0x2715, 0x0000 },
+  { 0x9a00, 0x271a, 0x3000 },
+  { 0x9a00, 0x2718, 0x2000 },
+  { 0x1a00, 0x2717, 0x0000 },
+  { 0x1a00, 0x2719, 0x0000 },
+  { 0x9a00, 0x271c, 0x2000 },
+  { 0x1a00, 0x271b, 0x0000 },
+  { 0x1a00, 0x271d, 0x0000 },
+  { 0x9a00, 0x272f, 0x5000 },
+  { 0x9a00, 0x2726, 0x4000 },
+  { 0x9a00, 0x2722, 0x3000 },
+  { 0x9a00, 0x2720, 0x2000 },
+  { 0x1a00, 0x271f, 0x0000 },
+  { 0x1a00, 0x2721, 0x0000 },
+  { 0x9a00, 0x2724, 0x2000 },
+  { 0x1a00, 0x2723, 0x0000 },
+  { 0x1a00, 0x2725, 0x0000 },
+  { 0x9a00, 0x272b, 0x3000 },
+  { 0x9a00, 0x2729, 0x2000 },
+  { 0x1a00, 0x2727, 0x0000 },
+  { 0x1a00, 0x272a, 0x0000 },
+  { 0x9a00, 0x272d, 0x2000 },
+  { 0x1a00, 0x272c, 0x0000 },
+  { 0x1a00, 0x272e, 0x0000 },
+  { 0x9a00, 0x2737, 0x4000 },
+  { 0x9a00, 0x2733, 0x3000 },
+  { 0x9a00, 0x2731, 0x2000 },
+  { 0x1a00, 0x2730, 0x0000 },
+  { 0x1a00, 0x2732, 0x0000 },
+  { 0x9a00, 0x2735, 0x2000 },
+  { 0x1a00, 0x2734, 0x0000 },
+  { 0x1a00, 0x2736, 0x0000 },
+  { 0x9a00, 0x273b, 0x3000 },
+  { 0x9a00, 0x2739, 0x2000 },
+  { 0x1a00, 0x2738, 0x0000 },
+  { 0x1a00, 0x273a, 0x0000 },
+  { 0x9a00, 0x273d, 0x2000 },
+  { 0x1a00, 0x273c, 0x0000 },
+  { 0x1a00, 0x273e, 0x0000 },
+  { 0x9a00, 0x2767, 0x6000 },
+  { 0x9a00, 0x2751, 0x5000 },
+  { 0x9a00, 0x2747, 0x4000 },
+  { 0x9a00, 0x2743, 0x3000 },
+  { 0x9a00, 0x2741, 0x2000 },
+  { 0x1a00, 0x2740, 0x0000 },
+  { 0x1a00, 0x2742, 0x0000 },
+  { 0x9a00, 0x2745, 0x2000 },
+  { 0x1a00, 0x2744, 0x0000 },
+  { 0x1a00, 0x2746, 0x0000 },
+  { 0x9a00, 0x274b, 0x3000 },
+  { 0x9a00, 0x2749, 0x2000 },
+  { 0x1a00, 0x2748, 0x0000 },
+  { 0x1a00, 0x274a, 0x0000 },
+  { 0x9a00, 0x274f, 0x2000 },
+  { 0x1a00, 0x274d, 0x0000 },
+  { 0x1a00, 0x2750, 0x0000 },
+  { 0x9a00, 0x275d, 0x4000 },
+  { 0x9a00, 0x2759, 0x3000 },
+  { 0x9a00, 0x2756, 0x2000 },
+  { 0x1a00, 0x2752, 0x0000 },
+  { 0x1a00, 0x2758, 0x0000 },
+  { 0x9a00, 0x275b, 0x2000 },
+  { 0x1a00, 0x275a, 0x0000 },
+  { 0x1a00, 0x275c, 0x0000 },
+  { 0x9a00, 0x2763, 0x3000 },
+  { 0x9a00, 0x2761, 0x2000 },
+  { 0x1a00, 0x275e, 0x0000 },
+  { 0x1a00, 0x2762, 0x0000 },
+  { 0x9a00, 0x2765, 0x2000 },
+  { 0x1a00, 0x2764, 0x0000 },
+  { 0x1a00, 0x2766, 0x0000 },
+  { 0x8f00, 0x2777, 0x5000 },
+  { 0x9200, 0x276f, 0x4000 },
+  { 0x9200, 0x276b, 0x3000 },
+  { 0x9200, 0x2769, 0x2000 },
+  { 0x1600, 0x2768, 0x0000 },
+  { 0x1600, 0x276a, 0x0000 },
+  { 0x9200, 0x276d, 0x2000 },
+  { 0x1600, 0x276c, 0x0000 },
+  { 0x1600, 0x276e, 0x0000 },
+  { 0x9200, 0x2773, 0x3000 },
+  { 0x9200, 0x2771, 0x2000 },
+  { 0x1600, 0x2770, 0x0000 },
+  { 0x1600, 0x2772, 0x0000 },
+  { 0x9200, 0x2775, 0x2000 },
+  { 0x1600, 0x2774, 0x0000 },
+  { 0x0f00, 0x2776, 0x0000 },
+  { 0x8f00, 0x277f, 0x4000 },
+  { 0x8f00, 0x277b, 0x3000 },
+  { 0x8f00, 0x2779, 0x2000 },
+  { 0x0f00, 0x2778, 0x0000 },
+  { 0x0f00, 0x277a, 0x0000 },
+  { 0x8f00, 0x277d, 0x2000 },
+  { 0x0f00, 0x277c, 0x0000 },
+  { 0x0f00, 0x277e, 0x0000 },
+  { 0x8f00, 0x2783, 0x3000 },
+  { 0x8f00, 0x2781, 0x2000 },
+  { 0x0f00, 0x2780, 0x0000 },
+  { 0x0f00, 0x2782, 0x0000 },
+  { 0x8f00, 0x2785, 0x2000 },
+  { 0x0f00, 0x2784, 0x0000 },
+  { 0x0f00, 0x2786, 0x0000 },
+  { 0x9900, 0x29a0, 0xa000 },
+  { 0x9a00, 0x28a0, 0x9000 },
+  { 0x9a00, 0x2820, 0x8000 },
+  { 0x9900, 0x27dc, 0x7000 },
+  { 0x9a00, 0x27aa, 0x6000 },
+  { 0x9a00, 0x279a, 0x5000 },
+  { 0x8f00, 0x278f, 0x4000 },
+  { 0x8f00, 0x278b, 0x3000 },
+  { 0x8f00, 0x2789, 0x2000 },
+  { 0x0f00, 0x2788, 0x0000 },
+  { 0x0f00, 0x278a, 0x0000 },
+  { 0x8f00, 0x278d, 0x2000 },
+  { 0x0f00, 0x278c, 0x0000 },
+  { 0x0f00, 0x278e, 0x0000 },
+  { 0x8f00, 0x2793, 0x3000 },
+  { 0x8f00, 0x2791, 0x2000 },
+  { 0x0f00, 0x2790, 0x0000 },
+  { 0x0f00, 0x2792, 0x0000 },
+  { 0x9a00, 0x2798, 0x2000 },
+  { 0x1a00, 0x2794, 0x0000 },
+  { 0x1a00, 0x2799, 0x0000 },
+  { 0x9a00, 0x27a2, 0x4000 },
+  { 0x9a00, 0x279e, 0x3000 },
+  { 0x9a00, 0x279c, 0x2000 },
+  { 0x1a00, 0x279b, 0x0000 },
+  { 0x1a00, 0x279d, 0x0000 },
+  { 0x9a00, 0x27a0, 0x2000 },
+  { 0x1a00, 0x279f, 0x0000 },
+  { 0x1a00, 0x27a1, 0x0000 },
+  { 0x9a00, 0x27a6, 0x3000 },
+  { 0x9a00, 0x27a4, 0x2000 },
+  { 0x1a00, 0x27a3, 0x0000 },
+  { 0x1a00, 0x27a5, 0x0000 },
+  { 0x9a00, 0x27a8, 0x2000 },
+  { 0x1a00, 0x27a7, 0x0000 },
+  { 0x1a00, 0x27a9, 0x0000 },
+  { 0x9a00, 0x27bb, 0x5000 },
+  { 0x9a00, 0x27b3, 0x4000 },
+  { 0x9a00, 0x27ae, 0x3000 },
+  { 0x9a00, 0x27ac, 0x2000 },
+  { 0x1a00, 0x27ab, 0x0000 },
+  { 0x1a00, 0x27ad, 0x0000 },
+  { 0x9a00, 0x27b1, 0x2000 },
+  { 0x1a00, 0x27af, 0x0000 },
+  { 0x1a00, 0x27b2, 0x0000 },
+  { 0x9a00, 0x27b7, 0x3000 },
+  { 0x9a00, 0x27b5, 0x2000 },
+  { 0x1a00, 0x27b4, 0x0000 },
+  { 0x1a00, 0x27b6, 0x0000 },
+  { 0x9a00, 0x27b9, 0x2000 },
+  { 0x1a00, 0x27b8, 0x0000 },
+  { 0x1a00, 0x27ba, 0x0000 },
+  { 0x9900, 0x27d4, 0x4000 },
+  { 0x9900, 0x27d0, 0x3000 },
+  { 0x9a00, 0x27bd, 0x2000 },
+  { 0x1a00, 0x27bc, 0x0000 },
+  { 0x1a00, 0x27be, 0x0000 },
+  { 0x9900, 0x27d2, 0x2000 },
+  { 0x1900, 0x27d1, 0x0000 },
+  { 0x1900, 0x27d3, 0x0000 },
+  { 0x9900, 0x27d8, 0x3000 },
+  { 0x9900, 0x27d6, 0x2000 },
+  { 0x1900, 0x27d5, 0x0000 },
+  { 0x1900, 0x27d7, 0x0000 },
+  { 0x9900, 0x27da, 0x2000 },
+  { 0x1900, 0x27d9, 0x0000 },
+  { 0x1900, 0x27db, 0x0000 },
+  { 0x9a00, 0x2800, 0x6000 },
+  { 0x9900, 0x27f0, 0x5000 },
+  { 0x9900, 0x27e4, 0x4000 },
+  { 0x9900, 0x27e0, 0x3000 },
+  { 0x9900, 0x27de, 0x2000 },
+  { 0x1900, 0x27dd, 0x0000 },
+  { 0x1900, 0x27df, 0x0000 },
+  { 0x9900, 0x27e2, 0x2000 },
+  { 0x1900, 0x27e1, 0x0000 },
+  { 0x1900, 0x27e3, 0x0000 },
+  { 0x9600, 0x27e8, 0x3000 },
+  { 0x9600, 0x27e6, 0x2000 },
+  { 0x1900, 0x27e5, 0x0000 },
+  { 0x1200, 0x27e7, 0x0000 },
+  { 0x9600, 0x27ea, 0x2000 },
+  { 0x1200, 0x27e9, 0x0000 },
+  { 0x1200, 0x27eb, 0x0000 },
+  { 0x9900, 0x27f8, 0x4000 },
+  { 0x9900, 0x27f4, 0x3000 },
+  { 0x9900, 0x27f2, 0x2000 },
+  { 0x1900, 0x27f1, 0x0000 },
+  { 0x1900, 0x27f3, 0x0000 },
+  { 0x9900, 0x27f6, 0x2000 },
+  { 0x1900, 0x27f5, 0x0000 },
+  { 0x1900, 0x27f7, 0x0000 },
+  { 0x9900, 0x27fc, 0x3000 },
+  { 0x9900, 0x27fa, 0x2000 },
+  { 0x1900, 0x27f9, 0x0000 },
+  { 0x1900, 0x27fb, 0x0000 },
+  { 0x9900, 0x27fe, 0x2000 },
+  { 0x1900, 0x27fd, 0x0000 },
+  { 0x1900, 0x27ff, 0x0000 },
+  { 0x9a00, 0x2810, 0x5000 },
+  { 0x9a00, 0x2808, 0x4000 },
+  { 0x9a00, 0x2804, 0x3000 },
+  { 0x9a00, 0x2802, 0x2000 },
+  { 0x1a00, 0x2801, 0x0000 },
+  { 0x1a00, 0x2803, 0x0000 },
+  { 0x9a00, 0x2806, 0x2000 },
+  { 0x1a00, 0x2805, 0x0000 },
+  { 0x1a00, 0x2807, 0x0000 },
+  { 0x9a00, 0x280c, 0x3000 },
+  { 0x9a00, 0x280a, 0x2000 },
+  { 0x1a00, 0x2809, 0x0000 },
+  { 0x1a00, 0x280b, 0x0000 },
+  { 0x9a00, 0x280e, 0x2000 },
+  { 0x1a00, 0x280d, 0x0000 },
+  { 0x1a00, 0x280f, 0x0000 },
+  { 0x9a00, 0x2818, 0x4000 },
+  { 0x9a00, 0x2814, 0x3000 },
+  { 0x9a00, 0x2812, 0x2000 },
+  { 0x1a00, 0x2811, 0x0000 },
+  { 0x1a00, 0x2813, 0x0000 },
+  { 0x9a00, 0x2816, 0x2000 },
+  { 0x1a00, 0x2815, 0x0000 },
+  { 0x1a00, 0x2817, 0x0000 },
+  { 0x9a00, 0x281c, 0x3000 },
+  { 0x9a00, 0x281a, 0x2000 },
+  { 0x1a00, 0x2819, 0x0000 },
+  { 0x1a00, 0x281b, 0x0000 },
+  { 0x9a00, 0x281e, 0x2000 },
+  { 0x1a00, 0x281d, 0x0000 },
+  { 0x1a00, 0x281f, 0x0000 },
+  { 0x9a00, 0x2860, 0x7000 },
+  { 0x9a00, 0x2840, 0x6000 },
+  { 0x9a00, 0x2830, 0x5000 },
+  { 0x9a00, 0x2828, 0x4000 },
+  { 0x9a00, 0x2824, 0x3000 },
+  { 0x9a00, 0x2822, 0x2000 },
+  { 0x1a00, 0x2821, 0x0000 },
+  { 0x1a00, 0x2823, 0x0000 },
+  { 0x9a00, 0x2826, 0x2000 },
+  { 0x1a00, 0x2825, 0x0000 },
+  { 0x1a00, 0x2827, 0x0000 },
+  { 0x9a00, 0x282c, 0x3000 },
+  { 0x9a00, 0x282a, 0x2000 },
+  { 0x1a00, 0x2829, 0x0000 },
+  { 0x1a00, 0x282b, 0x0000 },
+  { 0x9a00, 0x282e, 0x2000 },
+  { 0x1a00, 0x282d, 0x0000 },
+  { 0x1a00, 0x282f, 0x0000 },
+  { 0x9a00, 0x2838, 0x4000 },
+  { 0x9a00, 0x2834, 0x3000 },
+  { 0x9a00, 0x2832, 0x2000 },
+  { 0x1a00, 0x2831, 0x0000 },
+  { 0x1a00, 0x2833, 0x0000 },
+  { 0x9a00, 0x2836, 0x2000 },
+  { 0x1a00, 0x2835, 0x0000 },
+  { 0x1a00, 0x2837, 0x0000 },
+  { 0x9a00, 0x283c, 0x3000 },
+  { 0x9a00, 0x283a, 0x2000 },
+  { 0x1a00, 0x2839, 0x0000 },
+  { 0x1a00, 0x283b, 0x0000 },
+  { 0x9a00, 0x283e, 0x2000 },
+  { 0x1a00, 0x283d, 0x0000 },
+  { 0x1a00, 0x283f, 0x0000 },
+  { 0x9a00, 0x2850, 0x5000 },
+  { 0x9a00, 0x2848, 0x4000 },
+  { 0x9a00, 0x2844, 0x3000 },
+  { 0x9a00, 0x2842, 0x2000 },
+  { 0x1a00, 0x2841, 0x0000 },
+  { 0x1a00, 0x2843, 0x0000 },
+  { 0x9a00, 0x2846, 0x2000 },
+  { 0x1a00, 0x2845, 0x0000 },
+  { 0x1a00, 0x2847, 0x0000 },
+  { 0x9a00, 0x284c, 0x3000 },
+  { 0x9a00, 0x284a, 0x2000 },
+  { 0x1a00, 0x2849, 0x0000 },
+  { 0x1a00, 0x284b, 0x0000 },
+  { 0x9a00, 0x284e, 0x2000 },
+  { 0x1a00, 0x284d, 0x0000 },
+  { 0x1a00, 0x284f, 0x0000 },
+  { 0x9a00, 0x2858, 0x4000 },
+  { 0x9a00, 0x2854, 0x3000 },
+  { 0x9a00, 0x2852, 0x2000 },
+  { 0x1a00, 0x2851, 0x0000 },
+  { 0x1a00, 0x2853, 0x0000 },
+  { 0x9a00, 0x2856, 0x2000 },
+  { 0x1a00, 0x2855, 0x0000 },
+  { 0x1a00, 0x2857, 0x0000 },
+  { 0x9a00, 0x285c, 0x3000 },
+  { 0x9a00, 0x285a, 0x2000 },
+  { 0x1a00, 0x2859, 0x0000 },
+  { 0x1a00, 0x285b, 0x0000 },
+  { 0x9a00, 0x285e, 0x2000 },
+  { 0x1a00, 0x285d, 0x0000 },
+  { 0x1a00, 0x285f, 0x0000 },
+  { 0x9a00, 0x2880, 0x6000 },
+  { 0x9a00, 0x2870, 0x5000 },
+  { 0x9a00, 0x2868, 0x4000 },
+  { 0x9a00, 0x2864, 0x3000 },
+  { 0x9a00, 0x2862, 0x2000 },
+  { 0x1a00, 0x2861, 0x0000 },
+  { 0x1a00, 0x2863, 0x0000 },
+  { 0x9a00, 0x2866, 0x2000 },
+  { 0x1a00, 0x2865, 0x0000 },
+  { 0x1a00, 0x2867, 0x0000 },
+  { 0x9a00, 0x286c, 0x3000 },
+  { 0x9a00, 0x286a, 0x2000 },
+  { 0x1a00, 0x2869, 0x0000 },
+  { 0x1a00, 0x286b, 0x0000 },
+  { 0x9a00, 0x286e, 0x2000 },
+  { 0x1a00, 0x286d, 0x0000 },
+  { 0x1a00, 0x286f, 0x0000 },
+  { 0x9a00, 0x2878, 0x4000 },
+  { 0x9a00, 0x2874, 0x3000 },
+  { 0x9a00, 0x2872, 0x2000 },
+  { 0x1a00, 0x2871, 0x0000 },
+  { 0x1a00, 0x2873, 0x0000 },
+  { 0x9a00, 0x2876, 0x2000 },
+  { 0x1a00, 0x2875, 0x0000 },
+  { 0x1a00, 0x2877, 0x0000 },
+  { 0x9a00, 0x287c, 0x3000 },
+  { 0x9a00, 0x287a, 0x2000 },
+  { 0x1a00, 0x2879, 0x0000 },
+  { 0x1a00, 0x287b, 0x0000 },
+  { 0x9a00, 0x287e, 0x2000 },
+  { 0x1a00, 0x287d, 0x0000 },
+  { 0x1a00, 0x287f, 0x0000 },
+  { 0x9a00, 0x2890, 0x5000 },
+  { 0x9a00, 0x2888, 0x4000 },
+  { 0x9a00, 0x2884, 0x3000 },
+  { 0x9a00, 0x2882, 0x2000 },
+  { 0x1a00, 0x2881, 0x0000 },
+  { 0x1a00, 0x2883, 0x0000 },
+  { 0x9a00, 0x2886, 0x2000 },
+  { 0x1a00, 0x2885, 0x0000 },
+  { 0x1a00, 0x2887, 0x0000 },
+  { 0x9a00, 0x288c, 0x3000 },
+  { 0x9a00, 0x288a, 0x2000 },
+  { 0x1a00, 0x2889, 0x0000 },
+  { 0x1a00, 0x288b, 0x0000 },
+  { 0x9a00, 0x288e, 0x2000 },
+  { 0x1a00, 0x288d, 0x0000 },
+  { 0x1a00, 0x288f, 0x0000 },
+  { 0x9a00, 0x2898, 0x4000 },
+  { 0x9a00, 0x2894, 0x3000 },
+  { 0x9a00, 0x2892, 0x2000 },
+  { 0x1a00, 0x2891, 0x0000 },
+  { 0x1a00, 0x2893, 0x0000 },
+  { 0x9a00, 0x2896, 0x2000 },
+  { 0x1a00, 0x2895, 0x0000 },
+  { 0x1a00, 0x2897, 0x0000 },
+  { 0x9a00, 0x289c, 0x3000 },
+  { 0x9a00, 0x289a, 0x2000 },
+  { 0x1a00, 0x2899, 0x0000 },
+  { 0x1a00, 0x289b, 0x0000 },
+  { 0x9a00, 0x289e, 0x2000 },
+  { 0x1a00, 0x289d, 0x0000 },
+  { 0x1a00, 0x289f, 0x0000 },
+  { 0x9900, 0x2920, 0x8000 },
+  { 0x9a00, 0x28e0, 0x7000 },
+  { 0x9a00, 0x28c0, 0x6000 },
+  { 0x9a00, 0x28b0, 0x5000 },
+  { 0x9a00, 0x28a8, 0x4000 },
+  { 0x9a00, 0x28a4, 0x3000 },
+  { 0x9a00, 0x28a2, 0x2000 },
+  { 0x1a00, 0x28a1, 0x0000 },
+  { 0x1a00, 0x28a3, 0x0000 },
+  { 0x9a00, 0x28a6, 0x2000 },
+  { 0x1a00, 0x28a5, 0x0000 },
+  { 0x1a00, 0x28a7, 0x0000 },
+  { 0x9a00, 0x28ac, 0x3000 },
+  { 0x9a00, 0x28aa, 0x2000 },
+  { 0x1a00, 0x28a9, 0x0000 },
+  { 0x1a00, 0x28ab, 0x0000 },
+  { 0x9a00, 0x28ae, 0x2000 },
+  { 0x1a00, 0x28ad, 0x0000 },
+  { 0x1a00, 0x28af, 0x0000 },
+  { 0x9a00, 0x28b8, 0x4000 },
+  { 0x9a00, 0x28b4, 0x3000 },
+  { 0x9a00, 0x28b2, 0x2000 },
+  { 0x1a00, 0x28b1, 0x0000 },
+  { 0x1a00, 0x28b3, 0x0000 },
+  { 0x9a00, 0x28b6, 0x2000 },
+  { 0x1a00, 0x28b5, 0x0000 },
+  { 0x1a00, 0x28b7, 0x0000 },
+  { 0x9a00, 0x28bc, 0x3000 },
+  { 0x9a00, 0x28ba, 0x2000 },
+  { 0x1a00, 0x28b9, 0x0000 },
+  { 0x1a00, 0x28bb, 0x0000 },
+  { 0x9a00, 0x28be, 0x2000 },
+  { 0x1a00, 0x28bd, 0x0000 },
+  { 0x1a00, 0x28bf, 0x0000 },
+  { 0x9a00, 0x28d0, 0x5000 },
+  { 0x9a00, 0x28c8, 0x4000 },
+  { 0x9a00, 0x28c4, 0x3000 },
+  { 0x9a00, 0x28c2, 0x2000 },
+  { 0x1a00, 0x28c1, 0x0000 },
+  { 0x1a00, 0x28c3, 0x0000 },
+  { 0x9a00, 0x28c6, 0x2000 },
+  { 0x1a00, 0x28c5, 0x0000 },
+  { 0x1a00, 0x28c7, 0x0000 },
+  { 0x9a00, 0x28cc, 0x3000 },
+  { 0x9a00, 0x28ca, 0x2000 },
+  { 0x1a00, 0x28c9, 0x0000 },
+  { 0x1a00, 0x28cb, 0x0000 },
+  { 0x9a00, 0x28ce, 0x2000 },
+  { 0x1a00, 0x28cd, 0x0000 },
+  { 0x1a00, 0x28cf, 0x0000 },
+  { 0x9a00, 0x28d8, 0x4000 },
+  { 0x9a00, 0x28d4, 0x3000 },
+  { 0x9a00, 0x28d2, 0x2000 },
+  { 0x1a00, 0x28d1, 0x0000 },
+  { 0x1a00, 0x28d3, 0x0000 },
+  { 0x9a00, 0x28d6, 0x2000 },
+  { 0x1a00, 0x28d5, 0x0000 },
+  { 0x1a00, 0x28d7, 0x0000 },
+  { 0x9a00, 0x28dc, 0x3000 },
+  { 0x9a00, 0x28da, 0x2000 },
+  { 0x1a00, 0x28d9, 0x0000 },
+  { 0x1a00, 0x28db, 0x0000 },
+  { 0x9a00, 0x28de, 0x2000 },
+  { 0x1a00, 0x28dd, 0x0000 },
+  { 0x1a00, 0x28df, 0x0000 },
+  { 0x9900, 0x2900, 0x6000 },
+  { 0x9a00, 0x28f0, 0x5000 },
+  { 0x9a00, 0x28e8, 0x4000 },
+  { 0x9a00, 0x28e4, 0x3000 },
+  { 0x9a00, 0x28e2, 0x2000 },
+  { 0x1a00, 0x28e1, 0x0000 },
+  { 0x1a00, 0x28e3, 0x0000 },
+  { 0x9a00, 0x28e6, 0x2000 },
+  { 0x1a00, 0x28e5, 0x0000 },
+  { 0x1a00, 0x28e7, 0x0000 },
+  { 0x9a00, 0x28ec, 0x3000 },
+  { 0x9a00, 0x28ea, 0x2000 },
+  { 0x1a00, 0x28e9, 0x0000 },
+  { 0x1a00, 0x28eb, 0x0000 },
+  { 0x9a00, 0x28ee, 0x2000 },
+  { 0x1a00, 0x28ed, 0x0000 },
+  { 0x1a00, 0x28ef, 0x0000 },
+  { 0x9a00, 0x28f8, 0x4000 },
+  { 0x9a00, 0x28f4, 0x3000 },
+  { 0x9a00, 0x28f2, 0x2000 },
+  { 0x1a00, 0x28f1, 0x0000 },
+  { 0x1a00, 0x28f3, 0x0000 },
+  { 0x9a00, 0x28f6, 0x2000 },
+  { 0x1a00, 0x28f5, 0x0000 },
+  { 0x1a00, 0x28f7, 0x0000 },
+  { 0x9a00, 0x28fc, 0x3000 },
+  { 0x9a00, 0x28fa, 0x2000 },
+  { 0x1a00, 0x28f9, 0x0000 },
+  { 0x1a00, 0x28fb, 0x0000 },
+  { 0x9a00, 0x28fe, 0x2000 },
+  { 0x1a00, 0x28fd, 0x0000 },
+  { 0x1a00, 0x28ff, 0x0000 },
+  { 0x9900, 0x2910, 0x5000 },
+  { 0x9900, 0x2908, 0x4000 },
+  { 0x9900, 0x2904, 0x3000 },
+  { 0x9900, 0x2902, 0x2000 },
+  { 0x1900, 0x2901, 0x0000 },
+  { 0x1900, 0x2903, 0x0000 },
+  { 0x9900, 0x2906, 0x2000 },
+  { 0x1900, 0x2905, 0x0000 },
+  { 0x1900, 0x2907, 0x0000 },
+  { 0x9900, 0x290c, 0x3000 },
+  { 0x9900, 0x290a, 0x2000 },
+  { 0x1900, 0x2909, 0x0000 },
+  { 0x1900, 0x290b, 0x0000 },
+  { 0x9900, 0x290e, 0x2000 },
+  { 0x1900, 0x290d, 0x0000 },
+  { 0x1900, 0x290f, 0x0000 },
+  { 0x9900, 0x2918, 0x4000 },
+  { 0x9900, 0x2914, 0x3000 },
+  { 0x9900, 0x2912, 0x2000 },
+  { 0x1900, 0x2911, 0x0000 },
+  { 0x1900, 0x2913, 0x0000 },
+  { 0x9900, 0x2916, 0x2000 },
+  { 0x1900, 0x2915, 0x0000 },
+  { 0x1900, 0x2917, 0x0000 },
+  { 0x9900, 0x291c, 0x3000 },
+  { 0x9900, 0x291a, 0x2000 },
+  { 0x1900, 0x2919, 0x0000 },
+  { 0x1900, 0x291b, 0x0000 },
+  { 0x9900, 0x291e, 0x2000 },
+  { 0x1900, 0x291d, 0x0000 },
+  { 0x1900, 0x291f, 0x0000 },
+  { 0x9900, 0x2960, 0x7000 },
+  { 0x9900, 0x2940, 0x6000 },
+  { 0x9900, 0x2930, 0x5000 },
+  { 0x9900, 0x2928, 0x4000 },
+  { 0x9900, 0x2924, 0x3000 },
+  { 0x9900, 0x2922, 0x2000 },
+  { 0x1900, 0x2921, 0x0000 },
+  { 0x1900, 0x2923, 0x0000 },
+  { 0x9900, 0x2926, 0x2000 },
+  { 0x1900, 0x2925, 0x0000 },
+  { 0x1900, 0x2927, 0x0000 },
+  { 0x9900, 0x292c, 0x3000 },
+  { 0x9900, 0x292a, 0x2000 },
+  { 0x1900, 0x2929, 0x0000 },
+  { 0x1900, 0x292b, 0x0000 },
+  { 0x9900, 0x292e, 0x2000 },
+  { 0x1900, 0x292d, 0x0000 },
+  { 0x1900, 0x292f, 0x0000 },
+  { 0x9900, 0x2938, 0x4000 },
+  { 0x9900, 0x2934, 0x3000 },
+  { 0x9900, 0x2932, 0x2000 },
+  { 0x1900, 0x2931, 0x0000 },
+  { 0x1900, 0x2933, 0x0000 },
+  { 0x9900, 0x2936, 0x2000 },
+  { 0x1900, 0x2935, 0x0000 },
+  { 0x1900, 0x2937, 0x0000 },
+  { 0x9900, 0x293c, 0x3000 },
+  { 0x9900, 0x293a, 0x2000 },
+  { 0x1900, 0x2939, 0x0000 },
+  { 0x1900, 0x293b, 0x0000 },
+  { 0x9900, 0x293e, 0x2000 },
+  { 0x1900, 0x293d, 0x0000 },
+  { 0x1900, 0x293f, 0x0000 },
+  { 0x9900, 0x2950, 0x5000 },
+  { 0x9900, 0x2948, 0x4000 },
+  { 0x9900, 0x2944, 0x3000 },
+  { 0x9900, 0x2942, 0x2000 },
+  { 0x1900, 0x2941, 0x0000 },
+  { 0x1900, 0x2943, 0x0000 },
+  { 0x9900, 0x2946, 0x2000 },
+  { 0x1900, 0x2945, 0x0000 },
+  { 0x1900, 0x2947, 0x0000 },
+  { 0x9900, 0x294c, 0x3000 },
+  { 0x9900, 0x294a, 0x2000 },
+  { 0x1900, 0x2949, 0x0000 },
+  { 0x1900, 0x294b, 0x0000 },
+  { 0x9900, 0x294e, 0x2000 },
+  { 0x1900, 0x294d, 0x0000 },
+  { 0x1900, 0x294f, 0x0000 },
+  { 0x9900, 0x2958, 0x4000 },
+  { 0x9900, 0x2954, 0x3000 },
+  { 0x9900, 0x2952, 0x2000 },
+  { 0x1900, 0x2951, 0x0000 },
+  { 0x1900, 0x2953, 0x0000 },
+  { 0x9900, 0x2956, 0x2000 },
+  { 0x1900, 0x2955, 0x0000 },
+  { 0x1900, 0x2957, 0x0000 },
+  { 0x9900, 0x295c, 0x3000 },
+  { 0x9900, 0x295a, 0x2000 },
+  { 0x1900, 0x2959, 0x0000 },
+  { 0x1900, 0x295b, 0x0000 },
+  { 0x9900, 0x295e, 0x2000 },
+  { 0x1900, 0x295d, 0x0000 },
+  { 0x1900, 0x295f, 0x0000 },
+  { 0x9900, 0x2980, 0x6000 },
+  { 0x9900, 0x2970, 0x5000 },
+  { 0x9900, 0x2968, 0x4000 },
+  { 0x9900, 0x2964, 0x3000 },
+  { 0x9900, 0x2962, 0x2000 },
+  { 0x1900, 0x2961, 0x0000 },
+  { 0x1900, 0x2963, 0x0000 },
+  { 0x9900, 0x2966, 0x2000 },
+  { 0x1900, 0x2965, 0x0000 },
+  { 0x1900, 0x2967, 0x0000 },
+  { 0x9900, 0x296c, 0x3000 },
+  { 0x9900, 0x296a, 0x2000 },
+  { 0x1900, 0x2969, 0x0000 },
+  { 0x1900, 0x296b, 0x0000 },
+  { 0x9900, 0x296e, 0x2000 },
+  { 0x1900, 0x296d, 0x0000 },
+  { 0x1900, 0x296f, 0x0000 },
+  { 0x9900, 0x2978, 0x4000 },
+  { 0x9900, 0x2974, 0x3000 },
+  { 0x9900, 0x2972, 0x2000 },
+  { 0x1900, 0x2971, 0x0000 },
+  { 0x1900, 0x2973, 0x0000 },
+  { 0x9900, 0x2976, 0x2000 },
+  { 0x1900, 0x2975, 0x0000 },
+  { 0x1900, 0x2977, 0x0000 },
+  { 0x9900, 0x297c, 0x3000 },
+  { 0x9900, 0x297a, 0x2000 },
+  { 0x1900, 0x2979, 0x0000 },
+  { 0x1900, 0x297b, 0x0000 },
+  { 0x9900, 0x297e, 0x2000 },
+  { 0x1900, 0x297d, 0x0000 },
+  { 0x1900, 0x297f, 0x0000 },
+  { 0x9200, 0x2990, 0x5000 },
+  { 0x9200, 0x2988, 0x4000 },
+  { 0x9200, 0x2984, 0x3000 },
+  { 0x9900, 0x2982, 0x2000 },
+  { 0x1900, 0x2981, 0x0000 },
+  { 0x1600, 0x2983, 0x0000 },
+  { 0x9200, 0x2986, 0x2000 },
+  { 0x1600, 0x2985, 0x0000 },
+  { 0x1600, 0x2987, 0x0000 },
+  { 0x9200, 0x298c, 0x3000 },
+  { 0x9200, 0x298a, 0x2000 },
+  { 0x1600, 0x2989, 0x0000 },
+  { 0x1600, 0x298b, 0x0000 },
+  { 0x9200, 0x298e, 0x2000 },
+  { 0x1600, 0x298d, 0x0000 },
+  { 0x1600, 0x298f, 0x0000 },
+  { 0x9200, 0x2998, 0x4000 },
+  { 0x9200, 0x2994, 0x3000 },
+  { 0x9200, 0x2992, 0x2000 },
+  { 0x1600, 0x2991, 0x0000 },
+  { 0x1600, 0x2993, 0x0000 },
+  { 0x9200, 0x2996, 0x2000 },
+  { 0x1600, 0x2995, 0x0000 },
+  { 0x1600, 0x2997, 0x0000 },
+  { 0x9900, 0x299c, 0x3000 },
+  { 0x9900, 0x299a, 0x2000 },
+  { 0x1900, 0x2999, 0x0000 },
+  { 0x1900, 0x299b, 0x0000 },
+  { 0x9900, 0x299e, 0x2000 },
+  { 0x1900, 0x299d, 0x0000 },
+  { 0x1900, 0x299f, 0x0000 },
+  { 0x9900, 0x2aa0, 0x9000 },
+  { 0x9900, 0x2a20, 0x8000 },
+  { 0x9900, 0x29e0, 0x7000 },
+  { 0x9900, 0x29c0, 0x6000 },
+  { 0x9900, 0x29b0, 0x5000 },
+  { 0x9900, 0x29a8, 0x4000 },
+  { 0x9900, 0x29a4, 0x3000 },
+  { 0x9900, 0x29a2, 0x2000 },
+  { 0x1900, 0x29a1, 0x0000 },
+  { 0x1900, 0x29a3, 0x0000 },
+  { 0x9900, 0x29a6, 0x2000 },
+  { 0x1900, 0x29a5, 0x0000 },
+  { 0x1900, 0x29a7, 0x0000 },
+  { 0x9900, 0x29ac, 0x3000 },
+  { 0x9900, 0x29aa, 0x2000 },
+  { 0x1900, 0x29a9, 0x0000 },
+  { 0x1900, 0x29ab, 0x0000 },
+  { 0x9900, 0x29ae, 0x2000 },
+  { 0x1900, 0x29ad, 0x0000 },
+  { 0x1900, 0x29af, 0x0000 },
+  { 0x9900, 0x29b8, 0x4000 },
+  { 0x9900, 0x29b4, 0x3000 },
+  { 0x9900, 0x29b2, 0x2000 },
+  { 0x1900, 0x29b1, 0x0000 },
+  { 0x1900, 0x29b3, 0x0000 },
+  { 0x9900, 0x29b6, 0x2000 },
+  { 0x1900, 0x29b5, 0x0000 },
+  { 0x1900, 0x29b7, 0x0000 },
+  { 0x9900, 0x29bc, 0x3000 },
+  { 0x9900, 0x29ba, 0x2000 },
+  { 0x1900, 0x29b9, 0x0000 },
+  { 0x1900, 0x29bb, 0x0000 },
+  { 0x9900, 0x29be, 0x2000 },
+  { 0x1900, 0x29bd, 0x0000 },
+  { 0x1900, 0x29bf, 0x0000 },
+  { 0x9900, 0x29d0, 0x5000 },
+  { 0x9900, 0x29c8, 0x4000 },
+  { 0x9900, 0x29c4, 0x3000 },
+  { 0x9900, 0x29c2, 0x2000 },
+  { 0x1900, 0x29c1, 0x0000 },
+  { 0x1900, 0x29c3, 0x0000 },
+  { 0x9900, 0x29c6, 0x2000 },
+  { 0x1900, 0x29c5, 0x0000 },
+  { 0x1900, 0x29c7, 0x0000 },
+  { 0x9900, 0x29cc, 0x3000 },
+  { 0x9900, 0x29ca, 0x2000 },
+  { 0x1900, 0x29c9, 0x0000 },
+  { 0x1900, 0x29cb, 0x0000 },
+  { 0x9900, 0x29ce, 0x2000 },
+  { 0x1900, 0x29cd, 0x0000 },
+  { 0x1900, 0x29cf, 0x0000 },
+  { 0x9600, 0x29d8, 0x4000 },
+  { 0x9900, 0x29d4, 0x3000 },
+  { 0x9900, 0x29d2, 0x2000 },
+  { 0x1900, 0x29d1, 0x0000 },
+  { 0x1900, 0x29d3, 0x0000 },
+  { 0x9900, 0x29d6, 0x2000 },
+  { 0x1900, 0x29d5, 0x0000 },
+  { 0x1900, 0x29d7, 0x0000 },
+  { 0x9900, 0x29dc, 0x3000 },
+  { 0x9600, 0x29da, 0x2000 },
+  { 0x1200, 0x29d9, 0x0000 },
+  { 0x1200, 0x29db, 0x0000 },
+  { 0x9900, 0x29de, 0x2000 },
+  { 0x1900, 0x29dd, 0x0000 },
+  { 0x1900, 0x29df, 0x0000 },
+  { 0x9900, 0x2a00, 0x6000 },
+  { 0x9900, 0x29f0, 0x5000 },
+  { 0x9900, 0x29e8, 0x4000 },
+  { 0x9900, 0x29e4, 0x3000 },
+  { 0x9900, 0x29e2, 0x2000 },
+  { 0x1900, 0x29e1, 0x0000 },
+  { 0x1900, 0x29e3, 0x0000 },
+  { 0x9900, 0x29e6, 0x2000 },
+  { 0x1900, 0x29e5, 0x0000 },
+  { 0x1900, 0x29e7, 0x0000 },
+  { 0x9900, 0x29ec, 0x3000 },
+  { 0x9900, 0x29ea, 0x2000 },
+  { 0x1900, 0x29e9, 0x0000 },
+  { 0x1900, 0x29eb, 0x0000 },
+  { 0x9900, 0x29ee, 0x2000 },
+  { 0x1900, 0x29ed, 0x0000 },
+  { 0x1900, 0x29ef, 0x0000 },
+  { 0x9900, 0x29f8, 0x4000 },
+  { 0x9900, 0x29f4, 0x3000 },
+  { 0x9900, 0x29f2, 0x2000 },
+  { 0x1900, 0x29f1, 0x0000 },
+  { 0x1900, 0x29f3, 0x0000 },
+  { 0x9900, 0x29f6, 0x2000 },
+  { 0x1900, 0x29f5, 0x0000 },
+  { 0x1900, 0x29f7, 0x0000 },
+  { 0x9600, 0x29fc, 0x3000 },
+  { 0x9900, 0x29fa, 0x2000 },
+  { 0x1900, 0x29f9, 0x0000 },
+  { 0x1900, 0x29fb, 0x0000 },
+  { 0x9900, 0x29fe, 0x2000 },
+  { 0x1200, 0x29fd, 0x0000 },
+  { 0x1900, 0x29ff, 0x0000 },
+  { 0x9900, 0x2a10, 0x5000 },
+  { 0x9900, 0x2a08, 0x4000 },
+  { 0x9900, 0x2a04, 0x3000 },
+  { 0x9900, 0x2a02, 0x2000 },
+  { 0x1900, 0x2a01, 0x0000 },
+  { 0x1900, 0x2a03, 0x0000 },
+  { 0x9900, 0x2a06, 0x2000 },
+  { 0x1900, 0x2a05, 0x0000 },
+  { 0x1900, 0x2a07, 0x0000 },
+  { 0x9900, 0x2a0c, 0x3000 },
+  { 0x9900, 0x2a0a, 0x2000 },
+  { 0x1900, 0x2a09, 0x0000 },
+  { 0x1900, 0x2a0b, 0x0000 },
+  { 0x9900, 0x2a0e, 0x2000 },
+  { 0x1900, 0x2a0d, 0x0000 },
+  { 0x1900, 0x2a0f, 0x0000 },
+  { 0x9900, 0x2a18, 0x4000 },
+  { 0x9900, 0x2a14, 0x3000 },
+  { 0x9900, 0x2a12, 0x2000 },
+  { 0x1900, 0x2a11, 0x0000 },
+  { 0x1900, 0x2a13, 0x0000 },
+  { 0x9900, 0x2a16, 0x2000 },
+  { 0x1900, 0x2a15, 0x0000 },
+  { 0x1900, 0x2a17, 0x0000 },
+  { 0x9900, 0x2a1c, 0x3000 },
+  { 0x9900, 0x2a1a, 0x2000 },
+  { 0x1900, 0x2a19, 0x0000 },
+  { 0x1900, 0x2a1b, 0x0000 },
+  { 0x9900, 0x2a1e, 0x2000 },
+  { 0x1900, 0x2a1d, 0x0000 },
+  { 0x1900, 0x2a1f, 0x0000 },
+  { 0x9900, 0x2a60, 0x7000 },
+  { 0x9900, 0x2a40, 0x6000 },
+  { 0x9900, 0x2a30, 0x5000 },
+  { 0x9900, 0x2a28, 0x4000 },
+  { 0x9900, 0x2a24, 0x3000 },
+  { 0x9900, 0x2a22, 0x2000 },
+  { 0x1900, 0x2a21, 0x0000 },
+  { 0x1900, 0x2a23, 0x0000 },
+  { 0x9900, 0x2a26, 0x2000 },
+  { 0x1900, 0x2a25, 0x0000 },
+  { 0x1900, 0x2a27, 0x0000 },
+  { 0x9900, 0x2a2c, 0x3000 },
+  { 0x9900, 0x2a2a, 0x2000 },
+  { 0x1900, 0x2a29, 0x0000 },
+  { 0x1900, 0x2a2b, 0x0000 },
+  { 0x9900, 0x2a2e, 0x2000 },
+  { 0x1900, 0x2a2d, 0x0000 },
+  { 0x1900, 0x2a2f, 0x0000 },
+  { 0x9900, 0x2a38, 0x4000 },
+  { 0x9900, 0x2a34, 0x3000 },
+  { 0x9900, 0x2a32, 0x2000 },
+  { 0x1900, 0x2a31, 0x0000 },
+  { 0x1900, 0x2a33, 0x0000 },
+  { 0x9900, 0x2a36, 0x2000 },
+  { 0x1900, 0x2a35, 0x0000 },
+  { 0x1900, 0x2a37, 0x0000 },
+  { 0x9900, 0x2a3c, 0x3000 },
+  { 0x9900, 0x2a3a, 0x2000 },
+  { 0x1900, 0x2a39, 0x0000 },
+  { 0x1900, 0x2a3b, 0x0000 },
+  { 0x9900, 0x2a3e, 0x2000 },
+  { 0x1900, 0x2a3d, 0x0000 },
+  { 0x1900, 0x2a3f, 0x0000 },
+  { 0x9900, 0x2a50, 0x5000 },
+  { 0x9900, 0x2a48, 0x4000 },
+  { 0x9900, 0x2a44, 0x3000 },
+  { 0x9900, 0x2a42, 0x2000 },
+  { 0x1900, 0x2a41, 0x0000 },
+  { 0x1900, 0x2a43, 0x0000 },
+  { 0x9900, 0x2a46, 0x2000 },
+  { 0x1900, 0x2a45, 0x0000 },
+  { 0x1900, 0x2a47, 0x0000 },
+  { 0x9900, 0x2a4c, 0x3000 },
+  { 0x9900, 0x2a4a, 0x2000 },
+  { 0x1900, 0x2a49, 0x0000 },
+  { 0x1900, 0x2a4b, 0x0000 },
+  { 0x9900, 0x2a4e, 0x2000 },
+  { 0x1900, 0x2a4d, 0x0000 },
+  { 0x1900, 0x2a4f, 0x0000 },
+  { 0x9900, 0x2a58, 0x4000 },
+  { 0x9900, 0x2a54, 0x3000 },
+  { 0x9900, 0x2a52, 0x2000 },
+  { 0x1900, 0x2a51, 0x0000 },
+  { 0x1900, 0x2a53, 0x0000 },
+  { 0x9900, 0x2a56, 0x2000 },
+  { 0x1900, 0x2a55, 0x0000 },
+  { 0x1900, 0x2a57, 0x0000 },
+  { 0x9900, 0x2a5c, 0x3000 },
+  { 0x9900, 0x2a5a, 0x2000 },
+  { 0x1900, 0x2a59, 0x0000 },
+  { 0x1900, 0x2a5b, 0x0000 },
+  { 0x9900, 0x2a5e, 0x2000 },
+  { 0x1900, 0x2a5d, 0x0000 },
+  { 0x1900, 0x2a5f, 0x0000 },
+  { 0x9900, 0x2a80, 0x6000 },
+  { 0x9900, 0x2a70, 0x5000 },
+  { 0x9900, 0x2a68, 0x4000 },
+  { 0x9900, 0x2a64, 0x3000 },
+  { 0x9900, 0x2a62, 0x2000 },
+  { 0x1900, 0x2a61, 0x0000 },
+  { 0x1900, 0x2a63, 0x0000 },
+  { 0x9900, 0x2a66, 0x2000 },
+  { 0x1900, 0x2a65, 0x0000 },
+  { 0x1900, 0x2a67, 0x0000 },
+  { 0x9900, 0x2a6c, 0x3000 },
+  { 0x9900, 0x2a6a, 0x2000 },
+  { 0x1900, 0x2a69, 0x0000 },
+  { 0x1900, 0x2a6b, 0x0000 },
+  { 0x9900, 0x2a6e, 0x2000 },
+  { 0x1900, 0x2a6d, 0x0000 },
+  { 0x1900, 0x2a6f, 0x0000 },
+  { 0x9900, 0x2a78, 0x4000 },
+  { 0x9900, 0x2a74, 0x3000 },
+  { 0x9900, 0x2a72, 0x2000 },
+  { 0x1900, 0x2a71, 0x0000 },
+  { 0x1900, 0x2a73, 0x0000 },
+  { 0x9900, 0x2a76, 0x2000 },
+  { 0x1900, 0x2a75, 0x0000 },
+  { 0x1900, 0x2a77, 0x0000 },
+  { 0x9900, 0x2a7c, 0x3000 },
+  { 0x9900, 0x2a7a, 0x2000 },
+  { 0x1900, 0x2a79, 0x0000 },
+  { 0x1900, 0x2a7b, 0x0000 },
+  { 0x9900, 0x2a7e, 0x2000 },
+  { 0x1900, 0x2a7d, 0x0000 },
+  { 0x1900, 0x2a7f, 0x0000 },
+  { 0x9900, 0x2a90, 0x5000 },
+  { 0x9900, 0x2a88, 0x4000 },
+  { 0x9900, 0x2a84, 0x3000 },
+  { 0x9900, 0x2a82, 0x2000 },
+  { 0x1900, 0x2a81, 0x0000 },
+  { 0x1900, 0x2a83, 0x0000 },
+  { 0x9900, 0x2a86, 0x2000 },
+  { 0x1900, 0x2a85, 0x0000 },
+  { 0x1900, 0x2a87, 0x0000 },
+  { 0x9900, 0x2a8c, 0x3000 },
+  { 0x9900, 0x2a8a, 0x2000 },
+  { 0x1900, 0x2a89, 0x0000 },
+  { 0x1900, 0x2a8b, 0x0000 },
+  { 0x9900, 0x2a8e, 0x2000 },
+  { 0x1900, 0x2a8d, 0x0000 },
+  { 0x1900, 0x2a8f, 0x0000 },
+  { 0x9900, 0x2a98, 0x4000 },
+  { 0x9900, 0x2a94, 0x3000 },
+  { 0x9900, 0x2a92, 0x2000 },
+  { 0x1900, 0x2a91, 0x0000 },
+  { 0x1900, 0x2a93, 0x0000 },
+  { 0x9900, 0x2a96, 0x2000 },
+  { 0x1900, 0x2a95, 0x0000 },
+  { 0x1900, 0x2a97, 0x0000 },
+  { 0x9900, 0x2a9c, 0x3000 },
+  { 0x9900, 0x2a9a, 0x2000 },
+  { 0x1900, 0x2a99, 0x0000 },
+  { 0x1900, 0x2a9b, 0x0000 },
+  { 0x9900, 0x2a9e, 0x2000 },
+  { 0x1900, 0x2a9d, 0x0000 },
+  { 0x1900, 0x2a9f, 0x0000 },
+  { 0x9a00, 0x2e92, 0x8000 },
+  { 0x9900, 0x2ae0, 0x7000 },
+  { 0x9900, 0x2ac0, 0x6000 },
+  { 0x9900, 0x2ab0, 0x5000 },
+  { 0x9900, 0x2aa8, 0x4000 },
+  { 0x9900, 0x2aa4, 0x3000 },
+  { 0x9900, 0x2aa2, 0x2000 },
+  { 0x1900, 0x2aa1, 0x0000 },
+  { 0x1900, 0x2aa3, 0x0000 },
+  { 0x9900, 0x2aa6, 0x2000 },
+  { 0x1900, 0x2aa5, 0x0000 },
+  { 0x1900, 0x2aa7, 0x0000 },
+  { 0x9900, 0x2aac, 0x3000 },
+  { 0x9900, 0x2aaa, 0x2000 },
+  { 0x1900, 0x2aa9, 0x0000 },
+  { 0x1900, 0x2aab, 0x0000 },
+  { 0x9900, 0x2aae, 0x2000 },
+  { 0x1900, 0x2aad, 0x0000 },
+  { 0x1900, 0x2aaf, 0x0000 },
+  { 0x9900, 0x2ab8, 0x4000 },
+  { 0x9900, 0x2ab4, 0x3000 },
+  { 0x9900, 0x2ab2, 0x2000 },
+  { 0x1900, 0x2ab1, 0x0000 },
+  { 0x1900, 0x2ab3, 0x0000 },
+  { 0x9900, 0x2ab6, 0x2000 },
+  { 0x1900, 0x2ab5, 0x0000 },
+  { 0x1900, 0x2ab7, 0x0000 },
+  { 0x9900, 0x2abc, 0x3000 },
+  { 0x9900, 0x2aba, 0x2000 },
+  { 0x1900, 0x2ab9, 0x0000 },
+  { 0x1900, 0x2abb, 0x0000 },
+  { 0x9900, 0x2abe, 0x2000 },
+  { 0x1900, 0x2abd, 0x0000 },
+  { 0x1900, 0x2abf, 0x0000 },
+  { 0x9900, 0x2ad0, 0x5000 },
+  { 0x9900, 0x2ac8, 0x4000 },
+  { 0x9900, 0x2ac4, 0x3000 },
+  { 0x9900, 0x2ac2, 0x2000 },
+  { 0x1900, 0x2ac1, 0x0000 },
+  { 0x1900, 0x2ac3, 0x0000 },
+  { 0x9900, 0x2ac6, 0x2000 },
+  { 0x1900, 0x2ac5, 0x0000 },
+  { 0x1900, 0x2ac7, 0x0000 },
+  { 0x9900, 0x2acc, 0x3000 },
+  { 0x9900, 0x2aca, 0x2000 },
+  { 0x1900, 0x2ac9, 0x0000 },
+  { 0x1900, 0x2acb, 0x0000 },
+  { 0x9900, 0x2ace, 0x2000 },
+  { 0x1900, 0x2acd, 0x0000 },
+  { 0x1900, 0x2acf, 0x0000 },
+  { 0x9900, 0x2ad8, 0x4000 },
+  { 0x9900, 0x2ad4, 0x3000 },
+  { 0x9900, 0x2ad2, 0x2000 },
+  { 0x1900, 0x2ad1, 0x0000 },
+  { 0x1900, 0x2ad3, 0x0000 },
+  { 0x9900, 0x2ad6, 0x2000 },
+  { 0x1900, 0x2ad5, 0x0000 },
+  { 0x1900, 0x2ad7, 0x0000 },
+  { 0x9900, 0x2adc, 0x3000 },
+  { 0x9900, 0x2ada, 0x2000 },
+  { 0x1900, 0x2ad9, 0x0000 },
+  { 0x1900, 0x2adb, 0x0000 },
+  { 0x9900, 0x2ade, 0x2000 },
+  { 0x1900, 0x2add, 0x0000 },
+  { 0x1900, 0x2adf, 0x0000 },
+  { 0x9a00, 0x2b00, 0x6000 },
+  { 0x9900, 0x2af0, 0x5000 },
+  { 0x9900, 0x2ae8, 0x4000 },
+  { 0x9900, 0x2ae4, 0x3000 },
+  { 0x9900, 0x2ae2, 0x2000 },
+  { 0x1900, 0x2ae1, 0x0000 },
+  { 0x1900, 0x2ae3, 0x0000 },
+  { 0x9900, 0x2ae6, 0x2000 },
+  { 0x1900, 0x2ae5, 0x0000 },
+  { 0x1900, 0x2ae7, 0x0000 },
+  { 0x9900, 0x2aec, 0x3000 },
+  { 0x9900, 0x2aea, 0x2000 },
+  { 0x1900, 0x2ae9, 0x0000 },
+  { 0x1900, 0x2aeb, 0x0000 },
+  { 0x9900, 0x2aee, 0x2000 },
+  { 0x1900, 0x2aed, 0x0000 },
+  { 0x1900, 0x2aef, 0x0000 },
+  { 0x9900, 0x2af8, 0x4000 },
+  { 0x9900, 0x2af4, 0x3000 },
+  { 0x9900, 0x2af2, 0x2000 },
+  { 0x1900, 0x2af1, 0x0000 },
+  { 0x1900, 0x2af3, 0x0000 },
+  { 0x9900, 0x2af6, 0x2000 },
+  { 0x1900, 0x2af5, 0x0000 },
+  { 0x1900, 0x2af7, 0x0000 },
+  { 0x9900, 0x2afc, 0x3000 },
+  { 0x9900, 0x2afa, 0x2000 },
+  { 0x1900, 0x2af9, 0x0000 },
+  { 0x1900, 0x2afb, 0x0000 },
+  { 0x9900, 0x2afe, 0x2000 },
+  { 0x1900, 0x2afd, 0x0000 },
+  { 0x1900, 0x2aff, 0x0000 },
+  { 0x9a00, 0x2e82, 0x5000 },
+  { 0x9a00, 0x2b08, 0x4000 },
+  { 0x9a00, 0x2b04, 0x3000 },
+  { 0x9a00, 0x2b02, 0x2000 },
+  { 0x1a00, 0x2b01, 0x0000 },
+  { 0x1a00, 0x2b03, 0x0000 },
+  { 0x9a00, 0x2b06, 0x2000 },
+  { 0x1a00, 0x2b05, 0x0000 },
+  { 0x1a00, 0x2b07, 0x0000 },
+  { 0x9a00, 0x2b0c, 0x3000 },
+  { 0x9a00, 0x2b0a, 0x2000 },
+  { 0x1a00, 0x2b09, 0x0000 },
+  { 0x1a00, 0x2b0b, 0x0000 },
+  { 0x9a00, 0x2e80, 0x2000 },
+  { 0x1a00, 0x2b0d, 0x0000 },
+  { 0x1a00, 0x2e81, 0x0000 },
+  { 0x9a00, 0x2e8a, 0x4000 },
+  { 0x9a00, 0x2e86, 0x3000 },
+  { 0x9a00, 0x2e84, 0x2000 },
+  { 0x1a00, 0x2e83, 0x0000 },
+  { 0x1a00, 0x2e85, 0x0000 },
+  { 0x9a00, 0x2e88, 0x2000 },
+  { 0x1a00, 0x2e87, 0x0000 },
+  { 0x1a00, 0x2e89, 0x0000 },
+  { 0x9a00, 0x2e8e, 0x3000 },
+  { 0x9a00, 0x2e8c, 0x2000 },
+  { 0x1a00, 0x2e8b, 0x0000 },
+  { 0x1a00, 0x2e8d, 0x0000 },
+  { 0x9a00, 0x2e90, 0x2000 },
+  { 0x1a00, 0x2e8f, 0x0000 },
+  { 0x1a00, 0x2e91, 0x0000 },
+  { 0x9a00, 0x2ed3, 0x7000 },
+  { 0x9a00, 0x2eb3, 0x6000 },
+  { 0x9a00, 0x2ea3, 0x5000 },
+  { 0x9a00, 0x2e9b, 0x4000 },
+  { 0x9a00, 0x2e96, 0x3000 },
+  { 0x9a00, 0x2e94, 0x2000 },
+  { 0x1a00, 0x2e93, 0x0000 },
+  { 0x1a00, 0x2e95, 0x0000 },
+  { 0x9a00, 0x2e98, 0x2000 },
+  { 0x1a00, 0x2e97, 0x0000 },
+  { 0x1a00, 0x2e99, 0x0000 },
+  { 0x9a00, 0x2e9f, 0x3000 },
+  { 0x9a00, 0x2e9d, 0x2000 },
+  { 0x1a00, 0x2e9c, 0x0000 },
+  { 0x1a00, 0x2e9e, 0x0000 },
+  { 0x9a00, 0x2ea1, 0x2000 },
+  { 0x1a00, 0x2ea0, 0x0000 },
+  { 0x1a00, 0x2ea2, 0x0000 },
+  { 0x9a00, 0x2eab, 0x4000 },
+  { 0x9a00, 0x2ea7, 0x3000 },
+  { 0x9a00, 0x2ea5, 0x2000 },
+  { 0x1a00, 0x2ea4, 0x0000 },
+  { 0x1a00, 0x2ea6, 0x0000 },
+  { 0x9a00, 0x2ea9, 0x2000 },
+  { 0x1a00, 0x2ea8, 0x0000 },
+  { 0x1a00, 0x2eaa, 0x0000 },
+  { 0x9a00, 0x2eaf, 0x3000 },
+  { 0x9a00, 0x2ead, 0x2000 },
+  { 0x1a00, 0x2eac, 0x0000 },
+  { 0x1a00, 0x2eae, 0x0000 },
+  { 0x9a00, 0x2eb1, 0x2000 },
+  { 0x1a00, 0x2eb0, 0x0000 },
+  { 0x1a00, 0x2eb2, 0x0000 },
+  { 0x9a00, 0x2ec3, 0x5000 },
+  { 0x9a00, 0x2ebb, 0x4000 },
+  { 0x9a00, 0x2eb7, 0x3000 },
+  { 0x9a00, 0x2eb5, 0x2000 },
+  { 0x1a00, 0x2eb4, 0x0000 },
+  { 0x1a00, 0x2eb6, 0x0000 },
+  { 0x9a00, 0x2eb9, 0x2000 },
+  { 0x1a00, 0x2eb8, 0x0000 },
+  { 0x1a00, 0x2eba, 0x0000 },
+  { 0x9a00, 0x2ebf, 0x3000 },
+  { 0x9a00, 0x2ebd, 0x2000 },
+  { 0x1a00, 0x2ebc, 0x0000 },
+  { 0x1a00, 0x2ebe, 0x0000 },
+  { 0x9a00, 0x2ec1, 0x2000 },
+  { 0x1a00, 0x2ec0, 0x0000 },
+  { 0x1a00, 0x2ec2, 0x0000 },
+  { 0x9a00, 0x2ecb, 0x4000 },
+  { 0x9a00, 0x2ec7, 0x3000 },
+  { 0x9a00, 0x2ec5, 0x2000 },
+  { 0x1a00, 0x2ec4, 0x0000 },
+  { 0x1a00, 0x2ec6, 0x0000 },
+  { 0x9a00, 0x2ec9, 0x2000 },
+  { 0x1a00, 0x2ec8, 0x0000 },
+  { 0x1a00, 0x2eca, 0x0000 },
+  { 0x9a00, 0x2ecf, 0x3000 },
+  { 0x9a00, 0x2ecd, 0x2000 },
+  { 0x1a00, 0x2ecc, 0x0000 },
+  { 0x1a00, 0x2ece, 0x0000 },
+  { 0x9a00, 0x2ed1, 0x2000 },
+  { 0x1a00, 0x2ed0, 0x0000 },
+  { 0x1a00, 0x2ed2, 0x0000 },
+  { 0x9a00, 0x2ef3, 0x6000 },
+  { 0x9a00, 0x2ee3, 0x5000 },
+  { 0x9a00, 0x2edb, 0x4000 },
+  { 0x9a00, 0x2ed7, 0x3000 },
+  { 0x9a00, 0x2ed5, 0x2000 },
+  { 0x1a00, 0x2ed4, 0x0000 },
+  { 0x1a00, 0x2ed6, 0x0000 },
+  { 0x9a00, 0x2ed9, 0x2000 },
+  { 0x1a00, 0x2ed8, 0x0000 },
+  { 0x1a00, 0x2eda, 0x0000 },
+  { 0x9a00, 0x2edf, 0x3000 },
+  { 0x9a00, 0x2edd, 0x2000 },
+  { 0x1a00, 0x2edc, 0x0000 },
+  { 0x1a00, 0x2ede, 0x0000 },
+  { 0x9a00, 0x2ee1, 0x2000 },
+  { 0x1a00, 0x2ee0, 0x0000 },
+  { 0x1a00, 0x2ee2, 0x0000 },
+  { 0x9a00, 0x2eeb, 0x4000 },
+  { 0x9a00, 0x2ee7, 0x3000 },
+  { 0x9a00, 0x2ee5, 0x2000 },
+  { 0x1a00, 0x2ee4, 0x0000 },
+  { 0x1a00, 0x2ee6, 0x0000 },
+  { 0x9a00, 0x2ee9, 0x2000 },
+  { 0x1a00, 0x2ee8, 0x0000 },
+  { 0x1a00, 0x2eea, 0x0000 },
+  { 0x9a00, 0x2eef, 0x3000 },
+  { 0x9a00, 0x2eed, 0x2000 },
+  { 0x1a00, 0x2eec, 0x0000 },
+  { 0x1a00, 0x2eee, 0x0000 },
+  { 0x9a00, 0x2ef1, 0x2000 },
+  { 0x1a00, 0x2ef0, 0x0000 },
+  { 0x1a00, 0x2ef2, 0x0000 },
+  { 0x9a00, 0x2f0f, 0x5000 },
+  { 0x9a00, 0x2f07, 0x4000 },
+  { 0x9a00, 0x2f03, 0x3000 },
+  { 0x9a00, 0x2f01, 0x2000 },
+  { 0x1a00, 0x2f00, 0x0000 },
+  { 0x1a00, 0x2f02, 0x0000 },
+  { 0x9a00, 0x2f05, 0x2000 },
+  { 0x1a00, 0x2f04, 0x0000 },
+  { 0x1a00, 0x2f06, 0x0000 },
+  { 0x9a00, 0x2f0b, 0x3000 },
+  { 0x9a00, 0x2f09, 0x2000 },
+  { 0x1a00, 0x2f08, 0x0000 },
+  { 0x1a00, 0x2f0a, 0x0000 },
+  { 0x9a00, 0x2f0d, 0x2000 },
+  { 0x1a00, 0x2f0c, 0x0000 },
+  { 0x1a00, 0x2f0e, 0x0000 },
+  { 0x9a00, 0x2f17, 0x4000 },
+  { 0x9a00, 0x2f13, 0x3000 },
+  { 0x9a00, 0x2f11, 0x2000 },
+  { 0x1a00, 0x2f10, 0x0000 },
+  { 0x1a00, 0x2f12, 0x0000 },
+  { 0x9a00, 0x2f15, 0x2000 },
+  { 0x1a00, 0x2f14, 0x0000 },
+  { 0x1a00, 0x2f16, 0x0000 },
+  { 0x9a00, 0x2f1b, 0x3000 },
+  { 0x9a00, 0x2f19, 0x2000 },
+  { 0x1a00, 0x2f18, 0x0000 },
+  { 0x1a00, 0x2f1a, 0x0000 },
+  { 0x9a00, 0x2f1d, 0x2000 },
+  { 0x1a00, 0x2f1c, 0x0000 },
+  { 0x1a00, 0x2f1e, 0x0000 },
+  { 0x8701, 0x00f0, 0xd000 },
+  { 0x8700, 0xa34d, 0xc000 },
+  { 0x9a00, 0x3391, 0xb000 },
+  { 0x8700, 0x3149, 0xa000 },
+  { 0x9500, 0x303d, 0x9000 },
+  { 0x9a00, 0x2f9f, 0x8000 },
+  { 0x9a00, 0x2f5f, 0x7000 },
+  { 0x9a00, 0x2f3f, 0x6000 },
+  { 0x9a00, 0x2f2f, 0x5000 },
+  { 0x9a00, 0x2f27, 0x4000 },
+  { 0x9a00, 0x2f23, 0x3000 },
+  { 0x9a00, 0x2f21, 0x2000 },
+  { 0x1a00, 0x2f20, 0x0000 },
+  { 0x1a00, 0x2f22, 0x0000 },
+  { 0x9a00, 0x2f25, 0x2000 },
+  { 0x1a00, 0x2f24, 0x0000 },
+  { 0x1a00, 0x2f26, 0x0000 },
+  { 0x9a00, 0x2f2b, 0x3000 },
+  { 0x9a00, 0x2f29, 0x2000 },
+  { 0x1a00, 0x2f28, 0x0000 },
+  { 0x1a00, 0x2f2a, 0x0000 },
+  { 0x9a00, 0x2f2d, 0x2000 },
+  { 0x1a00, 0x2f2c, 0x0000 },
+  { 0x1a00, 0x2f2e, 0x0000 },
+  { 0x9a00, 0x2f37, 0x4000 },
+  { 0x9a00, 0x2f33, 0x3000 },
+  { 0x9a00, 0x2f31, 0x2000 },
+  { 0x1a00, 0x2f30, 0x0000 },
+  { 0x1a00, 0x2f32, 0x0000 },
+  { 0x9a00, 0x2f35, 0x2000 },
+  { 0x1a00, 0x2f34, 0x0000 },
+  { 0x1a00, 0x2f36, 0x0000 },
+  { 0x9a00, 0x2f3b, 0x3000 },
+  { 0x9a00, 0x2f39, 0x2000 },
+  { 0x1a00, 0x2f38, 0x0000 },
+  { 0x1a00, 0x2f3a, 0x0000 },
+  { 0x9a00, 0x2f3d, 0x2000 },
+  { 0x1a00, 0x2f3c, 0x0000 },
+  { 0x1a00, 0x2f3e, 0x0000 },
+  { 0x9a00, 0x2f4f, 0x5000 },
+  { 0x9a00, 0x2f47, 0x4000 },
+  { 0x9a00, 0x2f43, 0x3000 },
+  { 0x9a00, 0x2f41, 0x2000 },
+  { 0x1a00, 0x2f40, 0x0000 },
+  { 0x1a00, 0x2f42, 0x0000 },
+  { 0x9a00, 0x2f45, 0x2000 },
+  { 0x1a00, 0x2f44, 0x0000 },
+  { 0x1a00, 0x2f46, 0x0000 },
+  { 0x9a00, 0x2f4b, 0x3000 },
+  { 0x9a00, 0x2f49, 0x2000 },
+  { 0x1a00, 0x2f48, 0x0000 },
+  { 0x1a00, 0x2f4a, 0x0000 },
+  { 0x9a00, 0x2f4d, 0x2000 },
+  { 0x1a00, 0x2f4c, 0x0000 },
+  { 0x1a00, 0x2f4e, 0x0000 },
+  { 0x9a00, 0x2f57, 0x4000 },
+  { 0x9a00, 0x2f53, 0x3000 },
+  { 0x9a00, 0x2f51, 0x2000 },
+  { 0x1a00, 0x2f50, 0x0000 },
+  { 0x1a00, 0x2f52, 0x0000 },
+  { 0x9a00, 0x2f55, 0x2000 },
+  { 0x1a00, 0x2f54, 0x0000 },
+  { 0x1a00, 0x2f56, 0x0000 },
+  { 0x9a00, 0x2f5b, 0x3000 },
+  { 0x9a00, 0x2f59, 0x2000 },
+  { 0x1a00, 0x2f58, 0x0000 },
+  { 0x1a00, 0x2f5a, 0x0000 },
+  { 0x9a00, 0x2f5d, 0x2000 },
+  { 0x1a00, 0x2f5c, 0x0000 },
+  { 0x1a00, 0x2f5e, 0x0000 },
+  { 0x9a00, 0x2f7f, 0x6000 },
+  { 0x9a00, 0x2f6f, 0x5000 },
+  { 0x9a00, 0x2f67, 0x4000 },
+  { 0x9a00, 0x2f63, 0x3000 },
+  { 0x9a00, 0x2f61, 0x2000 },
+  { 0x1a00, 0x2f60, 0x0000 },
+  { 0x1a00, 0x2f62, 0x0000 },
+  { 0x9a00, 0x2f65, 0x2000 },
+  { 0x1a00, 0x2f64, 0x0000 },
+  { 0x1a00, 0x2f66, 0x0000 },
+  { 0x9a00, 0x2f6b, 0x3000 },
+  { 0x9a00, 0x2f69, 0x2000 },
+  { 0x1a00, 0x2f68, 0x0000 },
+  { 0x1a00, 0x2f6a, 0x0000 },
+  { 0x9a00, 0x2f6d, 0x2000 },
+  { 0x1a00, 0x2f6c, 0x0000 },
+  { 0x1a00, 0x2f6e, 0x0000 },
+  { 0x9a00, 0x2f77, 0x4000 },
+  { 0x9a00, 0x2f73, 0x3000 },
+  { 0x9a00, 0x2f71, 0x2000 },
+  { 0x1a00, 0x2f70, 0x0000 },
+  { 0x1a00, 0x2f72, 0x0000 },
+  { 0x9a00, 0x2f75, 0x2000 },
+  { 0x1a00, 0x2f74, 0x0000 },
+  { 0x1a00, 0x2f76, 0x0000 },
+  { 0x9a00, 0x2f7b, 0x3000 },
+  { 0x9a00, 0x2f79, 0x2000 },
+  { 0x1a00, 0x2f78, 0x0000 },
+  { 0x1a00, 0x2f7a, 0x0000 },
+  { 0x9a00, 0x2f7d, 0x2000 },
+  { 0x1a00, 0x2f7c, 0x0000 },
+  { 0x1a00, 0x2f7e, 0x0000 },
+  { 0x9a00, 0x2f8f, 0x5000 },
+  { 0x9a00, 0x2f87, 0x4000 },
+  { 0x9a00, 0x2f83, 0x3000 },
+  { 0x9a00, 0x2f81, 0x2000 },
+  { 0x1a00, 0x2f80, 0x0000 },
+  { 0x1a00, 0x2f82, 0x0000 },
+  { 0x9a00, 0x2f85, 0x2000 },
+  { 0x1a00, 0x2f84, 0x0000 },
+  { 0x1a00, 0x2f86, 0x0000 },
+  { 0x9a00, 0x2f8b, 0x3000 },
+  { 0x9a00, 0x2f89, 0x2000 },
+  { 0x1a00, 0x2f88, 0x0000 },
+  { 0x1a00, 0x2f8a, 0x0000 },
+  { 0x9a00, 0x2f8d, 0x2000 },
+  { 0x1a00, 0x2f8c, 0x0000 },
+  { 0x1a00, 0x2f8e, 0x0000 },
+  { 0x9a00, 0x2f97, 0x4000 },
+  { 0x9a00, 0x2f93, 0x3000 },
+  { 0x9a00, 0x2f91, 0x2000 },
+  { 0x1a00, 0x2f90, 0x0000 },
+  { 0x1a00, 0x2f92, 0x0000 },
+  { 0x9a00, 0x2f95, 0x2000 },
+  { 0x1a00, 0x2f94, 0x0000 },
+  { 0x1a00, 0x2f96, 0x0000 },
+  { 0x9a00, 0x2f9b, 0x3000 },
+  { 0x9a00, 0x2f99, 0x2000 },
+  { 0x1a00, 0x2f98, 0x0000 },
+  { 0x1a00, 0x2f9a, 0x0000 },
+  { 0x9a00, 0x2f9d, 0x2000 },
+  { 0x1a00, 0x2f9c, 0x0000 },
+  { 0x1a00, 0x2f9e, 0x0000 },
+  { 0x9a00, 0x2ff9, 0x7000 },
+  { 0x9a00, 0x2fbf, 0x6000 },
+  { 0x9a00, 0x2faf, 0x5000 },
+  { 0x9a00, 0x2fa7, 0x4000 },
+  { 0x9a00, 0x2fa3, 0x3000 },
+  { 0x9a00, 0x2fa1, 0x2000 },
+  { 0x1a00, 0x2fa0, 0x0000 },
+  { 0x1a00, 0x2fa2, 0x0000 },
+  { 0x9a00, 0x2fa5, 0x2000 },
+  { 0x1a00, 0x2fa4, 0x0000 },
+  { 0x1a00, 0x2fa6, 0x0000 },
+  { 0x9a00, 0x2fab, 0x3000 },
+  { 0x9a00, 0x2fa9, 0x2000 },
+  { 0x1a00, 0x2fa8, 0x0000 },
+  { 0x1a00, 0x2faa, 0x0000 },
+  { 0x9a00, 0x2fad, 0x2000 },
+  { 0x1a00, 0x2fac, 0x0000 },
+  { 0x1a00, 0x2fae, 0x0000 },
+  { 0x9a00, 0x2fb7, 0x4000 },
+  { 0x9a00, 0x2fb3, 0x3000 },
+  { 0x9a00, 0x2fb1, 0x2000 },
+  { 0x1a00, 0x2fb0, 0x0000 },
+  { 0x1a00, 0x2fb2, 0x0000 },
+  { 0x9a00, 0x2fb5, 0x2000 },
+  { 0x1a00, 0x2fb4, 0x0000 },
+  { 0x1a00, 0x2fb6, 0x0000 },
+  { 0x9a00, 0x2fbb, 0x3000 },
+  { 0x9a00, 0x2fb9, 0x2000 },
+  { 0x1a00, 0x2fb8, 0x0000 },
+  { 0x1a00, 0x2fba, 0x0000 },
+  { 0x9a00, 0x2fbd, 0x2000 },
+  { 0x1a00, 0x2fbc, 0x0000 },
+  { 0x1a00, 0x2fbe, 0x0000 },
+  { 0x9a00, 0x2fcf, 0x5000 },
+  { 0x9a00, 0x2fc7, 0x4000 },
+  { 0x9a00, 0x2fc3, 0x3000 },
+  { 0x9a00, 0x2fc1, 0x2000 },
+  { 0x1a00, 0x2fc0, 0x0000 },
+  { 0x1a00, 0x2fc2, 0x0000 },
+  { 0x9a00, 0x2fc5, 0x2000 },
+  { 0x1a00, 0x2fc4, 0x0000 },
+  { 0x1a00, 0x2fc6, 0x0000 },
+  { 0x9a00, 0x2fcb, 0x3000 },
+  { 0x9a00, 0x2fc9, 0x2000 },
+  { 0x1a00, 0x2fc8, 0x0000 },
+  { 0x1a00, 0x2fca, 0x0000 },
+  { 0x9a00, 0x2fcd, 0x2000 },
+  { 0x1a00, 0x2fcc, 0x0000 },
+  { 0x1a00, 0x2fce, 0x0000 },
+  { 0x9a00, 0x2ff1, 0x4000 },
+  { 0x9a00, 0x2fd3, 0x3000 },
+  { 0x9a00, 0x2fd1, 0x2000 },
+  { 0x1a00, 0x2fd0, 0x0000 },
+  { 0x1a00, 0x2fd2, 0x0000 },
+  { 0x9a00, 0x2fd5, 0x2000 },
+  { 0x1a00, 0x2fd4, 0x0000 },
+  { 0x1a00, 0x2ff0, 0x0000 },
+  { 0x9a00, 0x2ff5, 0x3000 },
+  { 0x9a00, 0x2ff3, 0x2000 },
+  { 0x1a00, 0x2ff2, 0x0000 },
+  { 0x1a00, 0x2ff4, 0x0000 },
+  { 0x9a00, 0x2ff7, 0x2000 },
+  { 0x1a00, 0x2ff6, 0x0000 },
+  { 0x1a00, 0x2ff8, 0x0000 },
+  { 0x9600, 0x301d, 0x6000 },
+  { 0x9200, 0x300d, 0x5000 },
+  { 0x8600, 0x3005, 0x4000 },
+  { 0x9500, 0x3001, 0x3000 },
+  { 0x9a00, 0x2ffb, 0x2000 },
+  { 0x1a00, 0x2ffa, 0x0000 },
+  { 0x1d00, 0x3000, 0x0000 },
+  { 0x9500, 0x3003, 0x2000 },
+  { 0x1500, 0x3002, 0x0000 },
+  { 0x1a00, 0x3004, 0x0000 },
+  { 0x9200, 0x3009, 0x3000 },
+  { 0x8e00, 0x3007, 0x2000 },
+  { 0x0700, 0x3006, 0x0000 },
+  { 0x1600, 0x3008, 0x0000 },
+  { 0x9200, 0x300b, 0x2000 },
+  { 0x1600, 0x300a, 0x0000 },
+  { 0x1600, 0x300c, 0x0000 },
+  { 0x9200, 0x3015, 0x4000 },
+  { 0x9200, 0x3011, 0x3000 },
+  { 0x9200, 0x300f, 0x2000 },
+  { 0x1600, 0x300e, 0x0000 },
+  { 0x1600, 0x3010, 0x0000 },
+  { 0x9a00, 0x3013, 0x2000 },
+  { 0x1a00, 0x3012, 0x0000 },
+  { 0x1600, 0x3014, 0x0000 },
+  { 0x9200, 0x3019, 0x3000 },
+  { 0x9200, 0x3017, 0x2000 },
+  { 0x1600, 0x3016, 0x0000 },
+  { 0x1600, 0x3018, 0x0000 },
+  { 0x9200, 0x301b, 0x2000 },
+  { 0x1600, 0x301a, 0x0000 },
+  { 0x1100, 0x301c, 0x0000 },
+  { 0x8c00, 0x302d, 0x5000 },
+  { 0x8e00, 0x3025, 0x4000 },
+  { 0x8e00, 0x3021, 0x3000 },
+  { 0x9200, 0x301f, 0x2000 },
+  { 0x1200, 0x301e, 0x0000 },
+  { 0x1a00, 0x3020, 0x0000 },
+  { 0x8e00, 0x3023, 0x2000 },
+  { 0x0e00, 0x3022, 0x0000 },
+  { 0x0e00, 0x3024, 0x0000 },
+  { 0x8e00, 0x3029, 0x3000 },
+  { 0x8e00, 0x3027, 0x2000 },
+  { 0x0e00, 0x3026, 0x0000 },
+  { 0x0e00, 0x3028, 0x0000 },
+  { 0x8c00, 0x302b, 0x2000 },
+  { 0x0c00, 0x302a, 0x0000 },
+  { 0x0c00, 0x302c, 0x0000 },
+  { 0x8600, 0x3035, 0x4000 },
+  { 0x8600, 0x3031, 0x3000 },
+  { 0x8c00, 0x302f, 0x2000 },
+  { 0x0c00, 0x302e, 0x0000 },
+  { 0x1100, 0x3030, 0x0000 },
+  { 0x8600, 0x3033, 0x2000 },
+  { 0x0600, 0x3032, 0x0000 },
+  { 0x0600, 0x3034, 0x0000 },
+  { 0x8e00, 0x3039, 0x3000 },
+  { 0x9a00, 0x3037, 0x2000 },
+  { 0x1a00, 0x3036, 0x0000 },
+  { 0x0e00, 0x3038, 0x0000 },
+  { 0x8600, 0x303b, 0x2000 },
+  { 0x0e00, 0x303a, 0x0000 },
+  { 0x0700, 0x303c, 0x0000 },
+  { 0x8700, 0x30c0, 0x8000 },
+  { 0x8700, 0x307e, 0x7000 },
+  { 0x8700, 0x305e, 0x6000 },
+  { 0x8700, 0x304e, 0x5000 },
+  { 0x8700, 0x3046, 0x4000 },
+  { 0x8700, 0x3042, 0x3000 },
+  { 0x9a00, 0x303f, 0x2000 },
+  { 0x1a00, 0x303e, 0x0000 },
+  { 0x0700, 0x3041, 0x0000 },
+  { 0x8700, 0x3044, 0x2000 },
+  { 0x0700, 0x3043, 0x0000 },
+  { 0x0700, 0x3045, 0x0000 },
+  { 0x8700, 0x304a, 0x3000 },
+  { 0x8700, 0x3048, 0x2000 },
+  { 0x0700, 0x3047, 0x0000 },
+  { 0x0700, 0x3049, 0x0000 },
+  { 0x8700, 0x304c, 0x2000 },
+  { 0x0700, 0x304b, 0x0000 },
+  { 0x0700, 0x304d, 0x0000 },
+  { 0x8700, 0x3056, 0x4000 },
+  { 0x8700, 0x3052, 0x3000 },
+  { 0x8700, 0x3050, 0x2000 },
+  { 0x0700, 0x304f, 0x0000 },
+  { 0x0700, 0x3051, 0x0000 },
+  { 0x8700, 0x3054, 0x2000 },
+  { 0x0700, 0x3053, 0x0000 },
+  { 0x0700, 0x3055, 0x0000 },
+  { 0x8700, 0x305a, 0x3000 },
+  { 0x8700, 0x3058, 0x2000 },
+  { 0x0700, 0x3057, 0x0000 },
+  { 0x0700, 0x3059, 0x0000 },
+  { 0x8700, 0x305c, 0x2000 },
+  { 0x0700, 0x305b, 0x0000 },
+  { 0x0700, 0x305d, 0x0000 },
+  { 0x8700, 0x306e, 0x5000 },
+  { 0x8700, 0x3066, 0x4000 },
+  { 0x8700, 0x3062, 0x3000 },
+  { 0x8700, 0x3060, 0x2000 },
+  { 0x0700, 0x305f, 0x0000 },
+  { 0x0700, 0x3061, 0x0000 },
+  { 0x8700, 0x3064, 0x2000 },
+  { 0x0700, 0x3063, 0x0000 },
+  { 0x0700, 0x3065, 0x0000 },
+  { 0x8700, 0x306a, 0x3000 },
+  { 0x8700, 0x3068, 0x2000 },
+  { 0x0700, 0x3067, 0x0000 },
+  { 0x0700, 0x3069, 0x0000 },
+  { 0x8700, 0x306c, 0x2000 },
+  { 0x0700, 0x306b, 0x0000 },
+  { 0x0700, 0x306d, 0x0000 },
+  { 0x8700, 0x3076, 0x4000 },
+  { 0x8700, 0x3072, 0x3000 },
+  { 0x8700, 0x3070, 0x2000 },
+  { 0x0700, 0x306f, 0x0000 },
+  { 0x0700, 0x3071, 0x0000 },
+  { 0x8700, 0x3074, 0x2000 },
+  { 0x0700, 0x3073, 0x0000 },
+  { 0x0700, 0x3075, 0x0000 },
+  { 0x8700, 0x307a, 0x3000 },
+  { 0x8700, 0x3078, 0x2000 },
+  { 0x0700, 0x3077, 0x0000 },
+  { 0x0700, 0x3079, 0x0000 },
+  { 0x8700, 0x307c, 0x2000 },
+  { 0x0700, 0x307b, 0x0000 },
+  { 0x0700, 0x307d, 0x0000 },
+  { 0x9100, 0x30a0, 0x6000 },
+  { 0x8700, 0x308e, 0x5000 },
+  { 0x8700, 0x3086, 0x4000 },
+  { 0x8700, 0x3082, 0x3000 },
+  { 0x8700, 0x3080, 0x2000 },
+  { 0x0700, 0x307f, 0x0000 },
+  { 0x0700, 0x3081, 0x0000 },
+  { 0x8700, 0x3084, 0x2000 },
+  { 0x0700, 0x3083, 0x0000 },
+  { 0x0700, 0x3085, 0x0000 },
+  { 0x8700, 0x308a, 0x3000 },
+  { 0x8700, 0x3088, 0x2000 },
+  { 0x0700, 0x3087, 0x0000 },
+  { 0x0700, 0x3089, 0x0000 },
+  { 0x8700, 0x308c, 0x2000 },
+  { 0x0700, 0x308b, 0x0000 },
+  { 0x0700, 0x308d, 0x0000 },
+  { 0x8700, 0x3096, 0x4000 },
+  { 0x8700, 0x3092, 0x3000 },
+  { 0x8700, 0x3090, 0x2000 },
+  { 0x0700, 0x308f, 0x0000 },
+  { 0x0700, 0x3091, 0x0000 },
+  { 0x8700, 0x3094, 0x2000 },
+  { 0x0700, 0x3093, 0x0000 },
+  { 0x0700, 0x3095, 0x0000 },
+  { 0x9800, 0x309c, 0x3000 },
+  { 0x8c00, 0x309a, 0x2000 },
+  { 0x0c00, 0x3099, 0x0000 },
+  { 0x1800, 0x309b, 0x0000 },
+  { 0x8600, 0x309e, 0x2000 },
+  { 0x0600, 0x309d, 0x0000 },
+  { 0x0700, 0x309f, 0x0000 },
+  { 0x8700, 0x30b0, 0x5000 },
+  { 0x8700, 0x30a8, 0x4000 },
+  { 0x8700, 0x30a4, 0x3000 },
+  { 0x8700, 0x30a2, 0x2000 },
+  { 0x0700, 0x30a1, 0x0000 },
+  { 0x0700, 0x30a3, 0x0000 },
+  { 0x8700, 0x30a6, 0x2000 },
+  { 0x0700, 0x30a5, 0x0000 },
+  { 0x0700, 0x30a7, 0x0000 },
+  { 0x8700, 0x30ac, 0x3000 },
+  { 0x8700, 0x30aa, 0x2000 },
+  { 0x0700, 0x30a9, 0x0000 },
+  { 0x0700, 0x30ab, 0x0000 },
+  { 0x8700, 0x30ae, 0x2000 },
+  { 0x0700, 0x30ad, 0x0000 },
+  { 0x0700, 0x30af, 0x0000 },
+  { 0x8700, 0x30b8, 0x4000 },
+  { 0x8700, 0x30b4, 0x3000 },
+  { 0x8700, 0x30b2, 0x2000 },
+  { 0x0700, 0x30b1, 0x0000 },
+  { 0x0700, 0x30b3, 0x0000 },
+  { 0x8700, 0x30b6, 0x2000 },
+  { 0x0700, 0x30b5, 0x0000 },
+  { 0x0700, 0x30b7, 0x0000 },
+  { 0x8700, 0x30bc, 0x3000 },
+  { 0x8700, 0x30ba, 0x2000 },
+  { 0x0700, 0x30b9, 0x0000 },
+  { 0x0700, 0x30bb, 0x0000 },
+  { 0x8700, 0x30be, 0x2000 },
+  { 0x0700, 0x30bd, 0x0000 },
+  { 0x0700, 0x30bf, 0x0000 },
+  { 0x8700, 0x3105, 0x7000 },
+  { 0x8700, 0x30e0, 0x6000 },
+  { 0x8700, 0x30d0, 0x5000 },
+  { 0x8700, 0x30c8, 0x4000 },
+  { 0x8700, 0x30c4, 0x3000 },
+  { 0x8700, 0x30c2, 0x2000 },
+  { 0x0700, 0x30c1, 0x0000 },
+  { 0x0700, 0x30c3, 0x0000 },
+  { 0x8700, 0x30c6, 0x2000 },
+  { 0x0700, 0x30c5, 0x0000 },
+  { 0x0700, 0x30c7, 0x0000 },
+  { 0x8700, 0x30cc, 0x3000 },
+  { 0x8700, 0x30ca, 0x2000 },
+  { 0x0700, 0x30c9, 0x0000 },
+  { 0x0700, 0x30cb, 0x0000 },
+  { 0x8700, 0x30ce, 0x2000 },
+  { 0x0700, 0x30cd, 0x0000 },
+  { 0x0700, 0x30cf, 0x0000 },
+  { 0x8700, 0x30d8, 0x4000 },
+  { 0x8700, 0x30d4, 0x3000 },
+  { 0x8700, 0x30d2, 0x2000 },
+  { 0x0700, 0x30d1, 0x0000 },
+  { 0x0700, 0x30d3, 0x0000 },
+  { 0x8700, 0x30d6, 0x2000 },
+  { 0x0700, 0x30d5, 0x0000 },
+  { 0x0700, 0x30d7, 0x0000 },
+  { 0x8700, 0x30dc, 0x3000 },
+  { 0x8700, 0x30da, 0x2000 },
+  { 0x0700, 0x30d9, 0x0000 },
+  { 0x0700, 0x30db, 0x0000 },
+  { 0x8700, 0x30de, 0x2000 },
+  { 0x0700, 0x30dd, 0x0000 },
+  { 0x0700, 0x30df, 0x0000 },
+  { 0x8700, 0x30f0, 0x5000 },
+  { 0x8700, 0x30e8, 0x4000 },
+  { 0x8700, 0x30e4, 0x3000 },
+  { 0x8700, 0x30e2, 0x2000 },
+  { 0x0700, 0x30e1, 0x0000 },
+  { 0x0700, 0x30e3, 0x0000 },
+  { 0x8700, 0x30e6, 0x2000 },
+  { 0x0700, 0x30e5, 0x0000 },
+  { 0x0700, 0x30e7, 0x0000 },
+  { 0x8700, 0x30ec, 0x3000 },
+  { 0x8700, 0x30ea, 0x2000 },
+  { 0x0700, 0x30e9, 0x0000 },
+  { 0x0700, 0x30eb, 0x0000 },
+  { 0x8700, 0x30ee, 0x2000 },
+  { 0x0700, 0x30ed, 0x0000 },
+  { 0x0700, 0x30ef, 0x0000 },
+  { 0x8700, 0x30f8, 0x4000 },
+  { 0x8700, 0x30f4, 0x3000 },
+  { 0x8700, 0x30f2, 0x2000 },
+  { 0x0700, 0x30f1, 0x0000 },
+  { 0x0700, 0x30f3, 0x0000 },
+  { 0x8700, 0x30f6, 0x2000 },
+  { 0x0700, 0x30f5, 0x0000 },
+  { 0x0700, 0x30f7, 0x0000 },
+  { 0x8600, 0x30fc, 0x3000 },
+  { 0x8700, 0x30fa, 0x2000 },
+  { 0x0700, 0x30f9, 0x0000 },
+  { 0x1000, 0x30fb, 0x0000 },
+  { 0x8600, 0x30fe, 0x2000 },
+  { 0x0600, 0x30fd, 0x0000 },
+  { 0x0700, 0x30ff, 0x0000 },
+  { 0x8700, 0x3125, 0x6000 },
+  { 0x8700, 0x3115, 0x5000 },
+  { 0x8700, 0x310d, 0x4000 },
+  { 0x8700, 0x3109, 0x3000 },
+  { 0x8700, 0x3107, 0x2000 },
+  { 0x0700, 0x3106, 0x0000 },
+  { 0x0700, 0x3108, 0x0000 },
+  { 0x8700, 0x310b, 0x2000 },
+  { 0x0700, 0x310a, 0x0000 },
+  { 0x0700, 0x310c, 0x0000 },
+  { 0x8700, 0x3111, 0x3000 },
+  { 0x8700, 0x310f, 0x2000 },
+  { 0x0700, 0x310e, 0x0000 },
+  { 0x0700, 0x3110, 0x0000 },
+  { 0x8700, 0x3113, 0x2000 },
+  { 0x0700, 0x3112, 0x0000 },
+  { 0x0700, 0x3114, 0x0000 },
+  { 0x8700, 0x311d, 0x4000 },
+  { 0x8700, 0x3119, 0x3000 },
+  { 0x8700, 0x3117, 0x2000 },
+  { 0x0700, 0x3116, 0x0000 },
+  { 0x0700, 0x3118, 0x0000 },
+  { 0x8700, 0x311b, 0x2000 },
+  { 0x0700, 0x311a, 0x0000 },
+  { 0x0700, 0x311c, 0x0000 },
+  { 0x8700, 0x3121, 0x3000 },
+  { 0x8700, 0x311f, 0x2000 },
+  { 0x0700, 0x311e, 0x0000 },
+  { 0x0700, 0x3120, 0x0000 },
+  { 0x8700, 0x3123, 0x2000 },
+  { 0x0700, 0x3122, 0x0000 },
+  { 0x0700, 0x3124, 0x0000 },
+  { 0x8700, 0x3139, 0x5000 },
+  { 0x8700, 0x3131, 0x4000 },
+  { 0x8700, 0x3129, 0x3000 },
+  { 0x8700, 0x3127, 0x2000 },
+  { 0x0700, 0x3126, 0x0000 },
+  { 0x0700, 0x3128, 0x0000 },
+  { 0x8700, 0x312b, 0x2000 },
+  { 0x0700, 0x312a, 0x0000 },
+  { 0x0700, 0x312c, 0x0000 },
+  { 0x8700, 0x3135, 0x3000 },
+  { 0x8700, 0x3133, 0x2000 },
+  { 0x0700, 0x3132, 0x0000 },
+  { 0x0700, 0x3134, 0x0000 },
+  { 0x8700, 0x3137, 0x2000 },
+  { 0x0700, 0x3136, 0x0000 },
+  { 0x0700, 0x3138, 0x0000 },
+  { 0x8700, 0x3141, 0x4000 },
+  { 0x8700, 0x313d, 0x3000 },
+  { 0x8700, 0x313b, 0x2000 },
+  { 0x0700, 0x313a, 0x0000 },
+  { 0x0700, 0x313c, 0x0000 },
+  { 0x8700, 0x313f, 0x2000 },
+  { 0x0700, 0x313e, 0x0000 },
+  { 0x0700, 0x3140, 0x0000 },
+  { 0x8700, 0x3145, 0x3000 },
+  { 0x8700, 0x3143, 0x2000 },
+  { 0x0700, 0x3142, 0x0000 },
+  { 0x0700, 0x3144, 0x0000 },
+  { 0x8700, 0x3147, 0x2000 },
+  { 0x0700, 0x3146, 0x0000 },
+  { 0x0700, 0x3148, 0x0000 },
+  { 0x9a00, 0x3290, 0x9000 },
+  { 0x9a00, 0x3202, 0x8000 },
+  { 0x8700, 0x3189, 0x7000 },
+  { 0x8700, 0x3169, 0x6000 },
+  { 0x8700, 0x3159, 0x5000 },
+  { 0x8700, 0x3151, 0x4000 },
+  { 0x8700, 0x314d, 0x3000 },
+  { 0x8700, 0x314b, 0x2000 },
+  { 0x0700, 0x314a, 0x0000 },
+  { 0x0700, 0x314c, 0x0000 },
+  { 0x8700, 0x314f, 0x2000 },
+  { 0x0700, 0x314e, 0x0000 },
+  { 0x0700, 0x3150, 0x0000 },
+  { 0x8700, 0x3155, 0x3000 },
+  { 0x8700, 0x3153, 0x2000 },
+  { 0x0700, 0x3152, 0x0000 },
+  { 0x0700, 0x3154, 0x0000 },
+  { 0x8700, 0x3157, 0x2000 },
+  { 0x0700, 0x3156, 0x0000 },
+  { 0x0700, 0x3158, 0x0000 },
+  { 0x8700, 0x3161, 0x4000 },
+  { 0x8700, 0x315d, 0x3000 },
+  { 0x8700, 0x315b, 0x2000 },
+  { 0x0700, 0x315a, 0x0000 },
+  { 0x0700, 0x315c, 0x0000 },
+  { 0x8700, 0x315f, 0x2000 },
+  { 0x0700, 0x315e, 0x0000 },
+  { 0x0700, 0x3160, 0x0000 },
+  { 0x8700, 0x3165, 0x3000 },
+  { 0x8700, 0x3163, 0x2000 },
+  { 0x0700, 0x3162, 0x0000 },
+  { 0x0700, 0x3164, 0x0000 },
+  { 0x8700, 0x3167, 0x2000 },
+  { 0x0700, 0x3166, 0x0000 },
+  { 0x0700, 0x3168, 0x0000 },
+  { 0x8700, 0x3179, 0x5000 },
+  { 0x8700, 0x3171, 0x4000 },
+  { 0x8700, 0x316d, 0x3000 },
+  { 0x8700, 0x316b, 0x2000 },
+  { 0x0700, 0x316a, 0x0000 },
+  { 0x0700, 0x316c, 0x0000 },
+  { 0x8700, 0x316f, 0x2000 },
+  { 0x0700, 0x316e, 0x0000 },
+  { 0x0700, 0x3170, 0x0000 },
+  { 0x8700, 0x3175, 0x3000 },
+  { 0x8700, 0x3173, 0x2000 },
+  { 0x0700, 0x3172, 0x0000 },
+  { 0x0700, 0x3174, 0x0000 },
+  { 0x8700, 0x3177, 0x2000 },
+  { 0x0700, 0x3176, 0x0000 },
+  { 0x0700, 0x3178, 0x0000 },
+  { 0x8700, 0x3181, 0x4000 },
+  { 0x8700, 0x317d, 0x3000 },
+  { 0x8700, 0x317b, 0x2000 },
+  { 0x0700, 0x317a, 0x0000 },
+  { 0x0700, 0x317c, 0x0000 },
+  { 0x8700, 0x317f, 0x2000 },
+  { 0x0700, 0x317e, 0x0000 },
+  { 0x0700, 0x3180, 0x0000 },
+  { 0x8700, 0x3185, 0x3000 },
+  { 0x8700, 0x3183, 0x2000 },
+  { 0x0700, 0x3182, 0x0000 },
+  { 0x0700, 0x3184, 0x0000 },
+  { 0x8700, 0x3187, 0x2000 },
+  { 0x0700, 0x3186, 0x0000 },
+  { 0x0700, 0x3188, 0x0000 },
+  { 0x8700, 0x31aa, 0x6000 },
+  { 0x9a00, 0x319a, 0x5000 },
+  { 0x8f00, 0x3192, 0x4000 },
+  { 0x8700, 0x318d, 0x3000 },
+  { 0x8700, 0x318b, 0x2000 },
+  { 0x0700, 0x318a, 0x0000 },
+  { 0x0700, 0x318c, 0x0000 },
+  { 0x9a00, 0x3190, 0x2000 },
+  { 0x0700, 0x318e, 0x0000 },
+  { 0x1a00, 0x3191, 0x0000 },
+  { 0x9a00, 0x3196, 0x3000 },
+  { 0x8f00, 0x3194, 0x2000 },
+  { 0x0f00, 0x3193, 0x0000 },
+  { 0x0f00, 0x3195, 0x0000 },
+  { 0x9a00, 0x3198, 0x2000 },
+  { 0x1a00, 0x3197, 0x0000 },
+  { 0x1a00, 0x3199, 0x0000 },
+  { 0x8700, 0x31a2, 0x4000 },
+  { 0x9a00, 0x319e, 0x3000 },
+  { 0x9a00, 0x319c, 0x2000 },
+  { 0x1a00, 0x319b, 0x0000 },
+  { 0x1a00, 0x319d, 0x0000 },
+  { 0x8700, 0x31a0, 0x2000 },
+  { 0x1a00, 0x319f, 0x0000 },
+  { 0x0700, 0x31a1, 0x0000 },
+  { 0x8700, 0x31a6, 0x3000 },
+  { 0x8700, 0x31a4, 0x2000 },
+  { 0x0700, 0x31a3, 0x0000 },
+  { 0x0700, 0x31a5, 0x0000 },
+  { 0x8700, 0x31a8, 0x2000 },
+  { 0x0700, 0x31a7, 0x0000 },
+  { 0x0700, 0x31a9, 0x0000 },
+  { 0x8700, 0x31f2, 0x5000 },
+  { 0x8700, 0x31b2, 0x4000 },
+  { 0x8700, 0x31ae, 0x3000 },
+  { 0x8700, 0x31ac, 0x2000 },
+  { 0x0700, 0x31ab, 0x0000 },
+  { 0x0700, 0x31ad, 0x0000 },
+  { 0x8700, 0x31b0, 0x2000 },
+  { 0x0700, 0x31af, 0x0000 },
+  { 0x0700, 0x31b1, 0x0000 },
+  { 0x8700, 0x31b6, 0x3000 },
+  { 0x8700, 0x31b4, 0x2000 },
+  { 0x0700, 0x31b3, 0x0000 },
+  { 0x0700, 0x31b5, 0x0000 },
+  { 0x8700, 0x31f0, 0x2000 },
+  { 0x0700, 0x31b7, 0x0000 },
+  { 0x0700, 0x31f1, 0x0000 },
+  { 0x8700, 0x31fa, 0x4000 },
+  { 0x8700, 0x31f6, 0x3000 },
+  { 0x8700, 0x31f4, 0x2000 },
+  { 0x0700, 0x31f3, 0x0000 },
+  { 0x0700, 0x31f5, 0x0000 },
+  { 0x8700, 0x31f8, 0x2000 },
+  { 0x0700, 0x31f7, 0x0000 },
+  { 0x0700, 0x31f9, 0x0000 },
+  { 0x8700, 0x31fe, 0x3000 },
+  { 0x8700, 0x31fc, 0x2000 },
+  { 0x0700, 0x31fb, 0x0000 },
+  { 0x0700, 0x31fd, 0x0000 },
+  { 0x9a00, 0x3200, 0x2000 },
+  { 0x0700, 0x31ff, 0x0000 },
+  { 0x1a00, 0x3201, 0x0000 },
+  { 0x9a00, 0x3243, 0x7000 },
+  { 0x8f00, 0x3223, 0x6000 },
+  { 0x9a00, 0x3212, 0x5000 },
+  { 0x9a00, 0x320a, 0x4000 },
+  { 0x9a00, 0x3206, 0x3000 },
+  { 0x9a00, 0x3204, 0x2000 },
+  { 0x1a00, 0x3203, 0x0000 },
+  { 0x1a00, 0x3205, 0x0000 },
+  { 0x9a00, 0x3208, 0x2000 },
+  { 0x1a00, 0x3207, 0x0000 },
+  { 0x1a00, 0x3209, 0x0000 },
+  { 0x9a00, 0x320e, 0x3000 },
+  { 0x9a00, 0x320c, 0x2000 },
+  { 0x1a00, 0x320b, 0x0000 },
+  { 0x1a00, 0x320d, 0x0000 },
+  { 0x9a00, 0x3210, 0x2000 },
+  { 0x1a00, 0x320f, 0x0000 },
+  { 0x1a00, 0x3211, 0x0000 },
+  { 0x9a00, 0x321a, 0x4000 },
+  { 0x9a00, 0x3216, 0x3000 },
+  { 0x9a00, 0x3214, 0x2000 },
+  { 0x1a00, 0x3213, 0x0000 },
+  { 0x1a00, 0x3215, 0x0000 },
+  { 0x9a00, 0x3218, 0x2000 },
+  { 0x1a00, 0x3217, 0x0000 },
+  { 0x1a00, 0x3219, 0x0000 },
+  { 0x9a00, 0x321e, 0x3000 },
+  { 0x9a00, 0x321c, 0x2000 },
+  { 0x1a00, 0x321b, 0x0000 },
+  { 0x1a00, 0x321d, 0x0000 },
+  { 0x8f00, 0x3221, 0x2000 },
+  { 0x0f00, 0x3220, 0x0000 },
+  { 0x0f00, 0x3222, 0x0000 },
+  { 0x9a00, 0x3233, 0x5000 },
+  { 0x9a00, 0x322b, 0x4000 },
+  { 0x8f00, 0x3227, 0x3000 },
+  { 0x8f00, 0x3225, 0x2000 },
+  { 0x0f00, 0x3224, 0x0000 },
+  { 0x0f00, 0x3226, 0x0000 },
+  { 0x8f00, 0x3229, 0x2000 },
+  { 0x0f00, 0x3228, 0x0000 },
+  { 0x1a00, 0x322a, 0x0000 },
+  { 0x9a00, 0x322f, 0x3000 },
+  { 0x9a00, 0x322d, 0x2000 },
+  { 0x1a00, 0x322c, 0x0000 },
+  { 0x1a00, 0x322e, 0x0000 },
+  { 0x9a00, 0x3231, 0x2000 },
+  { 0x1a00, 0x3230, 0x0000 },
+  { 0x1a00, 0x3232, 0x0000 },
+  { 0x9a00, 0x323b, 0x4000 },
+  { 0x9a00, 0x3237, 0x3000 },
+  { 0x9a00, 0x3235, 0x2000 },
+  { 0x1a00, 0x3234, 0x0000 },
+  { 0x1a00, 0x3236, 0x0000 },
+  { 0x9a00, 0x3239, 0x2000 },
+  { 0x1a00, 0x3238, 0x0000 },
+  { 0x1a00, 0x323a, 0x0000 },
+  { 0x9a00, 0x323f, 0x3000 },
+  { 0x9a00, 0x323d, 0x2000 },
+  { 0x1a00, 0x323c, 0x0000 },
+  { 0x1a00, 0x323e, 0x0000 },
+  { 0x9a00, 0x3241, 0x2000 },
+  { 0x1a00, 0x3240, 0x0000 },
+  { 0x1a00, 0x3242, 0x0000 },
+  { 0x9a00, 0x326f, 0x6000 },
+  { 0x8f00, 0x325f, 0x5000 },
+  { 0x8f00, 0x3257, 0x4000 },
+  { 0x8f00, 0x3253, 0x3000 },
+  { 0x8f00, 0x3251, 0x2000 },
+  { 0x1a00, 0x3250, 0x0000 },
+  { 0x0f00, 0x3252, 0x0000 },
+  { 0x8f00, 0x3255, 0x2000 },
+  { 0x0f00, 0x3254, 0x0000 },
+  { 0x0f00, 0x3256, 0x0000 },
+  { 0x8f00, 0x325b, 0x3000 },
+  { 0x8f00, 0x3259, 0x2000 },
+  { 0x0f00, 0x3258, 0x0000 },
+  { 0x0f00, 0x325a, 0x0000 },
+  { 0x8f00, 0x325d, 0x2000 },
+  { 0x0f00, 0x325c, 0x0000 },
+  { 0x0f00, 0x325e, 0x0000 },
+  { 0x9a00, 0x3267, 0x4000 },
+  { 0x9a00, 0x3263, 0x3000 },
+  { 0x9a00, 0x3261, 0x2000 },
+  { 0x1a00, 0x3260, 0x0000 },
+  { 0x1a00, 0x3262, 0x0000 },
+  { 0x9a00, 0x3265, 0x2000 },
+  { 0x1a00, 0x3264, 0x0000 },
+  { 0x1a00, 0x3266, 0x0000 },
+  { 0x9a00, 0x326b, 0x3000 },
+  { 0x9a00, 0x3269, 0x2000 },
+  { 0x1a00, 0x3268, 0x0000 },
+  { 0x1a00, 0x326a, 0x0000 },
+  { 0x9a00, 0x326d, 0x2000 },
+  { 0x1a00, 0x326c, 0x0000 },
+  { 0x1a00, 0x326e, 0x0000 },
+  { 0x8f00, 0x3280, 0x5000 },
+  { 0x9a00, 0x3277, 0x4000 },
+  { 0x9a00, 0x3273, 0x3000 },
+  { 0x9a00, 0x3271, 0x2000 },
+  { 0x1a00, 0x3270, 0x0000 },
+  { 0x1a00, 0x3272, 0x0000 },
+  { 0x9a00, 0x3275, 0x2000 },
+  { 0x1a00, 0x3274, 0x0000 },
+  { 0x1a00, 0x3276, 0x0000 },
+  { 0x9a00, 0x327b, 0x3000 },
+  { 0x9a00, 0x3279, 0x2000 },
+  { 0x1a00, 0x3278, 0x0000 },
+  { 0x1a00, 0x327a, 0x0000 },
+  { 0x9a00, 0x327d, 0x2000 },
+  { 0x1a00, 0x327c, 0x0000 },
+  { 0x1a00, 0x327f, 0x0000 },
+  { 0x8f00, 0x3288, 0x4000 },
+  { 0x8f00, 0x3284, 0x3000 },
+  { 0x8f00, 0x3282, 0x2000 },
+  { 0x0f00, 0x3281, 0x0000 },
+  { 0x0f00, 0x3283, 0x0000 },
+  { 0x8f00, 0x3286, 0x2000 },
+  { 0x0f00, 0x3285, 0x0000 },
+  { 0x0f00, 0x3287, 0x0000 },
+  { 0x9a00, 0x328c, 0x3000 },
+  { 0x9a00, 0x328a, 0x2000 },
+  { 0x0f00, 0x3289, 0x0000 },
+  { 0x1a00, 0x328b, 0x0000 },
+  { 0x9a00, 0x328e, 0x2000 },
+  { 0x1a00, 0x328d, 0x0000 },
+  { 0x1a00, 0x328f, 0x0000 },
+  { 0x9a00, 0x3311, 0x8000 },
+  { 0x9a00, 0x32d0, 0x7000 },
+  { 0x9a00, 0x32b0, 0x6000 },
+  { 0x9a00, 0x32a0, 0x5000 },
+  { 0x9a00, 0x3298, 0x4000 },
+  { 0x9a00, 0x3294, 0x3000 },
+  { 0x9a00, 0x3292, 0x2000 },
+  { 0x1a00, 0x3291, 0x0000 },
+  { 0x1a00, 0x3293, 0x0000 },
+  { 0x9a00, 0x3296, 0x2000 },
+  { 0x1a00, 0x3295, 0x0000 },
+  { 0x1a00, 0x3297, 0x0000 },
+  { 0x9a00, 0x329c, 0x3000 },
+  { 0x9a00, 0x329a, 0x2000 },
+  { 0x1a00, 0x3299, 0x0000 },
+  { 0x1a00, 0x329b, 0x0000 },
+  { 0x9a00, 0x329e, 0x2000 },
+  { 0x1a00, 0x329d, 0x0000 },
+  { 0x1a00, 0x329f, 0x0000 },
+  { 0x9a00, 0x32a8, 0x4000 },
+  { 0x9a00, 0x32a4, 0x3000 },
+  { 0x9a00, 0x32a2, 0x2000 },
+  { 0x1a00, 0x32a1, 0x0000 },
+  { 0x1a00, 0x32a3, 0x0000 },
+  { 0x9a00, 0x32a6, 0x2000 },
+  { 0x1a00, 0x32a5, 0x0000 },
+  { 0x1a00, 0x32a7, 0x0000 },
+  { 0x9a00, 0x32ac, 0x3000 },
+  { 0x9a00, 0x32aa, 0x2000 },
+  { 0x1a00, 0x32a9, 0x0000 },
+  { 0x1a00, 0x32ab, 0x0000 },
+  { 0x9a00, 0x32ae, 0x2000 },
+  { 0x1a00, 0x32ad, 0x0000 },
+  { 0x1a00, 0x32af, 0x0000 },
+  { 0x9a00, 0x32c0, 0x5000 },
+  { 0x8f00, 0x32b8, 0x4000 },
+  { 0x8f00, 0x32b4, 0x3000 },
+  { 0x8f00, 0x32b2, 0x2000 },
+  { 0x0f00, 0x32b1, 0x0000 },
+  { 0x0f00, 0x32b3, 0x0000 },
+  { 0x8f00, 0x32b6, 0x2000 },
+  { 0x0f00, 0x32b5, 0x0000 },
+  { 0x0f00, 0x32b7, 0x0000 },
+  { 0x8f00, 0x32bc, 0x3000 },
+  { 0x8f00, 0x32ba, 0x2000 },
+  { 0x0f00, 0x32b9, 0x0000 },
+  { 0x0f00, 0x32bb, 0x0000 },
+  { 0x8f00, 0x32be, 0x2000 },
+  { 0x0f00, 0x32bd, 0x0000 },
+  { 0x0f00, 0x32bf, 0x0000 },
+  { 0x9a00, 0x32c8, 0x4000 },
+  { 0x9a00, 0x32c4, 0x3000 },
+  { 0x9a00, 0x32c2, 0x2000 },
+  { 0x1a00, 0x32c1, 0x0000 },
+  { 0x1a00, 0x32c3, 0x0000 },
+  { 0x9a00, 0x32c6, 0x2000 },
+  { 0x1a00, 0x32c5, 0x0000 },
+  { 0x1a00, 0x32c7, 0x0000 },
+  { 0x9a00, 0x32cc, 0x3000 },
+  { 0x9a00, 0x32ca, 0x2000 },
+  { 0x1a00, 0x32c9, 0x0000 },
+  { 0x1a00, 0x32cb, 0x0000 },
+  { 0x9a00, 0x32ce, 0x2000 },
+  { 0x1a00, 0x32cd, 0x0000 },
+  { 0x1a00, 0x32cf, 0x0000 },
+  { 0x9a00, 0x32f0, 0x6000 },
+  { 0x9a00, 0x32e0, 0x5000 },
+  { 0x9a00, 0x32d8, 0x4000 },
+  { 0x9a00, 0x32d4, 0x3000 },
+  { 0x9a00, 0x32d2, 0x2000 },
+  { 0x1a00, 0x32d1, 0x0000 },
+  { 0x1a00, 0x32d3, 0x0000 },
+  { 0x9a00, 0x32d6, 0x2000 },
+  { 0x1a00, 0x32d5, 0x0000 },
+  { 0x1a00, 0x32d7, 0x0000 },
+  { 0x9a00, 0x32dc, 0x3000 },
+  { 0x9a00, 0x32da, 0x2000 },
+  { 0x1a00, 0x32d9, 0x0000 },
+  { 0x1a00, 0x32db, 0x0000 },
+  { 0x9a00, 0x32de, 0x2000 },
+  { 0x1a00, 0x32dd, 0x0000 },
+  { 0x1a00, 0x32df, 0x0000 },
+  { 0x9a00, 0x32e8, 0x4000 },
+  { 0x9a00, 0x32e4, 0x3000 },
+  { 0x9a00, 0x32e2, 0x2000 },
+  { 0x1a00, 0x32e1, 0x0000 },
+  { 0x1a00, 0x32e3, 0x0000 },
+  { 0x9a00, 0x32e6, 0x2000 },
+  { 0x1a00, 0x32e5, 0x0000 },
+  { 0x1a00, 0x32e7, 0x0000 },
+  { 0x9a00, 0x32ec, 0x3000 },
+  { 0x9a00, 0x32ea, 0x2000 },
+  { 0x1a00, 0x32e9, 0x0000 },
+  { 0x1a00, 0x32eb, 0x0000 },
+  { 0x9a00, 0x32ee, 0x2000 },
+  { 0x1a00, 0x32ed, 0x0000 },
+  { 0x1a00, 0x32ef, 0x0000 },
+  { 0x9a00, 0x3301, 0x5000 },
+  { 0x9a00, 0x32f8, 0x4000 },
+  { 0x9a00, 0x32f4, 0x3000 },
+  { 0x9a00, 0x32f2, 0x2000 },
+  { 0x1a00, 0x32f1, 0x0000 },
+  { 0x1a00, 0x32f3, 0x0000 },
+  { 0x9a00, 0x32f6, 0x2000 },
+  { 0x1a00, 0x32f5, 0x0000 },
+  { 0x1a00, 0x32f7, 0x0000 },
+  { 0x9a00, 0x32fc, 0x3000 },
+  { 0x9a00, 0x32fa, 0x2000 },
+  { 0x1a00, 0x32f9, 0x0000 },
+  { 0x1a00, 0x32fb, 0x0000 },
+  { 0x9a00, 0x32fe, 0x2000 },
+  { 0x1a00, 0x32fd, 0x0000 },
+  { 0x1a00, 0x3300, 0x0000 },
+  { 0x9a00, 0x3309, 0x4000 },
+  { 0x9a00, 0x3305, 0x3000 },
+  { 0x9a00, 0x3303, 0x2000 },
+  { 0x1a00, 0x3302, 0x0000 },
+  { 0x1a00, 0x3304, 0x0000 },
+  { 0x9a00, 0x3307, 0x2000 },
+  { 0x1a00, 0x3306, 0x0000 },
+  { 0x1a00, 0x3308, 0x0000 },
+  { 0x9a00, 0x330d, 0x3000 },
+  { 0x9a00, 0x330b, 0x2000 },
+  { 0x1a00, 0x330a, 0x0000 },
+  { 0x1a00, 0x330c, 0x0000 },
+  { 0x9a00, 0x330f, 0x2000 },
+  { 0x1a00, 0x330e, 0x0000 },
+  { 0x1a00, 0x3310, 0x0000 },
+  { 0x9a00, 0x3351, 0x7000 },
+  { 0x9a00, 0x3331, 0x6000 },
+  { 0x9a00, 0x3321, 0x5000 },
+  { 0x9a00, 0x3319, 0x4000 },
+  { 0x9a00, 0x3315, 0x3000 },
+  { 0x9a00, 0x3313, 0x2000 },
+  { 0x1a00, 0x3312, 0x0000 },
+  { 0x1a00, 0x3314, 0x0000 },
+  { 0x9a00, 0x3317, 0x2000 },
+  { 0x1a00, 0x3316, 0x0000 },
+  { 0x1a00, 0x3318, 0x0000 },
+  { 0x9a00, 0x331d, 0x3000 },
+  { 0x9a00, 0x331b, 0x2000 },
+  { 0x1a00, 0x331a, 0x0000 },
+  { 0x1a00, 0x331c, 0x0000 },
+  { 0x9a00, 0x331f, 0x2000 },
+  { 0x1a00, 0x331e, 0x0000 },
+  { 0x1a00, 0x3320, 0x0000 },
+  { 0x9a00, 0x3329, 0x4000 },
+  { 0x9a00, 0x3325, 0x3000 },
+  { 0x9a00, 0x3323, 0x2000 },
+  { 0x1a00, 0x3322, 0x0000 },
+  { 0x1a00, 0x3324, 0x0000 },
+  { 0x9a00, 0x3327, 0x2000 },
+  { 0x1a00, 0x3326, 0x0000 },
+  { 0x1a00, 0x3328, 0x0000 },
+  { 0x9a00, 0x332d, 0x3000 },
+  { 0x9a00, 0x332b, 0x2000 },
+  { 0x1a00, 0x332a, 0x0000 },
+  { 0x1a00, 0x332c, 0x0000 },
+  { 0x9a00, 0x332f, 0x2000 },
+  { 0x1a00, 0x332e, 0x0000 },
+  { 0x1a00, 0x3330, 0x0000 },
+  { 0x9a00, 0x3341, 0x5000 },
+  { 0x9a00, 0x3339, 0x4000 },
+  { 0x9a00, 0x3335, 0x3000 },
+  { 0x9a00, 0x3333, 0x2000 },
+  { 0x1a00, 0x3332, 0x0000 },
+  { 0x1a00, 0x3334, 0x0000 },
+  { 0x9a00, 0x3337, 0x2000 },
+  { 0x1a00, 0x3336, 0x0000 },
+  { 0x1a00, 0x3338, 0x0000 },
+  { 0x9a00, 0x333d, 0x3000 },
+  { 0x9a00, 0x333b, 0x2000 },
+  { 0x1a00, 0x333a, 0x0000 },
+  { 0x1a00, 0x333c, 0x0000 },
+  { 0x9a00, 0x333f, 0x2000 },
+  { 0x1a00, 0x333e, 0x0000 },
+  { 0x1a00, 0x3340, 0x0000 },
+  { 0x9a00, 0x3349, 0x4000 },
+  { 0x9a00, 0x3345, 0x3000 },
+  { 0x9a00, 0x3343, 0x2000 },
+  { 0x1a00, 0x3342, 0x0000 },
+  { 0x1a00, 0x3344, 0x0000 },
+  { 0x9a00, 0x3347, 0x2000 },
+  { 0x1a00, 0x3346, 0x0000 },
+  { 0x1a00, 0x3348, 0x0000 },
+  { 0x9a00, 0x334d, 0x3000 },
+  { 0x9a00, 0x334b, 0x2000 },
+  { 0x1a00, 0x334a, 0x0000 },
+  { 0x1a00, 0x334c, 0x0000 },
+  { 0x9a00, 0x334f, 0x2000 },
+  { 0x1a00, 0x334e, 0x0000 },
+  { 0x1a00, 0x3350, 0x0000 },
+  { 0x9a00, 0x3371, 0x6000 },
+  { 0x9a00, 0x3361, 0x5000 },
+  { 0x9a00, 0x3359, 0x4000 },
+  { 0x9a00, 0x3355, 0x3000 },
+  { 0x9a00, 0x3353, 0x2000 },
+  { 0x1a00, 0x3352, 0x0000 },
+  { 0x1a00, 0x3354, 0x0000 },
+  { 0x9a00, 0x3357, 0x2000 },
+  { 0x1a00, 0x3356, 0x0000 },
+  { 0x1a00, 0x3358, 0x0000 },
+  { 0x9a00, 0x335d, 0x3000 },
+  { 0x9a00, 0x335b, 0x2000 },
+  { 0x1a00, 0x335a, 0x0000 },
+  { 0x1a00, 0x335c, 0x0000 },
+  { 0x9a00, 0x335f, 0x2000 },
+  { 0x1a00, 0x335e, 0x0000 },
+  { 0x1a00, 0x3360, 0x0000 },
+  { 0x9a00, 0x3369, 0x4000 },
+  { 0x9a00, 0x3365, 0x3000 },
+  { 0x9a00, 0x3363, 0x2000 },
+  { 0x1a00, 0x3362, 0x0000 },
+  { 0x1a00, 0x3364, 0x0000 },
+  { 0x9a00, 0x3367, 0x2000 },
+  { 0x1a00, 0x3366, 0x0000 },
+  { 0x1a00, 0x3368, 0x0000 },
+  { 0x9a00, 0x336d, 0x3000 },
+  { 0x9a00, 0x336b, 0x2000 },
+  { 0x1a00, 0x336a, 0x0000 },
+  { 0x1a00, 0x336c, 0x0000 },
+  { 0x9a00, 0x336f, 0x2000 },
+  { 0x1a00, 0x336e, 0x0000 },
+  { 0x1a00, 0x3370, 0x0000 },
+  { 0x9a00, 0x3381, 0x5000 },
+  { 0x9a00, 0x3379, 0x4000 },
+  { 0x9a00, 0x3375, 0x3000 },
+  { 0x9a00, 0x3373, 0x2000 },
+  { 0x1a00, 0x3372, 0x0000 },
+  { 0x1a00, 0x3374, 0x0000 },
+  { 0x9a00, 0x3377, 0x2000 },
+  { 0x1a00, 0x3376, 0x0000 },
+  { 0x1a00, 0x3378, 0x0000 },
+  { 0x9a00, 0x337d, 0x3000 },
+  { 0x9a00, 0x337b, 0x2000 },
+  { 0x1a00, 0x337a, 0x0000 },
+  { 0x1a00, 0x337c, 0x0000 },
+  { 0x9a00, 0x337f, 0x2000 },
+  { 0x1a00, 0x337e, 0x0000 },
+  { 0x1a00, 0x3380, 0x0000 },
+  { 0x9a00, 0x3389, 0x4000 },
+  { 0x9a00, 0x3385, 0x3000 },
+  { 0x9a00, 0x3383, 0x2000 },
+  { 0x1a00, 0x3382, 0x0000 },
+  { 0x1a00, 0x3384, 0x0000 },
+  { 0x9a00, 0x3387, 0x2000 },
+  { 0x1a00, 0x3386, 0x0000 },
+  { 0x1a00, 0x3388, 0x0000 },
+  { 0x9a00, 0x338d, 0x3000 },
+  { 0x9a00, 0x338b, 0x2000 },
+  { 0x1a00, 0x338a, 0x0000 },
+  { 0x1a00, 0x338c, 0x0000 },
+  { 0x9a00, 0x338f, 0x2000 },
+  { 0x1a00, 0x338e, 0x0000 },
+  { 0x1a00, 0x3390, 0x0000 },
+  { 0x8700, 0xa14d, 0xa000 },
+  { 0x8700, 0xa04d, 0x9000 },
+  { 0x9a00, 0x4dcf, 0x8000 },
+  { 0x9a00, 0x33d1, 0x7000 },
+  { 0x9a00, 0x33b1, 0x6000 },
+  { 0x9a00, 0x33a1, 0x5000 },
+  { 0x9a00, 0x3399, 0x4000 },
+  { 0x9a00, 0x3395, 0x3000 },
+  { 0x9a00, 0x3393, 0x2000 },
+  { 0x1a00, 0x3392, 0x0000 },
+  { 0x1a00, 0x3394, 0x0000 },
+  { 0x9a00, 0x3397, 0x2000 },
+  { 0x1a00, 0x3396, 0x0000 },
+  { 0x1a00, 0x3398, 0x0000 },
+  { 0x9a00, 0x339d, 0x3000 },
+  { 0x9a00, 0x339b, 0x2000 },
+  { 0x1a00, 0x339a, 0x0000 },
+  { 0x1a00, 0x339c, 0x0000 },
+  { 0x9a00, 0x339f, 0x2000 },
+  { 0x1a00, 0x339e, 0x0000 },
+  { 0x1a00, 0x33a0, 0x0000 },
+  { 0x9a00, 0x33a9, 0x4000 },
+  { 0x9a00, 0x33a5, 0x3000 },
+  { 0x9a00, 0x33a3, 0x2000 },
+  { 0x1a00, 0x33a2, 0x0000 },
+  { 0x1a00, 0x33a4, 0x0000 },
+  { 0x9a00, 0x33a7, 0x2000 },
+  { 0x1a00, 0x33a6, 0x0000 },
+  { 0x1a00, 0x33a8, 0x0000 },
+  { 0x9a00, 0x33ad, 0x3000 },
+  { 0x9a00, 0x33ab, 0x2000 },
+  { 0x1a00, 0x33aa, 0x0000 },
+  { 0x1a00, 0x33ac, 0x0000 },
+  { 0x9a00, 0x33af, 0x2000 },
+  { 0x1a00, 0x33ae, 0x0000 },
+  { 0x1a00, 0x33b0, 0x0000 },
+  { 0x9a00, 0x33c1, 0x5000 },
+  { 0x9a00, 0x33b9, 0x4000 },
+  { 0x9a00, 0x33b5, 0x3000 },
+  { 0x9a00, 0x33b3, 0x2000 },
+  { 0x1a00, 0x33b2, 0x0000 },
+  { 0x1a00, 0x33b4, 0x0000 },
+  { 0x9a00, 0x33b7, 0x2000 },
+  { 0x1a00, 0x33b6, 0x0000 },
+  { 0x1a00, 0x33b8, 0x0000 },
+  { 0x9a00, 0x33bd, 0x3000 },
+  { 0x9a00, 0x33bb, 0x2000 },
+  { 0x1a00, 0x33ba, 0x0000 },
+  { 0x1a00, 0x33bc, 0x0000 },
+  { 0x9a00, 0x33bf, 0x2000 },
+  { 0x1a00, 0x33be, 0x0000 },
+  { 0x1a00, 0x33c0, 0x0000 },
+  { 0x9a00, 0x33c9, 0x4000 },
+  { 0x9a00, 0x33c5, 0x3000 },
+  { 0x9a00, 0x33c3, 0x2000 },
+  { 0x1a00, 0x33c2, 0x0000 },
+  { 0x1a00, 0x33c4, 0x0000 },
+  { 0x9a00, 0x33c7, 0x2000 },
+  { 0x1a00, 0x33c6, 0x0000 },
+  { 0x1a00, 0x33c8, 0x0000 },
+  { 0x9a00, 0x33cd, 0x3000 },
+  { 0x9a00, 0x33cb, 0x2000 },
+  { 0x1a00, 0x33ca, 0x0000 },
+  { 0x1a00, 0x33cc, 0x0000 },
+  { 0x9a00, 0x33cf, 0x2000 },
+  { 0x1a00, 0x33ce, 0x0000 },
+  { 0x1a00, 0x33d0, 0x0000 },
+  { 0x9a00, 0x33f1, 0x6000 },
+  { 0x9a00, 0x33e1, 0x5000 },
+  { 0x9a00, 0x33d9, 0x4000 },
+  { 0x9a00, 0x33d5, 0x3000 },
+  { 0x9a00, 0x33d3, 0x2000 },
+  { 0x1a00, 0x33d2, 0x0000 },
+  { 0x1a00, 0x33d4, 0x0000 },
+  { 0x9a00, 0x33d7, 0x2000 },
+  { 0x1a00, 0x33d6, 0x0000 },
+  { 0x1a00, 0x33d8, 0x0000 },
+  { 0x9a00, 0x33dd, 0x3000 },
+  { 0x9a00, 0x33db, 0x2000 },
+  { 0x1a00, 0x33da, 0x0000 },
+  { 0x1a00, 0x33dc, 0x0000 },
+  { 0x9a00, 0x33df, 0x2000 },
+  { 0x1a00, 0x33de, 0x0000 },
+  { 0x1a00, 0x33e0, 0x0000 },
+  { 0x9a00, 0x33e9, 0x4000 },
+  { 0x9a00, 0x33e5, 0x3000 },
+  { 0x9a00, 0x33e3, 0x2000 },
+  { 0x1a00, 0x33e2, 0x0000 },
+  { 0x1a00, 0x33e4, 0x0000 },
+  { 0x9a00, 0x33e7, 0x2000 },
+  { 0x1a00, 0x33e6, 0x0000 },
+  { 0x1a00, 0x33e8, 0x0000 },
+  { 0x9a00, 0x33ed, 0x3000 },
+  { 0x9a00, 0x33eb, 0x2000 },
+  { 0x1a00, 0x33ea, 0x0000 },
+  { 0x1a00, 0x33ec, 0x0000 },
+  { 0x9a00, 0x33ef, 0x2000 },
+  { 0x1a00, 0x33ee, 0x0000 },
+  { 0x1a00, 0x33f0, 0x0000 },
+  { 0x8700, 0x4db5, 0x5000 },
+  { 0x9a00, 0x33f9, 0x4000 },
+  { 0x9a00, 0x33f5, 0x3000 },
+  { 0x9a00, 0x33f3, 0x2000 },
+  { 0x1a00, 0x33f2, 0x0000 },
+  { 0x1a00, 0x33f4, 0x0000 },
+  { 0x9a00, 0x33f7, 0x2000 },
+  { 0x1a00, 0x33f6, 0x0000 },
+  { 0x1a00, 0x33f8, 0x0000 },
+  { 0x9a00, 0x33fd, 0x3000 },
+  { 0x9a00, 0x33fb, 0x2000 },
+  { 0x1a00, 0x33fa, 0x0000 },
+  { 0x1a00, 0x33fc, 0x0000 },
+  { 0x9a00, 0x33ff, 0x2000 },
+  { 0x1a00, 0x33fe, 0x0000 },
+  { 0x0700, 0x3400, 0x0000 },
+  { 0x9a00, 0x4dc7, 0x4000 },
+  { 0x9a00, 0x4dc3, 0x3000 },
+  { 0x9a00, 0x4dc1, 0x2000 },
+  { 0x1a00, 0x4dc0, 0x0000 },
+  { 0x1a00, 0x4dc2, 0x0000 },
+  { 0x9a00, 0x4dc5, 0x2000 },
+  { 0x1a00, 0x4dc4, 0x0000 },
+  { 0x1a00, 0x4dc6, 0x0000 },
+  { 0x9a00, 0x4dcb, 0x3000 },
+  { 0x9a00, 0x4dc9, 0x2000 },
+  { 0x1a00, 0x4dc8, 0x0000 },
+  { 0x1a00, 0x4dca, 0x0000 },
+  { 0x9a00, 0x4dcd, 0x2000 },
+  { 0x1a00, 0x4dcc, 0x0000 },
+  { 0x1a00, 0x4dce, 0x0000 },
+  { 0x8700, 0xa00d, 0x7000 },
+  { 0x9a00, 0x4def, 0x6000 },
+  { 0x9a00, 0x4ddf, 0x5000 },
+  { 0x9a00, 0x4dd7, 0x4000 },
+  { 0x9a00, 0x4dd3, 0x3000 },
+  { 0x9a00, 0x4dd1, 0x2000 },
+  { 0x1a00, 0x4dd0, 0x0000 },
+  { 0x1a00, 0x4dd2, 0x0000 },
+  { 0x9a00, 0x4dd5, 0x2000 },
+  { 0x1a00, 0x4dd4, 0x0000 },
+  { 0x1a00, 0x4dd6, 0x0000 },
+  { 0x9a00, 0x4ddb, 0x3000 },
+  { 0x9a00, 0x4dd9, 0x2000 },
+  { 0x1a00, 0x4dd8, 0x0000 },
+  { 0x1a00, 0x4dda, 0x0000 },
+  { 0x9a00, 0x4ddd, 0x2000 },
+  { 0x1a00, 0x4ddc, 0x0000 },
+  { 0x1a00, 0x4dde, 0x0000 },
+  { 0x9a00, 0x4de7, 0x4000 },
+  { 0x9a00, 0x4de3, 0x3000 },
+  { 0x9a00, 0x4de1, 0x2000 },
+  { 0x1a00, 0x4de0, 0x0000 },
+  { 0x1a00, 0x4de2, 0x0000 },
+  { 0x9a00, 0x4de5, 0x2000 },
+  { 0x1a00, 0x4de4, 0x0000 },
+  { 0x1a00, 0x4de6, 0x0000 },
+  { 0x9a00, 0x4deb, 0x3000 },
+  { 0x9a00, 0x4de9, 0x2000 },
+  { 0x1a00, 0x4de8, 0x0000 },
+  { 0x1a00, 0x4dea, 0x0000 },
+  { 0x9a00, 0x4ded, 0x2000 },
+  { 0x1a00, 0x4dec, 0x0000 },
+  { 0x1a00, 0x4dee, 0x0000 },
+  { 0x9a00, 0x4dff, 0x5000 },
+  { 0x9a00, 0x4df7, 0x4000 },
+  { 0x9a00, 0x4df3, 0x3000 },
+  { 0x9a00, 0x4df1, 0x2000 },
+  { 0x1a00, 0x4df0, 0x0000 },
+  { 0x1a00, 0x4df2, 0x0000 },
+  { 0x9a00, 0x4df5, 0x2000 },
+  { 0x1a00, 0x4df4, 0x0000 },
+  { 0x1a00, 0x4df6, 0x0000 },
+  { 0x9a00, 0x4dfb, 0x3000 },
+  { 0x9a00, 0x4df9, 0x2000 },
+  { 0x1a00, 0x4df8, 0x0000 },
+  { 0x1a00, 0x4dfa, 0x0000 },
+  { 0x9a00, 0x4dfd, 0x2000 },
+  { 0x1a00, 0x4dfc, 0x0000 },
+  { 0x1a00, 0x4dfe, 0x0000 },
+  { 0x8700, 0xa005, 0x4000 },
+  { 0x8700, 0xa001, 0x3000 },
+  { 0x8700, 0x9fa5, 0x2000 },
+  { 0x0700, 0x4e00, 0x0000 },
+  { 0x0700, 0xa000, 0x0000 },
+  { 0x8700, 0xa003, 0x2000 },
+  { 0x0700, 0xa002, 0x0000 },
+  { 0x0700, 0xa004, 0x0000 },
+  { 0x8700, 0xa009, 0x3000 },
+  { 0x8700, 0xa007, 0x2000 },
+  { 0x0700, 0xa006, 0x0000 },
+  { 0x0700, 0xa008, 0x0000 },
+  { 0x8700, 0xa00b, 0x2000 },
+  { 0x0700, 0xa00a, 0x0000 },
+  { 0x0700, 0xa00c, 0x0000 },
+  { 0x8700, 0xa02d, 0x6000 },
+  { 0x8700, 0xa01d, 0x5000 },
+  { 0x8700, 0xa015, 0x4000 },
+  { 0x8700, 0xa011, 0x3000 },
+  { 0x8700, 0xa00f, 0x2000 },
+  { 0x0700, 0xa00e, 0x0000 },
+  { 0x0700, 0xa010, 0x0000 },
+  { 0x8700, 0xa013, 0x2000 },
+  { 0x0700, 0xa012, 0x0000 },
+  { 0x0700, 0xa014, 0x0000 },
+  { 0x8700, 0xa019, 0x3000 },
+  { 0x8700, 0xa017, 0x2000 },
+  { 0x0700, 0xa016, 0x0000 },
+  { 0x0700, 0xa018, 0x0000 },
+  { 0x8700, 0xa01b, 0x2000 },
+  { 0x0700, 0xa01a, 0x0000 },
+  { 0x0700, 0xa01c, 0x0000 },
+  { 0x8700, 0xa025, 0x4000 },
+  { 0x8700, 0xa021, 0x3000 },
+  { 0x8700, 0xa01f, 0x2000 },
+  { 0x0700, 0xa01e, 0x0000 },
+  { 0x0700, 0xa020, 0x0000 },
+  { 0x8700, 0xa023, 0x2000 },
+  { 0x0700, 0xa022, 0x0000 },
+  { 0x0700, 0xa024, 0x0000 },
+  { 0x8700, 0xa029, 0x3000 },
+  { 0x8700, 0xa027, 0x2000 },
+  { 0x0700, 0xa026, 0x0000 },
+  { 0x0700, 0xa028, 0x0000 },
+  { 0x8700, 0xa02b, 0x2000 },
+  { 0x0700, 0xa02a, 0x0000 },
+  { 0x0700, 0xa02c, 0x0000 },
+  { 0x8700, 0xa03d, 0x5000 },
+  { 0x8700, 0xa035, 0x4000 },
+  { 0x8700, 0xa031, 0x3000 },
+  { 0x8700, 0xa02f, 0x2000 },
+  { 0x0700, 0xa02e, 0x0000 },
+  { 0x0700, 0xa030, 0x0000 },
+  { 0x8700, 0xa033, 0x2000 },
+  { 0x0700, 0xa032, 0x0000 },
+  { 0x0700, 0xa034, 0x0000 },
+  { 0x8700, 0xa039, 0x3000 },
+  { 0x8700, 0xa037, 0x2000 },
+  { 0x0700, 0xa036, 0x0000 },
+  { 0x0700, 0xa038, 0x0000 },
+  { 0x8700, 0xa03b, 0x2000 },
+  { 0x0700, 0xa03a, 0x0000 },
+  { 0x0700, 0xa03c, 0x0000 },
+  { 0x8700, 0xa045, 0x4000 },
+  { 0x8700, 0xa041, 0x3000 },
+  { 0x8700, 0xa03f, 0x2000 },
+  { 0x0700, 0xa03e, 0x0000 },
+  { 0x0700, 0xa040, 0x0000 },
+  { 0x8700, 0xa043, 0x2000 },
+  { 0x0700, 0xa042, 0x0000 },
+  { 0x0700, 0xa044, 0x0000 },
+  { 0x8700, 0xa049, 0x3000 },
+  { 0x8700, 0xa047, 0x2000 },
+  { 0x0700, 0xa046, 0x0000 },
+  { 0x0700, 0xa048, 0x0000 },
+  { 0x8700, 0xa04b, 0x2000 },
+  { 0x0700, 0xa04a, 0x0000 },
+  { 0x0700, 0xa04c, 0x0000 },
+  { 0x8700, 0xa0cd, 0x8000 },
+  { 0x8700, 0xa08d, 0x7000 },
+  { 0x8700, 0xa06d, 0x6000 },
+  { 0x8700, 0xa05d, 0x5000 },
+  { 0x8700, 0xa055, 0x4000 },
+  { 0x8700, 0xa051, 0x3000 },
+  { 0x8700, 0xa04f, 0x2000 },
+  { 0x0700, 0xa04e, 0x0000 },
+  { 0x0700, 0xa050, 0x0000 },
+  { 0x8700, 0xa053, 0x2000 },
+  { 0x0700, 0xa052, 0x0000 },
+  { 0x0700, 0xa054, 0x0000 },
+  { 0x8700, 0xa059, 0x3000 },
+  { 0x8700, 0xa057, 0x2000 },
+  { 0x0700, 0xa056, 0x0000 },
+  { 0x0700, 0xa058, 0x0000 },
+  { 0x8700, 0xa05b, 0x2000 },
+  { 0x0700, 0xa05a, 0x0000 },
+  { 0x0700, 0xa05c, 0x0000 },
+  { 0x8700, 0xa065, 0x4000 },
+  { 0x8700, 0xa061, 0x3000 },
+  { 0x8700, 0xa05f, 0x2000 },
+  { 0x0700, 0xa05e, 0x0000 },
+  { 0x0700, 0xa060, 0x0000 },
+  { 0x8700, 0xa063, 0x2000 },
+  { 0x0700, 0xa062, 0x0000 },
+  { 0x0700, 0xa064, 0x0000 },
+  { 0x8700, 0xa069, 0x3000 },
+  { 0x8700, 0xa067, 0x2000 },
+  { 0x0700, 0xa066, 0x0000 },
+  { 0x0700, 0xa068, 0x0000 },
+  { 0x8700, 0xa06b, 0x2000 },
+  { 0x0700, 0xa06a, 0x0000 },
+  { 0x0700, 0xa06c, 0x0000 },
+  { 0x8700, 0xa07d, 0x5000 },
+  { 0x8700, 0xa075, 0x4000 },
+  { 0x8700, 0xa071, 0x3000 },
+  { 0x8700, 0xa06f, 0x2000 },
+  { 0x0700, 0xa06e, 0x0000 },
+  { 0x0700, 0xa070, 0x0000 },
+  { 0x8700, 0xa073, 0x2000 },
+  { 0x0700, 0xa072, 0x0000 },
+  { 0x0700, 0xa074, 0x0000 },
+  { 0x8700, 0xa079, 0x3000 },
+  { 0x8700, 0xa077, 0x2000 },
+  { 0x0700, 0xa076, 0x0000 },
+  { 0x0700, 0xa078, 0x0000 },
+  { 0x8700, 0xa07b, 0x2000 },
+  { 0x0700, 0xa07a, 0x0000 },
+  { 0x0700, 0xa07c, 0x0000 },
+  { 0x8700, 0xa085, 0x4000 },
+  { 0x8700, 0xa081, 0x3000 },
+  { 0x8700, 0xa07f, 0x2000 },
+  { 0x0700, 0xa07e, 0x0000 },
+  { 0x0700, 0xa080, 0x0000 },
+  { 0x8700, 0xa083, 0x2000 },
+  { 0x0700, 0xa082, 0x0000 },
+  { 0x0700, 0xa084, 0x0000 },
+  { 0x8700, 0xa089, 0x3000 },
+  { 0x8700, 0xa087, 0x2000 },
+  { 0x0700, 0xa086, 0x0000 },
+  { 0x0700, 0xa088, 0x0000 },
+  { 0x8700, 0xa08b, 0x2000 },
+  { 0x0700, 0xa08a, 0x0000 },
+  { 0x0700, 0xa08c, 0x0000 },
+  { 0x8700, 0xa0ad, 0x6000 },
+  { 0x8700, 0xa09d, 0x5000 },
+  { 0x8700, 0xa095, 0x4000 },
+  { 0x8700, 0xa091, 0x3000 },
+  { 0x8700, 0xa08f, 0x2000 },
+  { 0x0700, 0xa08e, 0x0000 },
+  { 0x0700, 0xa090, 0x0000 },
+  { 0x8700, 0xa093, 0x2000 },
+  { 0x0700, 0xa092, 0x0000 },
+  { 0x0700, 0xa094, 0x0000 },
+  { 0x8700, 0xa099, 0x3000 },
+  { 0x8700, 0xa097, 0x2000 },
+  { 0x0700, 0xa096, 0x0000 },
+  { 0x0700, 0xa098, 0x0000 },
+  { 0x8700, 0xa09b, 0x2000 },
+  { 0x0700, 0xa09a, 0x0000 },
+  { 0x0700, 0xa09c, 0x0000 },
+  { 0x8700, 0xa0a5, 0x4000 },
+  { 0x8700, 0xa0a1, 0x3000 },
+  { 0x8700, 0xa09f, 0x2000 },
+  { 0x0700, 0xa09e, 0x0000 },
+  { 0x0700, 0xa0a0, 0x0000 },
+  { 0x8700, 0xa0a3, 0x2000 },
+  { 0x0700, 0xa0a2, 0x0000 },
+  { 0x0700, 0xa0a4, 0x0000 },
+  { 0x8700, 0xa0a9, 0x3000 },
+  { 0x8700, 0xa0a7, 0x2000 },
+  { 0x0700, 0xa0a6, 0x0000 },
+  { 0x0700, 0xa0a8, 0x0000 },
+  { 0x8700, 0xa0ab, 0x2000 },
+  { 0x0700, 0xa0aa, 0x0000 },
+  { 0x0700, 0xa0ac, 0x0000 },
+  { 0x8700, 0xa0bd, 0x5000 },
+  { 0x8700, 0xa0b5, 0x4000 },
+  { 0x8700, 0xa0b1, 0x3000 },
+  { 0x8700, 0xa0af, 0x2000 },
+  { 0x0700, 0xa0ae, 0x0000 },
+  { 0x0700, 0xa0b0, 0x0000 },
+  { 0x8700, 0xa0b3, 0x2000 },
+  { 0x0700, 0xa0b2, 0x0000 },
+  { 0x0700, 0xa0b4, 0x0000 },
+  { 0x8700, 0xa0b9, 0x3000 },
+  { 0x8700, 0xa0b7, 0x2000 },
+  { 0x0700, 0xa0b6, 0x0000 },
+  { 0x0700, 0xa0b8, 0x0000 },
+  { 0x8700, 0xa0bb, 0x2000 },
+  { 0x0700, 0xa0ba, 0x0000 },
+  { 0x0700, 0xa0bc, 0x0000 },
+  { 0x8700, 0xa0c5, 0x4000 },
+  { 0x8700, 0xa0c1, 0x3000 },
+  { 0x8700, 0xa0bf, 0x2000 },
+  { 0x0700, 0xa0be, 0x0000 },
+  { 0x0700, 0xa0c0, 0x0000 },
+  { 0x8700, 0xa0c3, 0x2000 },
+  { 0x0700, 0xa0c2, 0x0000 },
+  { 0x0700, 0xa0c4, 0x0000 },
+  { 0x8700, 0xa0c9, 0x3000 },
+  { 0x8700, 0xa0c7, 0x2000 },
+  { 0x0700, 0xa0c6, 0x0000 },
+  { 0x0700, 0xa0c8, 0x0000 },
+  { 0x8700, 0xa0cb, 0x2000 },
+  { 0x0700, 0xa0ca, 0x0000 },
+  { 0x0700, 0xa0cc, 0x0000 },
+  { 0x8700, 0xa10d, 0x7000 },
+  { 0x8700, 0xa0ed, 0x6000 },
+  { 0x8700, 0xa0dd, 0x5000 },
+  { 0x8700, 0xa0d5, 0x4000 },
+  { 0x8700, 0xa0d1, 0x3000 },
+  { 0x8700, 0xa0cf, 0x2000 },
+  { 0x0700, 0xa0ce, 0x0000 },
+  { 0x0700, 0xa0d0, 0x0000 },
+  { 0x8700, 0xa0d3, 0x2000 },
+  { 0x0700, 0xa0d2, 0x0000 },
+  { 0x0700, 0xa0d4, 0x0000 },
+  { 0x8700, 0xa0d9, 0x3000 },
+  { 0x8700, 0xa0d7, 0x2000 },
+  { 0x0700, 0xa0d6, 0x0000 },
+  { 0x0700, 0xa0d8, 0x0000 },
+  { 0x8700, 0xa0db, 0x2000 },
+  { 0x0700, 0xa0da, 0x0000 },
+  { 0x0700, 0xa0dc, 0x0000 },
+  { 0x8700, 0xa0e5, 0x4000 },
+  { 0x8700, 0xa0e1, 0x3000 },
+  { 0x8700, 0xa0df, 0x2000 },
+  { 0x0700, 0xa0de, 0x0000 },
+  { 0x0700, 0xa0e0, 0x0000 },
+  { 0x8700, 0xa0e3, 0x2000 },
+  { 0x0700, 0xa0e2, 0x0000 },
+  { 0x0700, 0xa0e4, 0x0000 },
+  { 0x8700, 0xa0e9, 0x3000 },
+  { 0x8700, 0xa0e7, 0x2000 },
+  { 0x0700, 0xa0e6, 0x0000 },
+  { 0x0700, 0xa0e8, 0x0000 },
+  { 0x8700, 0xa0eb, 0x2000 },
+  { 0x0700, 0xa0ea, 0x0000 },
+  { 0x0700, 0xa0ec, 0x0000 },
+  { 0x8700, 0xa0fd, 0x5000 },
+  { 0x8700, 0xa0f5, 0x4000 },
+  { 0x8700, 0xa0f1, 0x3000 },
+  { 0x8700, 0xa0ef, 0x2000 },
+  { 0x0700, 0xa0ee, 0x0000 },
+  { 0x0700, 0xa0f0, 0x0000 },
+  { 0x8700, 0xa0f3, 0x2000 },
+  { 0x0700, 0xa0f2, 0x0000 },
+  { 0x0700, 0xa0f4, 0x0000 },
+  { 0x8700, 0xa0f9, 0x3000 },
+  { 0x8700, 0xa0f7, 0x2000 },
+  { 0x0700, 0xa0f6, 0x0000 },
+  { 0x0700, 0xa0f8, 0x0000 },
+  { 0x8700, 0xa0fb, 0x2000 },
+  { 0x0700, 0xa0fa, 0x0000 },
+  { 0x0700, 0xa0fc, 0x0000 },
+  { 0x8700, 0xa105, 0x4000 },
+  { 0x8700, 0xa101, 0x3000 },
+  { 0x8700, 0xa0ff, 0x2000 },
+  { 0x0700, 0xa0fe, 0x0000 },
+  { 0x0700, 0xa100, 0x0000 },
+  { 0x8700, 0xa103, 0x2000 },
+  { 0x0700, 0xa102, 0x0000 },
+  { 0x0700, 0xa104, 0x0000 },
+  { 0x8700, 0xa109, 0x3000 },
+  { 0x8700, 0xa107, 0x2000 },
+  { 0x0700, 0xa106, 0x0000 },
+  { 0x0700, 0xa108, 0x0000 },
+  { 0x8700, 0xa10b, 0x2000 },
+  { 0x0700, 0xa10a, 0x0000 },
+  { 0x0700, 0xa10c, 0x0000 },
+  { 0x8700, 0xa12d, 0x6000 },
+  { 0x8700, 0xa11d, 0x5000 },
+  { 0x8700, 0xa115, 0x4000 },
+  { 0x8700, 0xa111, 0x3000 },
+  { 0x8700, 0xa10f, 0x2000 },
+  { 0x0700, 0xa10e, 0x0000 },
+  { 0x0700, 0xa110, 0x0000 },
+  { 0x8700, 0xa113, 0x2000 },
+  { 0x0700, 0xa112, 0x0000 },
+  { 0x0700, 0xa114, 0x0000 },
+  { 0x8700, 0xa119, 0x3000 },
+  { 0x8700, 0xa117, 0x2000 },
+  { 0x0700, 0xa116, 0x0000 },
+  { 0x0700, 0xa118, 0x0000 },
+  { 0x8700, 0xa11b, 0x2000 },
+  { 0x0700, 0xa11a, 0x0000 },
+  { 0x0700, 0xa11c, 0x0000 },
+  { 0x8700, 0xa125, 0x4000 },
+  { 0x8700, 0xa121, 0x3000 },
+  { 0x8700, 0xa11f, 0x2000 },
+  { 0x0700, 0xa11e, 0x0000 },
+  { 0x0700, 0xa120, 0x0000 },
+  { 0x8700, 0xa123, 0x2000 },
+  { 0x0700, 0xa122, 0x0000 },
+  { 0x0700, 0xa124, 0x0000 },
+  { 0x8700, 0xa129, 0x3000 },
+  { 0x8700, 0xa127, 0x2000 },
+  { 0x0700, 0xa126, 0x0000 },
+  { 0x0700, 0xa128, 0x0000 },
+  { 0x8700, 0xa12b, 0x2000 },
+  { 0x0700, 0xa12a, 0x0000 },
+  { 0x0700, 0xa12c, 0x0000 },
+  { 0x8700, 0xa13d, 0x5000 },
+  { 0x8700, 0xa135, 0x4000 },
+  { 0x8700, 0xa131, 0x3000 },
+  { 0x8700, 0xa12f, 0x2000 },
+  { 0x0700, 0xa12e, 0x0000 },
+  { 0x0700, 0xa130, 0x0000 },
+  { 0x8700, 0xa133, 0x2000 },
+  { 0x0700, 0xa132, 0x0000 },
+  { 0x0700, 0xa134, 0x0000 },
+  { 0x8700, 0xa139, 0x3000 },
+  { 0x8700, 0xa137, 0x2000 },
+  { 0x0700, 0xa136, 0x0000 },
+  { 0x0700, 0xa138, 0x0000 },
+  { 0x8700, 0xa13b, 0x2000 },
+  { 0x0700, 0xa13a, 0x0000 },
+  { 0x0700, 0xa13c, 0x0000 },
+  { 0x8700, 0xa145, 0x4000 },
+  { 0x8700, 0xa141, 0x3000 },
+  { 0x8700, 0xa13f, 0x2000 },
+  { 0x0700, 0xa13e, 0x0000 },
+  { 0x0700, 0xa140, 0x0000 },
+  { 0x8700, 0xa143, 0x2000 },
+  { 0x0700, 0xa142, 0x0000 },
+  { 0x0700, 0xa144, 0x0000 },
+  { 0x8700, 0xa149, 0x3000 },
+  { 0x8700, 0xa147, 0x2000 },
+  { 0x0700, 0xa146, 0x0000 },
+  { 0x0700, 0xa148, 0x0000 },
+  { 0x8700, 0xa14b, 0x2000 },
+  { 0x0700, 0xa14a, 0x0000 },
+  { 0x0700, 0xa14c, 0x0000 },
+  { 0x8700, 0xa24d, 0x9000 },
+  { 0x8700, 0xa1cd, 0x8000 },
+  { 0x8700, 0xa18d, 0x7000 },
+  { 0x8700, 0xa16d, 0x6000 },
+  { 0x8700, 0xa15d, 0x5000 },
+  { 0x8700, 0xa155, 0x4000 },
+  { 0x8700, 0xa151, 0x3000 },
+  { 0x8700, 0xa14f, 0x2000 },
+  { 0x0700, 0xa14e, 0x0000 },
+  { 0x0700, 0xa150, 0x0000 },
+  { 0x8700, 0xa153, 0x2000 },
+  { 0x0700, 0xa152, 0x0000 },
+  { 0x0700, 0xa154, 0x0000 },
+  { 0x8700, 0xa159, 0x3000 },
+  { 0x8700, 0xa157, 0x2000 },
+  { 0x0700, 0xa156, 0x0000 },
+  { 0x0700, 0xa158, 0x0000 },
+  { 0x8700, 0xa15b, 0x2000 },
+  { 0x0700, 0xa15a, 0x0000 },
+  { 0x0700, 0xa15c, 0x0000 },
+  { 0x8700, 0xa165, 0x4000 },
+  { 0x8700, 0xa161, 0x3000 },
+  { 0x8700, 0xa15f, 0x2000 },
+  { 0x0700, 0xa15e, 0x0000 },
+  { 0x0700, 0xa160, 0x0000 },
+  { 0x8700, 0xa163, 0x2000 },
+  { 0x0700, 0xa162, 0x0000 },
+  { 0x0700, 0xa164, 0x0000 },
+  { 0x8700, 0xa169, 0x3000 },
+  { 0x8700, 0xa167, 0x2000 },
+  { 0x0700, 0xa166, 0x0000 },
+  { 0x0700, 0xa168, 0x0000 },
+  { 0x8700, 0xa16b, 0x2000 },
+  { 0x0700, 0xa16a, 0x0000 },
+  { 0x0700, 0xa16c, 0x0000 },
+  { 0x8700, 0xa17d, 0x5000 },
+  { 0x8700, 0xa175, 0x4000 },
+  { 0x8700, 0xa171, 0x3000 },
+  { 0x8700, 0xa16f, 0x2000 },
+  { 0x0700, 0xa16e, 0x0000 },
+  { 0x0700, 0xa170, 0x0000 },
+  { 0x8700, 0xa173, 0x2000 },
+  { 0x0700, 0xa172, 0x0000 },
+  { 0x0700, 0xa174, 0x0000 },
+  { 0x8700, 0xa179, 0x3000 },
+  { 0x8700, 0xa177, 0x2000 },
+  { 0x0700, 0xa176, 0x0000 },
+  { 0x0700, 0xa178, 0x0000 },
+  { 0x8700, 0xa17b, 0x2000 },
+  { 0x0700, 0xa17a, 0x0000 },
+  { 0x0700, 0xa17c, 0x0000 },
+  { 0x8700, 0xa185, 0x4000 },
+  { 0x8700, 0xa181, 0x3000 },
+  { 0x8700, 0xa17f, 0x2000 },
+  { 0x0700, 0xa17e, 0x0000 },
+  { 0x0700, 0xa180, 0x0000 },
+  { 0x8700, 0xa183, 0x2000 },
+  { 0x0700, 0xa182, 0x0000 },
+  { 0x0700, 0xa184, 0x0000 },
+  { 0x8700, 0xa189, 0x3000 },
+  { 0x8700, 0xa187, 0x2000 },
+  { 0x0700, 0xa186, 0x0000 },
+  { 0x0700, 0xa188, 0x0000 },
+  { 0x8700, 0xa18b, 0x2000 },
+  { 0x0700, 0xa18a, 0x0000 },
+  { 0x0700, 0xa18c, 0x0000 },
+  { 0x8700, 0xa1ad, 0x6000 },
+  { 0x8700, 0xa19d, 0x5000 },
+  { 0x8700, 0xa195, 0x4000 },
+  { 0x8700, 0xa191, 0x3000 },
+  { 0x8700, 0xa18f, 0x2000 },
+  { 0x0700, 0xa18e, 0x0000 },
+  { 0x0700, 0xa190, 0x0000 },
+  { 0x8700, 0xa193, 0x2000 },
+  { 0x0700, 0xa192, 0x0000 },
+  { 0x0700, 0xa194, 0x0000 },
+  { 0x8700, 0xa199, 0x3000 },
+  { 0x8700, 0xa197, 0x2000 },
+  { 0x0700, 0xa196, 0x0000 },
+  { 0x0700, 0xa198, 0x0000 },
+  { 0x8700, 0xa19b, 0x2000 },
+  { 0x0700, 0xa19a, 0x0000 },
+  { 0x0700, 0xa19c, 0x0000 },
+  { 0x8700, 0xa1a5, 0x4000 },
+  { 0x8700, 0xa1a1, 0x3000 },
+  { 0x8700, 0xa19f, 0x2000 },
+  { 0x0700, 0xa19e, 0x0000 },
+  { 0x0700, 0xa1a0, 0x0000 },
+  { 0x8700, 0xa1a3, 0x2000 },
+  { 0x0700, 0xa1a2, 0x0000 },
+  { 0x0700, 0xa1a4, 0x0000 },
+  { 0x8700, 0xa1a9, 0x3000 },
+  { 0x8700, 0xa1a7, 0x2000 },
+  { 0x0700, 0xa1a6, 0x0000 },
+  { 0x0700, 0xa1a8, 0x0000 },
+  { 0x8700, 0xa1ab, 0x2000 },
+  { 0x0700, 0xa1aa, 0x0000 },
+  { 0x0700, 0xa1ac, 0x0000 },
+  { 0x8700, 0xa1bd, 0x5000 },
+  { 0x8700, 0xa1b5, 0x4000 },
+  { 0x8700, 0xa1b1, 0x3000 },
+  { 0x8700, 0xa1af, 0x2000 },
+  { 0x0700, 0xa1ae, 0x0000 },
+  { 0x0700, 0xa1b0, 0x0000 },
+  { 0x8700, 0xa1b3, 0x2000 },
+  { 0x0700, 0xa1b2, 0x0000 },
+  { 0x0700, 0xa1b4, 0x0000 },
+  { 0x8700, 0xa1b9, 0x3000 },
+  { 0x8700, 0xa1b7, 0x2000 },
+  { 0x0700, 0xa1b6, 0x0000 },
+  { 0x0700, 0xa1b8, 0x0000 },
+  { 0x8700, 0xa1bb, 0x2000 },
+  { 0x0700, 0xa1ba, 0x0000 },
+  { 0x0700, 0xa1bc, 0x0000 },
+  { 0x8700, 0xa1c5, 0x4000 },
+  { 0x8700, 0xa1c1, 0x3000 },
+  { 0x8700, 0xa1bf, 0x2000 },
+  { 0x0700, 0xa1be, 0x0000 },
+  { 0x0700, 0xa1c0, 0x0000 },
+  { 0x8700, 0xa1c3, 0x2000 },
+  { 0x0700, 0xa1c2, 0x0000 },
+  { 0x0700, 0xa1c4, 0x0000 },
+  { 0x8700, 0xa1c9, 0x3000 },
+  { 0x8700, 0xa1c7, 0x2000 },
+  { 0x0700, 0xa1c6, 0x0000 },
+  { 0x0700, 0xa1c8, 0x0000 },
+  { 0x8700, 0xa1cb, 0x2000 },
+  { 0x0700, 0xa1ca, 0x0000 },
+  { 0x0700, 0xa1cc, 0x0000 },
+  { 0x8700, 0xa20d, 0x7000 },
+  { 0x8700, 0xa1ed, 0x6000 },
+  { 0x8700, 0xa1dd, 0x5000 },
+  { 0x8700, 0xa1d5, 0x4000 },
+  { 0x8700, 0xa1d1, 0x3000 },
+  { 0x8700, 0xa1cf, 0x2000 },
+  { 0x0700, 0xa1ce, 0x0000 },
+  { 0x0700, 0xa1d0, 0x0000 },
+  { 0x8700, 0xa1d3, 0x2000 },
+  { 0x0700, 0xa1d2, 0x0000 },
+  { 0x0700, 0xa1d4, 0x0000 },
+  { 0x8700, 0xa1d9, 0x3000 },
+  { 0x8700, 0xa1d7, 0x2000 },
+  { 0x0700, 0xa1d6, 0x0000 },
+  { 0x0700, 0xa1d8, 0x0000 },
+  { 0x8700, 0xa1db, 0x2000 },
+  { 0x0700, 0xa1da, 0x0000 },
+  { 0x0700, 0xa1dc, 0x0000 },
+  { 0x8700, 0xa1e5, 0x4000 },
+  { 0x8700, 0xa1e1, 0x3000 },
+  { 0x8700, 0xa1df, 0x2000 },
+  { 0x0700, 0xa1de, 0x0000 },
+  { 0x0700, 0xa1e0, 0x0000 },
+  { 0x8700, 0xa1e3, 0x2000 },
+  { 0x0700, 0xa1e2, 0x0000 },
+  { 0x0700, 0xa1e4, 0x0000 },
+  { 0x8700, 0xa1e9, 0x3000 },
+  { 0x8700, 0xa1e7, 0x2000 },
+  { 0x0700, 0xa1e6, 0x0000 },
+  { 0x0700, 0xa1e8, 0x0000 },
+  { 0x8700, 0xa1eb, 0x2000 },
+  { 0x0700, 0xa1ea, 0x0000 },
+  { 0x0700, 0xa1ec, 0x0000 },
+  { 0x8700, 0xa1fd, 0x5000 },
+  { 0x8700, 0xa1f5, 0x4000 },
+  { 0x8700, 0xa1f1, 0x3000 },
+  { 0x8700, 0xa1ef, 0x2000 },
+  { 0x0700, 0xa1ee, 0x0000 },
+  { 0x0700, 0xa1f0, 0x0000 },
+  { 0x8700, 0xa1f3, 0x2000 },
+  { 0x0700, 0xa1f2, 0x0000 },
+  { 0x0700, 0xa1f4, 0x0000 },
+  { 0x8700, 0xa1f9, 0x3000 },
+  { 0x8700, 0xa1f7, 0x2000 },
+  { 0x0700, 0xa1f6, 0x0000 },
+  { 0x0700, 0xa1f8, 0x0000 },
+  { 0x8700, 0xa1fb, 0x2000 },
+  { 0x0700, 0xa1fa, 0x0000 },
+  { 0x0700, 0xa1fc, 0x0000 },
+  { 0x8700, 0xa205, 0x4000 },
+  { 0x8700, 0xa201, 0x3000 },
+  { 0x8700, 0xa1ff, 0x2000 },
+  { 0x0700, 0xa1fe, 0x0000 },
+  { 0x0700, 0xa200, 0x0000 },
+  { 0x8700, 0xa203, 0x2000 },
+  { 0x0700, 0xa202, 0x0000 },
+  { 0x0700, 0xa204, 0x0000 },
+  { 0x8700, 0xa209, 0x3000 },
+  { 0x8700, 0xa207, 0x2000 },
+  { 0x0700, 0xa206, 0x0000 },
+  { 0x0700, 0xa208, 0x0000 },
+  { 0x8700, 0xa20b, 0x2000 },
+  { 0x0700, 0xa20a, 0x0000 },
+  { 0x0700, 0xa20c, 0x0000 },
+  { 0x8700, 0xa22d, 0x6000 },
+  { 0x8700, 0xa21d, 0x5000 },
+  { 0x8700, 0xa215, 0x4000 },
+  { 0x8700, 0xa211, 0x3000 },
+  { 0x8700, 0xa20f, 0x2000 },
+  { 0x0700, 0xa20e, 0x0000 },
+  { 0x0700, 0xa210, 0x0000 },
+  { 0x8700, 0xa213, 0x2000 },
+  { 0x0700, 0xa212, 0x0000 },
+  { 0x0700, 0xa214, 0x0000 },
+  { 0x8700, 0xa219, 0x3000 },
+  { 0x8700, 0xa217, 0x2000 },
+  { 0x0700, 0xa216, 0x0000 },
+  { 0x0700, 0xa218, 0x0000 },
+  { 0x8700, 0xa21b, 0x2000 },
+  { 0x0700, 0xa21a, 0x0000 },
+  { 0x0700, 0xa21c, 0x0000 },
+  { 0x8700, 0xa225, 0x4000 },
+  { 0x8700, 0xa221, 0x3000 },
+  { 0x8700, 0xa21f, 0x2000 },
+  { 0x0700, 0xa21e, 0x0000 },
+  { 0x0700, 0xa220, 0x0000 },
+  { 0x8700, 0xa223, 0x2000 },
+  { 0x0700, 0xa222, 0x0000 },
+  { 0x0700, 0xa224, 0x0000 },
+  { 0x8700, 0xa229, 0x3000 },
+  { 0x8700, 0xa227, 0x2000 },
+  { 0x0700, 0xa226, 0x0000 },
+  { 0x0700, 0xa228, 0x0000 },
+  { 0x8700, 0xa22b, 0x2000 },
+  { 0x0700, 0xa22a, 0x0000 },
+  { 0x0700, 0xa22c, 0x0000 },
+  { 0x8700, 0xa23d, 0x5000 },
+  { 0x8700, 0xa235, 0x4000 },
+  { 0x8700, 0xa231, 0x3000 },
+  { 0x8700, 0xa22f, 0x2000 },
+  { 0x0700, 0xa22e, 0x0000 },
+  { 0x0700, 0xa230, 0x0000 },
+  { 0x8700, 0xa233, 0x2000 },
+  { 0x0700, 0xa232, 0x0000 },
+  { 0x0700, 0xa234, 0x0000 },
+  { 0x8700, 0xa239, 0x3000 },
+  { 0x8700, 0xa237, 0x2000 },
+  { 0x0700, 0xa236, 0x0000 },
+  { 0x0700, 0xa238, 0x0000 },
+  { 0x8700, 0xa23b, 0x2000 },
+  { 0x0700, 0xa23a, 0x0000 },
+  { 0x0700, 0xa23c, 0x0000 },
+  { 0x8700, 0xa245, 0x4000 },
+  { 0x8700, 0xa241, 0x3000 },
+  { 0x8700, 0xa23f, 0x2000 },
+  { 0x0700, 0xa23e, 0x0000 },
+  { 0x0700, 0xa240, 0x0000 },
+  { 0x8700, 0xa243, 0x2000 },
+  { 0x0700, 0xa242, 0x0000 },
+  { 0x0700, 0xa244, 0x0000 },
+  { 0x8700, 0xa249, 0x3000 },
+  { 0x8700, 0xa247, 0x2000 },
+  { 0x0700, 0xa246, 0x0000 },
+  { 0x0700, 0xa248, 0x0000 },
+  { 0x8700, 0xa24b, 0x2000 },
+  { 0x0700, 0xa24a, 0x0000 },
+  { 0x0700, 0xa24c, 0x0000 },
+  { 0x8700, 0xa2cd, 0x8000 },
+  { 0x8700, 0xa28d, 0x7000 },
+  { 0x8700, 0xa26d, 0x6000 },
+  { 0x8700, 0xa25d, 0x5000 },
+  { 0x8700, 0xa255, 0x4000 },
+  { 0x8700, 0xa251, 0x3000 },
+  { 0x8700, 0xa24f, 0x2000 },
+  { 0x0700, 0xa24e, 0x0000 },
+  { 0x0700, 0xa250, 0x0000 },
+  { 0x8700, 0xa253, 0x2000 },
+  { 0x0700, 0xa252, 0x0000 },
+  { 0x0700, 0xa254, 0x0000 },
+  { 0x8700, 0xa259, 0x3000 },
+  { 0x8700, 0xa257, 0x2000 },
+  { 0x0700, 0xa256, 0x0000 },
+  { 0x0700, 0xa258, 0x0000 },
+  { 0x8700, 0xa25b, 0x2000 },
+  { 0x0700, 0xa25a, 0x0000 },
+  { 0x0700, 0xa25c, 0x0000 },
+  { 0x8700, 0xa265, 0x4000 },
+  { 0x8700, 0xa261, 0x3000 },
+  { 0x8700, 0xa25f, 0x2000 },
+  { 0x0700, 0xa25e, 0x0000 },
+  { 0x0700, 0xa260, 0x0000 },
+  { 0x8700, 0xa263, 0x2000 },
+  { 0x0700, 0xa262, 0x0000 },
+  { 0x0700, 0xa264, 0x0000 },
+  { 0x8700, 0xa269, 0x3000 },
+  { 0x8700, 0xa267, 0x2000 },
+  { 0x0700, 0xa266, 0x0000 },
+  { 0x0700, 0xa268, 0x0000 },
+  { 0x8700, 0xa26b, 0x2000 },
+  { 0x0700, 0xa26a, 0x0000 },
+  { 0x0700, 0xa26c, 0x0000 },
+  { 0x8700, 0xa27d, 0x5000 },
+  { 0x8700, 0xa275, 0x4000 },
+  { 0x8700, 0xa271, 0x3000 },
+  { 0x8700, 0xa26f, 0x2000 },
+  { 0x0700, 0xa26e, 0x0000 },
+  { 0x0700, 0xa270, 0x0000 },
+  { 0x8700, 0xa273, 0x2000 },
+  { 0x0700, 0xa272, 0x0000 },
+  { 0x0700, 0xa274, 0x0000 },
+  { 0x8700, 0xa279, 0x3000 },
+  { 0x8700, 0xa277, 0x2000 },
+  { 0x0700, 0xa276, 0x0000 },
+  { 0x0700, 0xa278, 0x0000 },
+  { 0x8700, 0xa27b, 0x2000 },
+  { 0x0700, 0xa27a, 0x0000 },
+  { 0x0700, 0xa27c, 0x0000 },
+  { 0x8700, 0xa285, 0x4000 },
+  { 0x8700, 0xa281, 0x3000 },
+  { 0x8700, 0xa27f, 0x2000 },
+  { 0x0700, 0xa27e, 0x0000 },
+  { 0x0700, 0xa280, 0x0000 },
+  { 0x8700, 0xa283, 0x2000 },
+  { 0x0700, 0xa282, 0x0000 },
+  { 0x0700, 0xa284, 0x0000 },
+  { 0x8700, 0xa289, 0x3000 },
+  { 0x8700, 0xa287, 0x2000 },
+  { 0x0700, 0xa286, 0x0000 },
+  { 0x0700, 0xa288, 0x0000 },
+  { 0x8700, 0xa28b, 0x2000 },
+  { 0x0700, 0xa28a, 0x0000 },
+  { 0x0700, 0xa28c, 0x0000 },
+  { 0x8700, 0xa2ad, 0x6000 },
+  { 0x8700, 0xa29d, 0x5000 },
+  { 0x8700, 0xa295, 0x4000 },
+  { 0x8700, 0xa291, 0x3000 },
+  { 0x8700, 0xa28f, 0x2000 },
+  { 0x0700, 0xa28e, 0x0000 },
+  { 0x0700, 0xa290, 0x0000 },
+  { 0x8700, 0xa293, 0x2000 },
+  { 0x0700, 0xa292, 0x0000 },
+  { 0x0700, 0xa294, 0x0000 },
+  { 0x8700, 0xa299, 0x3000 },
+  { 0x8700, 0xa297, 0x2000 },
+  { 0x0700, 0xa296, 0x0000 },
+  { 0x0700, 0xa298, 0x0000 },
+  { 0x8700, 0xa29b, 0x2000 },
+  { 0x0700, 0xa29a, 0x0000 },
+  { 0x0700, 0xa29c, 0x0000 },
+  { 0x8700, 0xa2a5, 0x4000 },
+  { 0x8700, 0xa2a1, 0x3000 },
+  { 0x8700, 0xa29f, 0x2000 },
+  { 0x0700, 0xa29e, 0x0000 },
+  { 0x0700, 0xa2a0, 0x0000 },
+  { 0x8700, 0xa2a3, 0x2000 },
+  { 0x0700, 0xa2a2, 0x0000 },
+  { 0x0700, 0xa2a4, 0x0000 },
+  { 0x8700, 0xa2a9, 0x3000 },
+  { 0x8700, 0xa2a7, 0x2000 },
+  { 0x0700, 0xa2a6, 0x0000 },
+  { 0x0700, 0xa2a8, 0x0000 },
+  { 0x8700, 0xa2ab, 0x2000 },
+  { 0x0700, 0xa2aa, 0x0000 },
+  { 0x0700, 0xa2ac, 0x0000 },
+  { 0x8700, 0xa2bd, 0x5000 },
+  { 0x8700, 0xa2b5, 0x4000 },
+  { 0x8700, 0xa2b1, 0x3000 },
+  { 0x8700, 0xa2af, 0x2000 },
+  { 0x0700, 0xa2ae, 0x0000 },
+  { 0x0700, 0xa2b0, 0x0000 },
+  { 0x8700, 0xa2b3, 0x2000 },
+  { 0x0700, 0xa2b2, 0x0000 },
+  { 0x0700, 0xa2b4, 0x0000 },
+  { 0x8700, 0xa2b9, 0x3000 },
+  { 0x8700, 0xa2b7, 0x2000 },
+  { 0x0700, 0xa2b6, 0x0000 },
+  { 0x0700, 0xa2b8, 0x0000 },
+  { 0x8700, 0xa2bb, 0x2000 },
+  { 0x0700, 0xa2ba, 0x0000 },
+  { 0x0700, 0xa2bc, 0x0000 },
+  { 0x8700, 0xa2c5, 0x4000 },
+  { 0x8700, 0xa2c1, 0x3000 },
+  { 0x8700, 0xa2bf, 0x2000 },
+  { 0x0700, 0xa2be, 0x0000 },
+  { 0x0700, 0xa2c0, 0x0000 },
+  { 0x8700, 0xa2c3, 0x2000 },
+  { 0x0700, 0xa2c2, 0x0000 },
+  { 0x0700, 0xa2c4, 0x0000 },
+  { 0x8700, 0xa2c9, 0x3000 },
+  { 0x8700, 0xa2c7, 0x2000 },
+  { 0x0700, 0xa2c6, 0x0000 },
+  { 0x0700, 0xa2c8, 0x0000 },
+  { 0x8700, 0xa2cb, 0x2000 },
+  { 0x0700, 0xa2ca, 0x0000 },
+  { 0x0700, 0xa2cc, 0x0000 },
+  { 0x8700, 0xa30d, 0x7000 },
+  { 0x8700, 0xa2ed, 0x6000 },
+  { 0x8700, 0xa2dd, 0x5000 },
+  { 0x8700, 0xa2d5, 0x4000 },
+  { 0x8700, 0xa2d1, 0x3000 },
+  { 0x8700, 0xa2cf, 0x2000 },
+  { 0x0700, 0xa2ce, 0x0000 },
+  { 0x0700, 0xa2d0, 0x0000 },
+  { 0x8700, 0xa2d3, 0x2000 },
+  { 0x0700, 0xa2d2, 0x0000 },
+  { 0x0700, 0xa2d4, 0x0000 },
+  { 0x8700, 0xa2d9, 0x3000 },
+  { 0x8700, 0xa2d7, 0x2000 },
+  { 0x0700, 0xa2d6, 0x0000 },
+  { 0x0700, 0xa2d8, 0x0000 },
+  { 0x8700, 0xa2db, 0x2000 },
+  { 0x0700, 0xa2da, 0x0000 },
+  { 0x0700, 0xa2dc, 0x0000 },
+  { 0x8700, 0xa2e5, 0x4000 },
+  { 0x8700, 0xa2e1, 0x3000 },
+  { 0x8700, 0xa2df, 0x2000 },
+  { 0x0700, 0xa2de, 0x0000 },
+  { 0x0700, 0xa2e0, 0x0000 },
+  { 0x8700, 0xa2e3, 0x2000 },
+  { 0x0700, 0xa2e2, 0x0000 },
+  { 0x0700, 0xa2e4, 0x0000 },
+  { 0x8700, 0xa2e9, 0x3000 },
+  { 0x8700, 0xa2e7, 0x2000 },
+  { 0x0700, 0xa2e6, 0x0000 },
+  { 0x0700, 0xa2e8, 0x0000 },
+  { 0x8700, 0xa2eb, 0x2000 },
+  { 0x0700, 0xa2ea, 0x0000 },
+  { 0x0700, 0xa2ec, 0x0000 },
+  { 0x8700, 0xa2fd, 0x5000 },
+  { 0x8700, 0xa2f5, 0x4000 },
+  { 0x8700, 0xa2f1, 0x3000 },
+  { 0x8700, 0xa2ef, 0x2000 },
+  { 0x0700, 0xa2ee, 0x0000 },
+  { 0x0700, 0xa2f0, 0x0000 },
+  { 0x8700, 0xa2f3, 0x2000 },
+  { 0x0700, 0xa2f2, 0x0000 },
+  { 0x0700, 0xa2f4, 0x0000 },
+  { 0x8700, 0xa2f9, 0x3000 },
+  { 0x8700, 0xa2f7, 0x2000 },
+  { 0x0700, 0xa2f6, 0x0000 },
+  { 0x0700, 0xa2f8, 0x0000 },
+  { 0x8700, 0xa2fb, 0x2000 },
+  { 0x0700, 0xa2fa, 0x0000 },
+  { 0x0700, 0xa2fc, 0x0000 },
+  { 0x8700, 0xa305, 0x4000 },
+  { 0x8700, 0xa301, 0x3000 },
+  { 0x8700, 0xa2ff, 0x2000 },
+  { 0x0700, 0xa2fe, 0x0000 },
+  { 0x0700, 0xa300, 0x0000 },
+  { 0x8700, 0xa303, 0x2000 },
+  { 0x0700, 0xa302, 0x0000 },
+  { 0x0700, 0xa304, 0x0000 },
+  { 0x8700, 0xa309, 0x3000 },
+  { 0x8700, 0xa307, 0x2000 },
+  { 0x0700, 0xa306, 0x0000 },
+  { 0x0700, 0xa308, 0x0000 },
+  { 0x8700, 0xa30b, 0x2000 },
+  { 0x0700, 0xa30a, 0x0000 },
+  { 0x0700, 0xa30c, 0x0000 },
+  { 0x8700, 0xa32d, 0x6000 },
+  { 0x8700, 0xa31d, 0x5000 },
+  { 0x8700, 0xa315, 0x4000 },
+  { 0x8700, 0xa311, 0x3000 },
+  { 0x8700, 0xa30f, 0x2000 },
+  { 0x0700, 0xa30e, 0x0000 },
+  { 0x0700, 0xa310, 0x0000 },
+  { 0x8700, 0xa313, 0x2000 },
+  { 0x0700, 0xa312, 0x0000 },
+  { 0x0700, 0xa314, 0x0000 },
+  { 0x8700, 0xa319, 0x3000 },
+  { 0x8700, 0xa317, 0x2000 },
+  { 0x0700, 0xa316, 0x0000 },
+  { 0x0700, 0xa318, 0x0000 },
+  { 0x8700, 0xa31b, 0x2000 },
+  { 0x0700, 0xa31a, 0x0000 },
+  { 0x0700, 0xa31c, 0x0000 },
+  { 0x8700, 0xa325, 0x4000 },
+  { 0x8700, 0xa321, 0x3000 },
+  { 0x8700, 0xa31f, 0x2000 },
+  { 0x0700, 0xa31e, 0x0000 },
+  { 0x0700, 0xa320, 0x0000 },
+  { 0x8700, 0xa323, 0x2000 },
+  { 0x0700, 0xa322, 0x0000 },
+  { 0x0700, 0xa324, 0x0000 },
+  { 0x8700, 0xa329, 0x3000 },
+  { 0x8700, 0xa327, 0x2000 },
+  { 0x0700, 0xa326, 0x0000 },
+  { 0x0700, 0xa328, 0x0000 },
+  { 0x8700, 0xa32b, 0x2000 },
+  { 0x0700, 0xa32a, 0x0000 },
+  { 0x0700, 0xa32c, 0x0000 },
+  { 0x8700, 0xa33d, 0x5000 },
+  { 0x8700, 0xa335, 0x4000 },
+  { 0x8700, 0xa331, 0x3000 },
+  { 0x8700, 0xa32f, 0x2000 },
+  { 0x0700, 0xa32e, 0x0000 },
+  { 0x0700, 0xa330, 0x0000 },
+  { 0x8700, 0xa333, 0x2000 },
+  { 0x0700, 0xa332, 0x0000 },
+  { 0x0700, 0xa334, 0x0000 },
+  { 0x8700, 0xa339, 0x3000 },
+  { 0x8700, 0xa337, 0x2000 },
+  { 0x0700, 0xa336, 0x0000 },
+  { 0x0700, 0xa338, 0x0000 },
+  { 0x8700, 0xa33b, 0x2000 },
+  { 0x0700, 0xa33a, 0x0000 },
+  { 0x0700, 0xa33c, 0x0000 },
+  { 0x8700, 0xa345, 0x4000 },
+  { 0x8700, 0xa341, 0x3000 },
+  { 0x8700, 0xa33f, 0x2000 },
+  { 0x0700, 0xa33e, 0x0000 },
+  { 0x0700, 0xa340, 0x0000 },
+  { 0x8700, 0xa343, 0x2000 },
+  { 0x0700, 0xa342, 0x0000 },
+  { 0x0700, 0xa344, 0x0000 },
+  { 0x8700, 0xa349, 0x3000 },
+  { 0x8700, 0xa347, 0x2000 },
+  { 0x0700, 0xa346, 0x0000 },
+  { 0x0700, 0xa348, 0x0000 },
+  { 0x8700, 0xa34b, 0x2000 },
+  { 0x0700, 0xa34a, 0x0000 },
+  { 0x0700, 0xa34c, 0x0000 },
+  { 0x8700, 0xfc4d, 0xb000 },
+  { 0x8700, 0xf97f, 0xa000 },
+  { 0x8700, 0xa44d, 0x9000 },
+  { 0x8700, 0xa3cd, 0x8000 },
+  { 0x8700, 0xa38d, 0x7000 },
+  { 0x8700, 0xa36d, 0x6000 },
+  { 0x8700, 0xa35d, 0x5000 },
+  { 0x8700, 0xa355, 0x4000 },
+  { 0x8700, 0xa351, 0x3000 },
+  { 0x8700, 0xa34f, 0x2000 },
+  { 0x0700, 0xa34e, 0x0000 },
+  { 0x0700, 0xa350, 0x0000 },
+  { 0x8700, 0xa353, 0x2000 },
+  { 0x0700, 0xa352, 0x0000 },
+  { 0x0700, 0xa354, 0x0000 },
+  { 0x8700, 0xa359, 0x3000 },
+  { 0x8700, 0xa357, 0x2000 },
+  { 0x0700, 0xa356, 0x0000 },
+  { 0x0700, 0xa358, 0x0000 },
+  { 0x8700, 0xa35b, 0x2000 },
+  { 0x0700, 0xa35a, 0x0000 },
+  { 0x0700, 0xa35c, 0x0000 },
+  { 0x8700, 0xa365, 0x4000 },
+  { 0x8700, 0xa361, 0x3000 },
+  { 0x8700, 0xa35f, 0x2000 },
+  { 0x0700, 0xa35e, 0x0000 },
+  { 0x0700, 0xa360, 0x0000 },
+  { 0x8700, 0xa363, 0x2000 },
+  { 0x0700, 0xa362, 0x0000 },
+  { 0x0700, 0xa364, 0x0000 },
+  { 0x8700, 0xa369, 0x3000 },
+  { 0x8700, 0xa367, 0x2000 },
+  { 0x0700, 0xa366, 0x0000 },
+  { 0x0700, 0xa368, 0x0000 },
+  { 0x8700, 0xa36b, 0x2000 },
+  { 0x0700, 0xa36a, 0x0000 },
+  { 0x0700, 0xa36c, 0x0000 },
+  { 0x8700, 0xa37d, 0x5000 },
+  { 0x8700, 0xa375, 0x4000 },
+  { 0x8700, 0xa371, 0x3000 },
+  { 0x8700, 0xa36f, 0x2000 },
+  { 0x0700, 0xa36e, 0x0000 },
+  { 0x0700, 0xa370, 0x0000 },
+  { 0x8700, 0xa373, 0x2000 },
+  { 0x0700, 0xa372, 0x0000 },
+  { 0x0700, 0xa374, 0x0000 },
+  { 0x8700, 0xa379, 0x3000 },
+  { 0x8700, 0xa377, 0x2000 },
+  { 0x0700, 0xa376, 0x0000 },
+  { 0x0700, 0xa378, 0x0000 },
+  { 0x8700, 0xa37b, 0x2000 },
+  { 0x0700, 0xa37a, 0x0000 },
+  { 0x0700, 0xa37c, 0x0000 },
+  { 0x8700, 0xa385, 0x4000 },
+  { 0x8700, 0xa381, 0x3000 },
+  { 0x8700, 0xa37f, 0x2000 },
+  { 0x0700, 0xa37e, 0x0000 },
+  { 0x0700, 0xa380, 0x0000 },
+  { 0x8700, 0xa383, 0x2000 },
+  { 0x0700, 0xa382, 0x0000 },
+  { 0x0700, 0xa384, 0x0000 },
+  { 0x8700, 0xa389, 0x3000 },
+  { 0x8700, 0xa387, 0x2000 },
+  { 0x0700, 0xa386, 0x0000 },
+  { 0x0700, 0xa388, 0x0000 },
+  { 0x8700, 0xa38b, 0x2000 },
+  { 0x0700, 0xa38a, 0x0000 },
+  { 0x0700, 0xa38c, 0x0000 },
+  { 0x8700, 0xa3ad, 0x6000 },
+  { 0x8700, 0xa39d, 0x5000 },
+  { 0x8700, 0xa395, 0x4000 },
+  { 0x8700, 0xa391, 0x3000 },
+  { 0x8700, 0xa38f, 0x2000 },
+  { 0x0700, 0xa38e, 0x0000 },
+  { 0x0700, 0xa390, 0x0000 },
+  { 0x8700, 0xa393, 0x2000 },
+  { 0x0700, 0xa392, 0x0000 },
+  { 0x0700, 0xa394, 0x0000 },
+  { 0x8700, 0xa399, 0x3000 },
+  { 0x8700, 0xa397, 0x2000 },
+  { 0x0700, 0xa396, 0x0000 },
+  { 0x0700, 0xa398, 0x0000 },
+  { 0x8700, 0xa39b, 0x2000 },
+  { 0x0700, 0xa39a, 0x0000 },
+  { 0x0700, 0xa39c, 0x0000 },
+  { 0x8700, 0xa3a5, 0x4000 },
+  { 0x8700, 0xa3a1, 0x3000 },
+  { 0x8700, 0xa39f, 0x2000 },
+  { 0x0700, 0xa39e, 0x0000 },
+  { 0x0700, 0xa3a0, 0x0000 },
+  { 0x8700, 0xa3a3, 0x2000 },
+  { 0x0700, 0xa3a2, 0x0000 },
+  { 0x0700, 0xa3a4, 0x0000 },
+  { 0x8700, 0xa3a9, 0x3000 },
+  { 0x8700, 0xa3a7, 0x2000 },
+  { 0x0700, 0xa3a6, 0x0000 },
+  { 0x0700, 0xa3a8, 0x0000 },
+  { 0x8700, 0xa3ab, 0x2000 },
+  { 0x0700, 0xa3aa, 0x0000 },
+  { 0x0700, 0xa3ac, 0x0000 },
+  { 0x8700, 0xa3bd, 0x5000 },
+  { 0x8700, 0xa3b5, 0x4000 },
+  { 0x8700, 0xa3b1, 0x3000 },
+  { 0x8700, 0xa3af, 0x2000 },
+  { 0x0700, 0xa3ae, 0x0000 },
+  { 0x0700, 0xa3b0, 0x0000 },
+  { 0x8700, 0xa3b3, 0x2000 },
+  { 0x0700, 0xa3b2, 0x0000 },
+  { 0x0700, 0xa3b4, 0x0000 },
+  { 0x8700, 0xa3b9, 0x3000 },
+  { 0x8700, 0xa3b7, 0x2000 },
+  { 0x0700, 0xa3b6, 0x0000 },
+  { 0x0700, 0xa3b8, 0x0000 },
+  { 0x8700, 0xa3bb, 0x2000 },
+  { 0x0700, 0xa3ba, 0x0000 },
+  { 0x0700, 0xa3bc, 0x0000 },
+  { 0x8700, 0xa3c5, 0x4000 },
+  { 0x8700, 0xa3c1, 0x3000 },
+  { 0x8700, 0xa3bf, 0x2000 },
+  { 0x0700, 0xa3be, 0x0000 },
+  { 0x0700, 0xa3c0, 0x0000 },
+  { 0x8700, 0xa3c3, 0x2000 },
+  { 0x0700, 0xa3c2, 0x0000 },
+  { 0x0700, 0xa3c4, 0x0000 },
+  { 0x8700, 0xa3c9, 0x3000 },
+  { 0x8700, 0xa3c7, 0x2000 },
+  { 0x0700, 0xa3c6, 0x0000 },
+  { 0x0700, 0xa3c8, 0x0000 },
+  { 0x8700, 0xa3cb, 0x2000 },
+  { 0x0700, 0xa3ca, 0x0000 },
+  { 0x0700, 0xa3cc, 0x0000 },
+  { 0x8700, 0xa40d, 0x7000 },
+  { 0x8700, 0xa3ed, 0x6000 },
+  { 0x8700, 0xa3dd, 0x5000 },
+  { 0x8700, 0xa3d5, 0x4000 },
+  { 0x8700, 0xa3d1, 0x3000 },
+  { 0x8700, 0xa3cf, 0x2000 },
+  { 0x0700, 0xa3ce, 0x0000 },
+  { 0x0700, 0xa3d0, 0x0000 },
+  { 0x8700, 0xa3d3, 0x2000 },
+  { 0x0700, 0xa3d2, 0x0000 },
+  { 0x0700, 0xa3d4, 0x0000 },
+  { 0x8700, 0xa3d9, 0x3000 },
+  { 0x8700, 0xa3d7, 0x2000 },
+  { 0x0700, 0xa3d6, 0x0000 },
+  { 0x0700, 0xa3d8, 0x0000 },
+  { 0x8700, 0xa3db, 0x2000 },
+  { 0x0700, 0xa3da, 0x0000 },
+  { 0x0700, 0xa3dc, 0x0000 },
+  { 0x8700, 0xa3e5, 0x4000 },
+  { 0x8700, 0xa3e1, 0x3000 },
+  { 0x8700, 0xa3df, 0x2000 },
+  { 0x0700, 0xa3de, 0x0000 },
+  { 0x0700, 0xa3e0, 0x0000 },
+  { 0x8700, 0xa3e3, 0x2000 },
+  { 0x0700, 0xa3e2, 0x0000 },
+  { 0x0700, 0xa3e4, 0x0000 },
+  { 0x8700, 0xa3e9, 0x3000 },
+  { 0x8700, 0xa3e7, 0x2000 },
+  { 0x0700, 0xa3e6, 0x0000 },
+  { 0x0700, 0xa3e8, 0x0000 },
+  { 0x8700, 0xa3eb, 0x2000 },
+  { 0x0700, 0xa3ea, 0x0000 },
+  { 0x0700, 0xa3ec, 0x0000 },
+  { 0x8700, 0xa3fd, 0x5000 },
+  { 0x8700, 0xa3f5, 0x4000 },
+  { 0x8700, 0xa3f1, 0x3000 },
+  { 0x8700, 0xa3ef, 0x2000 },
+  { 0x0700, 0xa3ee, 0x0000 },
+  { 0x0700, 0xa3f0, 0x0000 },
+  { 0x8700, 0xa3f3, 0x2000 },
+  { 0x0700, 0xa3f2, 0x0000 },
+  { 0x0700, 0xa3f4, 0x0000 },
+  { 0x8700, 0xa3f9, 0x3000 },
+  { 0x8700, 0xa3f7, 0x2000 },
+  { 0x0700, 0xa3f6, 0x0000 },
+  { 0x0700, 0xa3f8, 0x0000 },
+  { 0x8700, 0xa3fb, 0x2000 },
+  { 0x0700, 0xa3fa, 0x0000 },
+  { 0x0700, 0xa3fc, 0x0000 },
+  { 0x8700, 0xa405, 0x4000 },
+  { 0x8700, 0xa401, 0x3000 },
+  { 0x8700, 0xa3ff, 0x2000 },
+  { 0x0700, 0xa3fe, 0x0000 },
+  { 0x0700, 0xa400, 0x0000 },
+  { 0x8700, 0xa403, 0x2000 },
+  { 0x0700, 0xa402, 0x0000 },
+  { 0x0700, 0xa404, 0x0000 },
+  { 0x8700, 0xa409, 0x3000 },
+  { 0x8700, 0xa407, 0x2000 },
+  { 0x0700, 0xa406, 0x0000 },
+  { 0x0700, 0xa408, 0x0000 },
+  { 0x8700, 0xa40b, 0x2000 },
+  { 0x0700, 0xa40a, 0x0000 },
+  { 0x0700, 0xa40c, 0x0000 },
+  { 0x8700, 0xa42d, 0x6000 },
+  { 0x8700, 0xa41d, 0x5000 },
+  { 0x8700, 0xa415, 0x4000 },
+  { 0x8700, 0xa411, 0x3000 },
+  { 0x8700, 0xa40f, 0x2000 },
+  { 0x0700, 0xa40e, 0x0000 },
+  { 0x0700, 0xa410, 0x0000 },
+  { 0x8700, 0xa413, 0x2000 },
+  { 0x0700, 0xa412, 0x0000 },
+  { 0x0700, 0xa414, 0x0000 },
+  { 0x8700, 0xa419, 0x3000 },
+  { 0x8700, 0xa417, 0x2000 },
+  { 0x0700, 0xa416, 0x0000 },
+  { 0x0700, 0xa418, 0x0000 },
+  { 0x8700, 0xa41b, 0x2000 },
+  { 0x0700, 0xa41a, 0x0000 },
+  { 0x0700, 0xa41c, 0x0000 },
+  { 0x8700, 0xa425, 0x4000 },
+  { 0x8700, 0xa421, 0x3000 },
+  { 0x8700, 0xa41f, 0x2000 },
+  { 0x0700, 0xa41e, 0x0000 },
+  { 0x0700, 0xa420, 0x0000 },
+  { 0x8700, 0xa423, 0x2000 },
+  { 0x0700, 0xa422, 0x0000 },
+  { 0x0700, 0xa424, 0x0000 },
+  { 0x8700, 0xa429, 0x3000 },
+  { 0x8700, 0xa427, 0x2000 },
+  { 0x0700, 0xa426, 0x0000 },
+  { 0x0700, 0xa428, 0x0000 },
+  { 0x8700, 0xa42b, 0x2000 },
+  { 0x0700, 0xa42a, 0x0000 },
+  { 0x0700, 0xa42c, 0x0000 },
+  { 0x8700, 0xa43d, 0x5000 },
+  { 0x8700, 0xa435, 0x4000 },
+  { 0x8700, 0xa431, 0x3000 },
+  { 0x8700, 0xa42f, 0x2000 },
+  { 0x0700, 0xa42e, 0x0000 },
+  { 0x0700, 0xa430, 0x0000 },
+  { 0x8700, 0xa433, 0x2000 },
+  { 0x0700, 0xa432, 0x0000 },
+  { 0x0700, 0xa434, 0x0000 },
+  { 0x8700, 0xa439, 0x3000 },
+  { 0x8700, 0xa437, 0x2000 },
+  { 0x0700, 0xa436, 0x0000 },
+  { 0x0700, 0xa438, 0x0000 },
+  { 0x8700, 0xa43b, 0x2000 },
+  { 0x0700, 0xa43a, 0x0000 },
+  { 0x0700, 0xa43c, 0x0000 },
+  { 0x8700, 0xa445, 0x4000 },
+  { 0x8700, 0xa441, 0x3000 },
+  { 0x8700, 0xa43f, 0x2000 },
+  { 0x0700, 0xa43e, 0x0000 },
+  { 0x0700, 0xa440, 0x0000 },
+  { 0x8700, 0xa443, 0x2000 },
+  { 0x0700, 0xa442, 0x0000 },
+  { 0x0700, 0xa444, 0x0000 },
+  { 0x8700, 0xa449, 0x3000 },
+  { 0x8700, 0xa447, 0x2000 },
+  { 0x0700, 0xa446, 0x0000 },
+  { 0x0700, 0xa448, 0x0000 },
+  { 0x8700, 0xa44b, 0x2000 },
+  { 0x0700, 0xa44a, 0x0000 },
+  { 0x0700, 0xa44c, 0x0000 },
+  { 0x8300, 0xf8ff, 0x8000 },
+  { 0x9a00, 0xa490, 0x7000 },
+  { 0x8700, 0xa46d, 0x6000 },
+  { 0x8700, 0xa45d, 0x5000 },
+  { 0x8700, 0xa455, 0x4000 },
+  { 0x8700, 0xa451, 0x3000 },
+  { 0x8700, 0xa44f, 0x2000 },
+  { 0x0700, 0xa44e, 0x0000 },
+  { 0x0700, 0xa450, 0x0000 },
+  { 0x8700, 0xa453, 0x2000 },
+  { 0x0700, 0xa452, 0x0000 },
+  { 0x0700, 0xa454, 0x0000 },
+  { 0x8700, 0xa459, 0x3000 },
+  { 0x8700, 0xa457, 0x2000 },
+  { 0x0700, 0xa456, 0x0000 },
+  { 0x0700, 0xa458, 0x0000 },
+  { 0x8700, 0xa45b, 0x2000 },
+  { 0x0700, 0xa45a, 0x0000 },
+  { 0x0700, 0xa45c, 0x0000 },
+  { 0x8700, 0xa465, 0x4000 },
+  { 0x8700, 0xa461, 0x3000 },
+  { 0x8700, 0xa45f, 0x2000 },
+  { 0x0700, 0xa45e, 0x0000 },
+  { 0x0700, 0xa460, 0x0000 },
+  { 0x8700, 0xa463, 0x2000 },
+  { 0x0700, 0xa462, 0x0000 },
+  { 0x0700, 0xa464, 0x0000 },
+  { 0x8700, 0xa469, 0x3000 },
+  { 0x8700, 0xa467, 0x2000 },
+  { 0x0700, 0xa466, 0x0000 },
+  { 0x0700, 0xa468, 0x0000 },
+  { 0x8700, 0xa46b, 0x2000 },
+  { 0x0700, 0xa46a, 0x0000 },
+  { 0x0700, 0xa46c, 0x0000 },
+  { 0x8700, 0xa47d, 0x5000 },
+  { 0x8700, 0xa475, 0x4000 },
+  { 0x8700, 0xa471, 0x3000 },
+  { 0x8700, 0xa46f, 0x2000 },
+  { 0x0700, 0xa46e, 0x0000 },
+  { 0x0700, 0xa470, 0x0000 },
+  { 0x8700, 0xa473, 0x2000 },
+  { 0x0700, 0xa472, 0x0000 },
+  { 0x0700, 0xa474, 0x0000 },
+  { 0x8700, 0xa479, 0x3000 },
+  { 0x8700, 0xa477, 0x2000 },
+  { 0x0700, 0xa476, 0x0000 },
+  { 0x0700, 0xa478, 0x0000 },
+  { 0x8700, 0xa47b, 0x2000 },
+  { 0x0700, 0xa47a, 0x0000 },
+  { 0x0700, 0xa47c, 0x0000 },
+  { 0x8700, 0xa485, 0x4000 },
+  { 0x8700, 0xa481, 0x3000 },
+  { 0x8700, 0xa47f, 0x2000 },
+  { 0x0700, 0xa47e, 0x0000 },
+  { 0x0700, 0xa480, 0x0000 },
+  { 0x8700, 0xa483, 0x2000 },
+  { 0x0700, 0xa482, 0x0000 },
+  { 0x0700, 0xa484, 0x0000 },
+  { 0x8700, 0xa489, 0x3000 },
+  { 0x8700, 0xa487, 0x2000 },
+  { 0x0700, 0xa486, 0x0000 },
+  { 0x0700, 0xa488, 0x0000 },
+  { 0x8700, 0xa48b, 0x2000 },
+  { 0x0700, 0xa48a, 0x0000 },
+  { 0x0700, 0xa48c, 0x0000 },
+  { 0x9a00, 0xa4b0, 0x6000 },
+  { 0x9a00, 0xa4a0, 0x5000 },
+  { 0x9a00, 0xa498, 0x4000 },
+  { 0x9a00, 0xa494, 0x3000 },
+  { 0x9a00, 0xa492, 0x2000 },
+  { 0x1a00, 0xa491, 0x0000 },
+  { 0x1a00, 0xa493, 0x0000 },
+  { 0x9a00, 0xa496, 0x2000 },
+  { 0x1a00, 0xa495, 0x0000 },
+  { 0x1a00, 0xa497, 0x0000 },
+  { 0x9a00, 0xa49c, 0x3000 },
+  { 0x9a00, 0xa49a, 0x2000 },
+  { 0x1a00, 0xa499, 0x0000 },
+  { 0x1a00, 0xa49b, 0x0000 },
+  { 0x9a00, 0xa49e, 0x2000 },
+  { 0x1a00, 0xa49d, 0x0000 },
+  { 0x1a00, 0xa49f, 0x0000 },
+  { 0x9a00, 0xa4a8, 0x4000 },
+  { 0x9a00, 0xa4a4, 0x3000 },
+  { 0x9a00, 0xa4a2, 0x2000 },
+  { 0x1a00, 0xa4a1, 0x0000 },
+  { 0x1a00, 0xa4a3, 0x0000 },
+  { 0x9a00, 0xa4a6, 0x2000 },
+  { 0x1a00, 0xa4a5, 0x0000 },
+  { 0x1a00, 0xa4a7, 0x0000 },
+  { 0x9a00, 0xa4ac, 0x3000 },
+  { 0x9a00, 0xa4aa, 0x2000 },
+  { 0x1a00, 0xa4a9, 0x0000 },
+  { 0x1a00, 0xa4ab, 0x0000 },
+  { 0x9a00, 0xa4ae, 0x2000 },
+  { 0x1a00, 0xa4ad, 0x0000 },
+  { 0x1a00, 0xa4af, 0x0000 },
+  { 0x9a00, 0xa4c0, 0x5000 },
+  { 0x9a00, 0xa4b8, 0x4000 },
+  { 0x9a00, 0xa4b4, 0x3000 },
+  { 0x9a00, 0xa4b2, 0x2000 },
+  { 0x1a00, 0xa4b1, 0x0000 },
+  { 0x1a00, 0xa4b3, 0x0000 },
+  { 0x9a00, 0xa4b6, 0x2000 },
+  { 0x1a00, 0xa4b5, 0x0000 },
+  { 0x1a00, 0xa4b7, 0x0000 },
+  { 0x9a00, 0xa4bc, 0x3000 },
+  { 0x9a00, 0xa4ba, 0x2000 },
+  { 0x1a00, 0xa4b9, 0x0000 },
+  { 0x1a00, 0xa4bb, 0x0000 },
+  { 0x9a00, 0xa4be, 0x2000 },
+  { 0x1a00, 0xa4bd, 0x0000 },
+  { 0x1a00, 0xa4bf, 0x0000 },
+  { 0x8700, 0xd7a3, 0x4000 },
+  { 0x9a00, 0xa4c4, 0x3000 },
+  { 0x9a00, 0xa4c2, 0x2000 },
+  { 0x1a00, 0xa4c1, 0x0000 },
+  { 0x1a00, 0xa4c3, 0x0000 },
+  { 0x9a00, 0xa4c6, 0x2000 },
+  { 0x1a00, 0xa4c5, 0x0000 },
+  { 0x0700, 0xac00, 0x0000 },
+  { 0x8400, 0xdbff, 0x3000 },
+  { 0x8400, 0xdb7f, 0x2000 },
+  { 0x0400, 0xd800, 0x0000 },
+  { 0x0400, 0xdb80, 0x0000 },
+  { 0x8400, 0xdfff, 0x2000 },
+  { 0x0400, 0xdc00, 0x0000 },
+  { 0x0300, 0xe000, 0x0000 },
+  { 0x8700, 0xf93f, 0x7000 },
+  { 0x8700, 0xf91f, 0x6000 },
+  { 0x8700, 0xf90f, 0x5000 },
+  { 0x8700, 0xf907, 0x4000 },
+  { 0x8700, 0xf903, 0x3000 },
+  { 0x8700, 0xf901, 0x2000 },
+  { 0x0700, 0xf900, 0x0000 },
+  { 0x0700, 0xf902, 0x0000 },
+  { 0x8700, 0xf905, 0x2000 },
+  { 0x0700, 0xf904, 0x0000 },
+  { 0x0700, 0xf906, 0x0000 },
+  { 0x8700, 0xf90b, 0x3000 },
+  { 0x8700, 0xf909, 0x2000 },
+  { 0x0700, 0xf908, 0x0000 },
+  { 0x0700, 0xf90a, 0x0000 },
+  { 0x8700, 0xf90d, 0x2000 },
+  { 0x0700, 0xf90c, 0x0000 },
+  { 0x0700, 0xf90e, 0x0000 },
+  { 0x8700, 0xf917, 0x4000 },
+  { 0x8700, 0xf913, 0x3000 },
+  { 0x8700, 0xf911, 0x2000 },
+  { 0x0700, 0xf910, 0x0000 },
+  { 0x0700, 0xf912, 0x0000 },
+  { 0x8700, 0xf915, 0x2000 },
+  { 0x0700, 0xf914, 0x0000 },
+  { 0x0700, 0xf916, 0x0000 },
+  { 0x8700, 0xf91b, 0x3000 },
+  { 0x8700, 0xf919, 0x2000 },
+  { 0x0700, 0xf918, 0x0000 },
+  { 0x0700, 0xf91a, 0x0000 },
+  { 0x8700, 0xf91d, 0x2000 },
+  { 0x0700, 0xf91c, 0x0000 },
+  { 0x0700, 0xf91e, 0x0000 },
+  { 0x8700, 0xf92f, 0x5000 },
+  { 0x8700, 0xf927, 0x4000 },
+  { 0x8700, 0xf923, 0x3000 },
+  { 0x8700, 0xf921, 0x2000 },
+  { 0x0700, 0xf920, 0x0000 },
+  { 0x0700, 0xf922, 0x0000 },
+  { 0x8700, 0xf925, 0x2000 },
+  { 0x0700, 0xf924, 0x0000 },
+  { 0x0700, 0xf926, 0x0000 },
+  { 0x8700, 0xf92b, 0x3000 },
+  { 0x8700, 0xf929, 0x2000 },
+  { 0x0700, 0xf928, 0x0000 },
+  { 0x0700, 0xf92a, 0x0000 },
+  { 0x8700, 0xf92d, 0x2000 },
+  { 0x0700, 0xf92c, 0x0000 },
+  { 0x0700, 0xf92e, 0x0000 },
+  { 0x8700, 0xf937, 0x4000 },
+  { 0x8700, 0xf933, 0x3000 },
+  { 0x8700, 0xf931, 0x2000 },
+  { 0x0700, 0xf930, 0x0000 },
+  { 0x0700, 0xf932, 0x0000 },
+  { 0x8700, 0xf935, 0x2000 },
+  { 0x0700, 0xf934, 0x0000 },
+  { 0x0700, 0xf936, 0x0000 },
+  { 0x8700, 0xf93b, 0x3000 },
+  { 0x8700, 0xf939, 0x2000 },
+  { 0x0700, 0xf938, 0x0000 },
+  { 0x0700, 0xf93a, 0x0000 },
+  { 0x8700, 0xf93d, 0x2000 },
+  { 0x0700, 0xf93c, 0x0000 },
+  { 0x0700, 0xf93e, 0x0000 },
+  { 0x8700, 0xf95f, 0x6000 },
+  { 0x8700, 0xf94f, 0x5000 },
+  { 0x8700, 0xf947, 0x4000 },
+  { 0x8700, 0xf943, 0x3000 },
+  { 0x8700, 0xf941, 0x2000 },
+  { 0x0700, 0xf940, 0x0000 },
+  { 0x0700, 0xf942, 0x0000 },
+  { 0x8700, 0xf945, 0x2000 },
+  { 0x0700, 0xf944, 0x0000 },
+  { 0x0700, 0xf946, 0x0000 },
+  { 0x8700, 0xf94b, 0x3000 },
+  { 0x8700, 0xf949, 0x2000 },
+  { 0x0700, 0xf948, 0x0000 },
+  { 0x0700, 0xf94a, 0x0000 },
+  { 0x8700, 0xf94d, 0x2000 },
+  { 0x0700, 0xf94c, 0x0000 },
+  { 0x0700, 0xf94e, 0x0000 },
+  { 0x8700, 0xf957, 0x4000 },
+  { 0x8700, 0xf953, 0x3000 },
+  { 0x8700, 0xf951, 0x2000 },
+  { 0x0700, 0xf950, 0x0000 },
+  { 0x0700, 0xf952, 0x0000 },
+  { 0x8700, 0xf955, 0x2000 },
+  { 0x0700, 0xf954, 0x0000 },
+  { 0x0700, 0xf956, 0x0000 },
+  { 0x8700, 0xf95b, 0x3000 },
+  { 0x8700, 0xf959, 0x2000 },
+  { 0x0700, 0xf958, 0x0000 },
+  { 0x0700, 0xf95a, 0x0000 },
+  { 0x8700, 0xf95d, 0x2000 },
+  { 0x0700, 0xf95c, 0x0000 },
+  { 0x0700, 0xf95e, 0x0000 },
+  { 0x8700, 0xf96f, 0x5000 },
+  { 0x8700, 0xf967, 0x4000 },
+  { 0x8700, 0xf963, 0x3000 },
+  { 0x8700, 0xf961, 0x2000 },
+  { 0x0700, 0xf960, 0x0000 },
+  { 0x0700, 0xf962, 0x0000 },
+  { 0x8700, 0xf965, 0x2000 },
+  { 0x0700, 0xf964, 0x0000 },
+  { 0x0700, 0xf966, 0x0000 },
+  { 0x8700, 0xf96b, 0x3000 },
+  { 0x8700, 0xf969, 0x2000 },
+  { 0x0700, 0xf968, 0x0000 },
+  { 0x0700, 0xf96a, 0x0000 },
+  { 0x8700, 0xf96d, 0x2000 },
+  { 0x0700, 0xf96c, 0x0000 },
+  { 0x0700, 0xf96e, 0x0000 },
+  { 0x8700, 0xf977, 0x4000 },
+  { 0x8700, 0xf973, 0x3000 },
+  { 0x8700, 0xf971, 0x2000 },
+  { 0x0700, 0xf970, 0x0000 },
+  { 0x0700, 0xf972, 0x0000 },
+  { 0x8700, 0xf975, 0x2000 },
+  { 0x0700, 0xf974, 0x0000 },
+  { 0x0700, 0xf976, 0x0000 },
+  { 0x8700, 0xf97b, 0x3000 },
+  { 0x8700, 0xf979, 0x2000 },
+  { 0x0700, 0xf978, 0x0000 },
+  { 0x0700, 0xf97a, 0x0000 },
+  { 0x8700, 0xf97d, 0x2000 },
+  { 0x0700, 0xf97c, 0x0000 },
+  { 0x0700, 0xf97e, 0x0000 },
+  { 0x8700, 0xfb27, 0x9000 },
+  { 0x8700, 0xf9ff, 0x8000 },
+  { 0x8700, 0xf9bf, 0x7000 },
+  { 0x8700, 0xf99f, 0x6000 },
+  { 0x8700, 0xf98f, 0x5000 },
+  { 0x8700, 0xf987, 0x4000 },
+  { 0x8700, 0xf983, 0x3000 },
+  { 0x8700, 0xf981, 0x2000 },
+  { 0x0700, 0xf980, 0x0000 },
+  { 0x0700, 0xf982, 0x0000 },
+  { 0x8700, 0xf985, 0x2000 },
+  { 0x0700, 0xf984, 0x0000 },
+  { 0x0700, 0xf986, 0x0000 },
+  { 0x8700, 0xf98b, 0x3000 },
+  { 0x8700, 0xf989, 0x2000 },
+  { 0x0700, 0xf988, 0x0000 },
+  { 0x0700, 0xf98a, 0x0000 },
+  { 0x8700, 0xf98d, 0x2000 },
+  { 0x0700, 0xf98c, 0x0000 },
+  { 0x0700, 0xf98e, 0x0000 },
+  { 0x8700, 0xf997, 0x4000 },
+  { 0x8700, 0xf993, 0x3000 },
+  { 0x8700, 0xf991, 0x2000 },
+  { 0x0700, 0xf990, 0x0000 },
+  { 0x0700, 0xf992, 0x0000 },
+  { 0x8700, 0xf995, 0x2000 },
+  { 0x0700, 0xf994, 0x0000 },
+  { 0x0700, 0xf996, 0x0000 },
+  { 0x8700, 0xf99b, 0x3000 },
+  { 0x8700, 0xf999, 0x2000 },
+  { 0x0700, 0xf998, 0x0000 },
+  { 0x0700, 0xf99a, 0x0000 },
+  { 0x8700, 0xf99d, 0x2000 },
+  { 0x0700, 0xf99c, 0x0000 },
+  { 0x0700, 0xf99e, 0x0000 },
+  { 0x8700, 0xf9af, 0x5000 },
+  { 0x8700, 0xf9a7, 0x4000 },
+  { 0x8700, 0xf9a3, 0x3000 },
+  { 0x8700, 0xf9a1, 0x2000 },
+  { 0x0700, 0xf9a0, 0x0000 },
+  { 0x0700, 0xf9a2, 0x0000 },
+  { 0x8700, 0xf9a5, 0x2000 },
+  { 0x0700, 0xf9a4, 0x0000 },
+  { 0x0700, 0xf9a6, 0x0000 },
+  { 0x8700, 0xf9ab, 0x3000 },
+  { 0x8700, 0xf9a9, 0x2000 },
+  { 0x0700, 0xf9a8, 0x0000 },
+  { 0x0700, 0xf9aa, 0x0000 },
+  { 0x8700, 0xf9ad, 0x2000 },
+  { 0x0700, 0xf9ac, 0x0000 },
+  { 0x0700, 0xf9ae, 0x0000 },
+  { 0x8700, 0xf9b7, 0x4000 },
+  { 0x8700, 0xf9b3, 0x3000 },
+  { 0x8700, 0xf9b1, 0x2000 },
+  { 0x0700, 0xf9b0, 0x0000 },
+  { 0x0700, 0xf9b2, 0x0000 },
+  { 0x8700, 0xf9b5, 0x2000 },
+  { 0x0700, 0xf9b4, 0x0000 },
+  { 0x0700, 0xf9b6, 0x0000 },
+  { 0x8700, 0xf9bb, 0x3000 },
+  { 0x8700, 0xf9b9, 0x2000 },
+  { 0x0700, 0xf9b8, 0x0000 },
+  { 0x0700, 0xf9ba, 0x0000 },
+  { 0x8700, 0xf9bd, 0x2000 },
+  { 0x0700, 0xf9bc, 0x0000 },
+  { 0x0700, 0xf9be, 0x0000 },
+  { 0x8700, 0xf9df, 0x6000 },
+  { 0x8700, 0xf9cf, 0x5000 },
+  { 0x8700, 0xf9c7, 0x4000 },
+  { 0x8700, 0xf9c3, 0x3000 },
+  { 0x8700, 0xf9c1, 0x2000 },
+  { 0x0700, 0xf9c0, 0x0000 },
+  { 0x0700, 0xf9c2, 0x0000 },
+  { 0x8700, 0xf9c5, 0x2000 },
+  { 0x0700, 0xf9c4, 0x0000 },
+  { 0x0700, 0xf9c6, 0x0000 },
+  { 0x8700, 0xf9cb, 0x3000 },
+  { 0x8700, 0xf9c9, 0x2000 },
+  { 0x0700, 0xf9c8, 0x0000 },
+  { 0x0700, 0xf9ca, 0x0000 },
+  { 0x8700, 0xf9cd, 0x2000 },
+  { 0x0700, 0xf9cc, 0x0000 },
+  { 0x0700, 0xf9ce, 0x0000 },
+  { 0x8700, 0xf9d7, 0x4000 },
+  { 0x8700, 0xf9d3, 0x3000 },
+  { 0x8700, 0xf9d1, 0x2000 },
+  { 0x0700, 0xf9d0, 0x0000 },
+  { 0x0700, 0xf9d2, 0x0000 },
+  { 0x8700, 0xf9d5, 0x2000 },
+  { 0x0700, 0xf9d4, 0x0000 },
+  { 0x0700, 0xf9d6, 0x0000 },
+  { 0x8700, 0xf9db, 0x3000 },
+  { 0x8700, 0xf9d9, 0x2000 },
+  { 0x0700, 0xf9d8, 0x0000 },
+  { 0x0700, 0xf9da, 0x0000 },
+  { 0x8700, 0xf9dd, 0x2000 },
+  { 0x0700, 0xf9dc, 0x0000 },
+  { 0x0700, 0xf9de, 0x0000 },
+  { 0x8700, 0xf9ef, 0x5000 },
+  { 0x8700, 0xf9e7, 0x4000 },
+  { 0x8700, 0xf9e3, 0x3000 },
+  { 0x8700, 0xf9e1, 0x2000 },
+  { 0x0700, 0xf9e0, 0x0000 },
+  { 0x0700, 0xf9e2, 0x0000 },
+  { 0x8700, 0xf9e5, 0x2000 },
+  { 0x0700, 0xf9e4, 0x0000 },
+  { 0x0700, 0xf9e6, 0x0000 },
+  { 0x8700, 0xf9eb, 0x3000 },
+  { 0x8700, 0xf9e9, 0x2000 },
+  { 0x0700, 0xf9e8, 0x0000 },
+  { 0x0700, 0xf9ea, 0x0000 },
+  { 0x8700, 0xf9ed, 0x2000 },
+  { 0x0700, 0xf9ec, 0x0000 },
+  { 0x0700, 0xf9ee, 0x0000 },
+  { 0x8700, 0xf9f7, 0x4000 },
+  { 0x8700, 0xf9f3, 0x3000 },
+  { 0x8700, 0xf9f1, 0x2000 },
+  { 0x0700, 0xf9f0, 0x0000 },
+  { 0x0700, 0xf9f2, 0x0000 },
+  { 0x8700, 0xf9f5, 0x2000 },
+  { 0x0700, 0xf9f4, 0x0000 },
+  { 0x0700, 0xf9f6, 0x0000 },
+  { 0x8700, 0xf9fb, 0x3000 },
+  { 0x8700, 0xf9f9, 0x2000 },
+  { 0x0700, 0xf9f8, 0x0000 },
+  { 0x0700, 0xf9fa, 0x0000 },
+  { 0x8700, 0xf9fd, 0x2000 },
+  { 0x0700, 0xf9fc, 0x0000 },
+  { 0x0700, 0xf9fe, 0x0000 },
+  { 0x8700, 0xfa41, 0x7000 },
+  { 0x8700, 0xfa1f, 0x6000 },
+  { 0x8700, 0xfa0f, 0x5000 },
+  { 0x8700, 0xfa07, 0x4000 },
+  { 0x8700, 0xfa03, 0x3000 },
+  { 0x8700, 0xfa01, 0x2000 },
+  { 0x0700, 0xfa00, 0x0000 },
+  { 0x0700, 0xfa02, 0x0000 },
+  { 0x8700, 0xfa05, 0x2000 },
+  { 0x0700, 0xfa04, 0x0000 },
+  { 0x0700, 0xfa06, 0x0000 },
+  { 0x8700, 0xfa0b, 0x3000 },
+  { 0x8700, 0xfa09, 0x2000 },
+  { 0x0700, 0xfa08, 0x0000 },
+  { 0x0700, 0xfa0a, 0x0000 },
+  { 0x8700, 0xfa0d, 0x2000 },
+  { 0x0700, 0xfa0c, 0x0000 },
+  { 0x0700, 0xfa0e, 0x0000 },
+  { 0x8700, 0xfa17, 0x4000 },
+  { 0x8700, 0xfa13, 0x3000 },
+  { 0x8700, 0xfa11, 0x2000 },
+  { 0x0700, 0xfa10, 0x0000 },
+  { 0x0700, 0xfa12, 0x0000 },
+  { 0x8700, 0xfa15, 0x2000 },
+  { 0x0700, 0xfa14, 0x0000 },
+  { 0x0700, 0xfa16, 0x0000 },
+  { 0x8700, 0xfa1b, 0x3000 },
+  { 0x8700, 0xfa19, 0x2000 },
+  { 0x0700, 0xfa18, 0x0000 },
+  { 0x0700, 0xfa1a, 0x0000 },
+  { 0x8700, 0xfa1d, 0x2000 },
+  { 0x0700, 0xfa1c, 0x0000 },
+  { 0x0700, 0xfa1e, 0x0000 },
+  { 0x8700, 0xfa31, 0x5000 },
+  { 0x8700, 0xfa27, 0x4000 },
+  { 0x8700, 0xfa23, 0x3000 },
+  { 0x8700, 0xfa21, 0x2000 },
+  { 0x0700, 0xfa20, 0x0000 },
+  { 0x0700, 0xfa22, 0x0000 },
+  { 0x8700, 0xfa25, 0x2000 },
+  { 0x0700, 0xfa24, 0x0000 },
+  { 0x0700, 0xfa26, 0x0000 },
+  { 0x8700, 0xfa2b, 0x3000 },
+  { 0x8700, 0xfa29, 0x2000 },
+  { 0x0700, 0xfa28, 0x0000 },
+  { 0x0700, 0xfa2a, 0x0000 },
+  { 0x8700, 0xfa2d, 0x2000 },
+  { 0x0700, 0xfa2c, 0x0000 },
+  { 0x0700, 0xfa30, 0x0000 },
+  { 0x8700, 0xfa39, 0x4000 },
+  { 0x8700, 0xfa35, 0x3000 },
+  { 0x8700, 0xfa33, 0x2000 },
+  { 0x0700, 0xfa32, 0x0000 },
+  { 0x0700, 0xfa34, 0x0000 },
+  { 0x8700, 0xfa37, 0x2000 },
+  { 0x0700, 0xfa36, 0x0000 },
+  { 0x0700, 0xfa38, 0x0000 },
+  { 0x8700, 0xfa3d, 0x3000 },
+  { 0x8700, 0xfa3b, 0x2000 },
+  { 0x0700, 0xfa3a, 0x0000 },
+  { 0x0700, 0xfa3c, 0x0000 },
+  { 0x8700, 0xfa3f, 0x2000 },
+  { 0x0700, 0xfa3e, 0x0000 },
+  { 0x0700, 0xfa40, 0x0000 },
+  { 0x8700, 0xfa61, 0x6000 },
+  { 0x8700, 0xfa51, 0x5000 },
+  { 0x8700, 0xfa49, 0x4000 },
+  { 0x8700, 0xfa45, 0x3000 },
+  { 0x8700, 0xfa43, 0x2000 },
+  { 0x0700, 0xfa42, 0x0000 },
+  { 0x0700, 0xfa44, 0x0000 },
+  { 0x8700, 0xfa47, 0x2000 },
+  { 0x0700, 0xfa46, 0x0000 },
+  { 0x0700, 0xfa48, 0x0000 },
+  { 0x8700, 0xfa4d, 0x3000 },
+  { 0x8700, 0xfa4b, 0x2000 },
+  { 0x0700, 0xfa4a, 0x0000 },
+  { 0x0700, 0xfa4c, 0x0000 },
+  { 0x8700, 0xfa4f, 0x2000 },
+  { 0x0700, 0xfa4e, 0x0000 },
+  { 0x0700, 0xfa50, 0x0000 },
+  { 0x8700, 0xfa59, 0x4000 },
+  { 0x8700, 0xfa55, 0x3000 },
+  { 0x8700, 0xfa53, 0x2000 },
+  { 0x0700, 0xfa52, 0x0000 },
+  { 0x0700, 0xfa54, 0x0000 },
+  { 0x8700, 0xfa57, 0x2000 },
+  { 0x0700, 0xfa56, 0x0000 },
+  { 0x0700, 0xfa58, 0x0000 },
+  { 0x8700, 0xfa5d, 0x3000 },
+  { 0x8700, 0xfa5b, 0x2000 },
+  { 0x0700, 0xfa5a, 0x0000 },
+  { 0x0700, 0xfa5c, 0x0000 },
+  { 0x8700, 0xfa5f, 0x2000 },
+  { 0x0700, 0xfa5e, 0x0000 },
+  { 0x0700, 0xfa60, 0x0000 },
+  { 0x8500, 0xfb06, 0x5000 },
+  { 0x8700, 0xfa69, 0x4000 },
+  { 0x8700, 0xfa65, 0x3000 },
+  { 0x8700, 0xfa63, 0x2000 },
+  { 0x0700, 0xfa62, 0x0000 },
+  { 0x0700, 0xfa64, 0x0000 },
+  { 0x8700, 0xfa67, 0x2000 },
+  { 0x0700, 0xfa66, 0x0000 },
+  { 0x0700, 0xfa68, 0x0000 },
+  { 0x8500, 0xfb02, 0x3000 },
+  { 0x8500, 0xfb00, 0x2000 },
+  { 0x0700, 0xfa6a, 0x0000 },
+  { 0x0500, 0xfb01, 0x0000 },
+  { 0x8500, 0xfb04, 0x2000 },
+  { 0x0500, 0xfb03, 0x0000 },
+  { 0x0500, 0xfb05, 0x0000 },
+  { 0x8700, 0xfb1f, 0x4000 },
+  { 0x8500, 0xfb16, 0x3000 },
+  { 0x8500, 0xfb14, 0x2000 },
+  { 0x0500, 0xfb13, 0x0000 },
+  { 0x0500, 0xfb15, 0x0000 },
+  { 0x8700, 0xfb1d, 0x2000 },
+  { 0x0500, 0xfb17, 0x0000 },
+  { 0x0c00, 0xfb1e, 0x0000 },
+  { 0x8700, 0xfb23, 0x3000 },
+  { 0x8700, 0xfb21, 0x2000 },
+  { 0x0700, 0xfb20, 0x0000 },
+  { 0x0700, 0xfb22, 0x0000 },
+  { 0x8700, 0xfb25, 0x2000 },
+  { 0x0700, 0xfb24, 0x0000 },
+  { 0x0700, 0xfb26, 0x0000 },
+  { 0x8700, 0xfbac, 0x8000 },
+  { 0x8700, 0xfb6c, 0x7000 },
+  { 0x8700, 0xfb4c, 0x6000 },
+  { 0x8700, 0xfb38, 0x5000 },
+  { 0x8700, 0xfb2f, 0x4000 },
+  { 0x8700, 0xfb2b, 0x3000 },
+  { 0x9900, 0xfb29, 0x2000 },
+  { 0x0700, 0xfb28, 0x0000 },
+  { 0x0700, 0xfb2a, 0x0000 },
+  { 0x8700, 0xfb2d, 0x2000 },
+  { 0x0700, 0xfb2c, 0x0000 },
+  { 0x0700, 0xfb2e, 0x0000 },
+  { 0x8700, 0xfb33, 0x3000 },
+  { 0x8700, 0xfb31, 0x2000 },
+  { 0x0700, 0xfb30, 0x0000 },
+  { 0x0700, 0xfb32, 0x0000 },
+  { 0x8700, 0xfb35, 0x2000 },
+  { 0x0700, 0xfb34, 0x0000 },
+  { 0x0700, 0xfb36, 0x0000 },
+  { 0x8700, 0xfb43, 0x4000 },
+  { 0x8700, 0xfb3c, 0x3000 },
+  { 0x8700, 0xfb3a, 0x2000 },
+  { 0x0700, 0xfb39, 0x0000 },
+  { 0x0700, 0xfb3b, 0x0000 },
+  { 0x8700, 0xfb40, 0x2000 },
+  { 0x0700, 0xfb3e, 0x0000 },
+  { 0x0700, 0xfb41, 0x0000 },
+  { 0x8700, 0xfb48, 0x3000 },
+  { 0x8700, 0xfb46, 0x2000 },
+  { 0x0700, 0xfb44, 0x0000 },
+  { 0x0700, 0xfb47, 0x0000 },
+  { 0x8700, 0xfb4a, 0x2000 },
+  { 0x0700, 0xfb49, 0x0000 },
+  { 0x0700, 0xfb4b, 0x0000 },
+  { 0x8700, 0xfb5c, 0x5000 },
+  { 0x8700, 0xfb54, 0x4000 },
+  { 0x8700, 0xfb50, 0x3000 },
+  { 0x8700, 0xfb4e, 0x2000 },
+  { 0x0700, 0xfb4d, 0x0000 },
+  { 0x0700, 0xfb4f, 0x0000 },
+  { 0x8700, 0xfb52, 0x2000 },
+  { 0x0700, 0xfb51, 0x0000 },
+  { 0x0700, 0xfb53, 0x0000 },
+  { 0x8700, 0xfb58, 0x3000 },
+  { 0x8700, 0xfb56, 0x2000 },
+  { 0x0700, 0xfb55, 0x0000 },
+  { 0x0700, 0xfb57, 0x0000 },
+  { 0x8700, 0xfb5a, 0x2000 },
+  { 0x0700, 0xfb59, 0x0000 },
+  { 0x0700, 0xfb5b, 0x0000 },
+  { 0x8700, 0xfb64, 0x4000 },
+  { 0x8700, 0xfb60, 0x3000 },
+  { 0x8700, 0xfb5e, 0x2000 },
+  { 0x0700, 0xfb5d, 0x0000 },
+  { 0x0700, 0xfb5f, 0x0000 },
+  { 0x8700, 0xfb62, 0x2000 },
+  { 0x0700, 0xfb61, 0x0000 },
+  { 0x0700, 0xfb63, 0x0000 },
+  { 0x8700, 0xfb68, 0x3000 },
+  { 0x8700, 0xfb66, 0x2000 },
+  { 0x0700, 0xfb65, 0x0000 },
+  { 0x0700, 0xfb67, 0x0000 },
+  { 0x8700, 0xfb6a, 0x2000 },
+  { 0x0700, 0xfb69, 0x0000 },
+  { 0x0700, 0xfb6b, 0x0000 },
+  { 0x8700, 0xfb8c, 0x6000 },
+  { 0x8700, 0xfb7c, 0x5000 },
+  { 0x8700, 0xfb74, 0x4000 },
+  { 0x8700, 0xfb70, 0x3000 },
+  { 0x8700, 0xfb6e, 0x2000 },
+  { 0x0700, 0xfb6d, 0x0000 },
+  { 0x0700, 0xfb6f, 0x0000 },
+  { 0x8700, 0xfb72, 0x2000 },
+  { 0x0700, 0xfb71, 0x0000 },
+  { 0x0700, 0xfb73, 0x0000 },
+  { 0x8700, 0xfb78, 0x3000 },
+  { 0x8700, 0xfb76, 0x2000 },
+  { 0x0700, 0xfb75, 0x0000 },
+  { 0x0700, 0xfb77, 0x0000 },
+  { 0x8700, 0xfb7a, 0x2000 },
+  { 0x0700, 0xfb79, 0x0000 },
+  { 0x0700, 0xfb7b, 0x0000 },
+  { 0x8700, 0xfb84, 0x4000 },
+  { 0x8700, 0xfb80, 0x3000 },
+  { 0x8700, 0xfb7e, 0x2000 },
+  { 0x0700, 0xfb7d, 0x0000 },
+  { 0x0700, 0xfb7f, 0x0000 },
+  { 0x8700, 0xfb82, 0x2000 },
+  { 0x0700, 0xfb81, 0x0000 },
+  { 0x0700, 0xfb83, 0x0000 },
+  { 0x8700, 0xfb88, 0x3000 },
+  { 0x8700, 0xfb86, 0x2000 },
+  { 0x0700, 0xfb85, 0x0000 },
+  { 0x0700, 0xfb87, 0x0000 },
+  { 0x8700, 0xfb8a, 0x2000 },
+  { 0x0700, 0xfb89, 0x0000 },
+  { 0x0700, 0xfb8b, 0x0000 },
+  { 0x8700, 0xfb9c, 0x5000 },
+  { 0x8700, 0xfb94, 0x4000 },
+  { 0x8700, 0xfb90, 0x3000 },
+  { 0x8700, 0xfb8e, 0x2000 },
+  { 0x0700, 0xfb8d, 0x0000 },
+  { 0x0700, 0xfb8f, 0x0000 },
+  { 0x8700, 0xfb92, 0x2000 },
+  { 0x0700, 0xfb91, 0x0000 },
+  { 0x0700, 0xfb93, 0x0000 },
+  { 0x8700, 0xfb98, 0x3000 },
+  { 0x8700, 0xfb96, 0x2000 },
+  { 0x0700, 0xfb95, 0x0000 },
+  { 0x0700, 0xfb97, 0x0000 },
+  { 0x8700, 0xfb9a, 0x2000 },
+  { 0x0700, 0xfb99, 0x0000 },
+  { 0x0700, 0xfb9b, 0x0000 },
+  { 0x8700, 0xfba4, 0x4000 },
+  { 0x8700, 0xfba0, 0x3000 },
+  { 0x8700, 0xfb9e, 0x2000 },
+  { 0x0700, 0xfb9d, 0x0000 },
+  { 0x0700, 0xfb9f, 0x0000 },
+  { 0x8700, 0xfba2, 0x2000 },
+  { 0x0700, 0xfba1, 0x0000 },
+  { 0x0700, 0xfba3, 0x0000 },
+  { 0x8700, 0xfba8, 0x3000 },
+  { 0x8700, 0xfba6, 0x2000 },
+  { 0x0700, 0xfba5, 0x0000 },
+  { 0x0700, 0xfba7, 0x0000 },
+  { 0x8700, 0xfbaa, 0x2000 },
+  { 0x0700, 0xfba9, 0x0000 },
+  { 0x0700, 0xfbab, 0x0000 },
+  { 0x8700, 0xfc0d, 0x7000 },
+  { 0x8700, 0xfbed, 0x6000 },
+  { 0x8700, 0xfbdd, 0x5000 },
+  { 0x8700, 0xfbd5, 0x4000 },
+  { 0x8700, 0xfbb0, 0x3000 },
+  { 0x8700, 0xfbae, 0x2000 },
+  { 0x0700, 0xfbad, 0x0000 },
+  { 0x0700, 0xfbaf, 0x0000 },
+  { 0x8700, 0xfbd3, 0x2000 },
+  { 0x0700, 0xfbb1, 0x0000 },
+  { 0x0700, 0xfbd4, 0x0000 },
+  { 0x8700, 0xfbd9, 0x3000 },
+  { 0x8700, 0xfbd7, 0x2000 },
+  { 0x0700, 0xfbd6, 0x0000 },
+  { 0x0700, 0xfbd8, 0x0000 },
+  { 0x8700, 0xfbdb, 0x2000 },
+  { 0x0700, 0xfbda, 0x0000 },
+  { 0x0700, 0xfbdc, 0x0000 },
+  { 0x8700, 0xfbe5, 0x4000 },
+  { 0x8700, 0xfbe1, 0x3000 },
+  { 0x8700, 0xfbdf, 0x2000 },
+  { 0x0700, 0xfbde, 0x0000 },
+  { 0x0700, 0xfbe0, 0x0000 },
+  { 0x8700, 0xfbe3, 0x2000 },
+  { 0x0700, 0xfbe2, 0x0000 },
+  { 0x0700, 0xfbe4, 0x0000 },
+  { 0x8700, 0xfbe9, 0x3000 },
+  { 0x8700, 0xfbe7, 0x2000 },
+  { 0x0700, 0xfbe6, 0x0000 },
+  { 0x0700, 0xfbe8, 0x0000 },
+  { 0x8700, 0xfbeb, 0x2000 },
+  { 0x0700, 0xfbea, 0x0000 },
+  { 0x0700, 0xfbec, 0x0000 },
+  { 0x8700, 0xfbfd, 0x5000 },
+  { 0x8700, 0xfbf5, 0x4000 },
+  { 0x8700, 0xfbf1, 0x3000 },
+  { 0x8700, 0xfbef, 0x2000 },
+  { 0x0700, 0xfbee, 0x0000 },
+  { 0x0700, 0xfbf0, 0x0000 },
+  { 0x8700, 0xfbf3, 0x2000 },
+  { 0x0700, 0xfbf2, 0x0000 },
+  { 0x0700, 0xfbf4, 0x0000 },
+  { 0x8700, 0xfbf9, 0x3000 },
+  { 0x8700, 0xfbf7, 0x2000 },
+  { 0x0700, 0xfbf6, 0x0000 },
+  { 0x0700, 0xfbf8, 0x0000 },
+  { 0x8700, 0xfbfb, 0x2000 },
+  { 0x0700, 0xfbfa, 0x0000 },
+  { 0x0700, 0xfbfc, 0x0000 },
+  { 0x8700, 0xfc05, 0x4000 },
+  { 0x8700, 0xfc01, 0x3000 },
+  { 0x8700, 0xfbff, 0x2000 },
+  { 0x0700, 0xfbfe, 0x0000 },
+  { 0x0700, 0xfc00, 0x0000 },
+  { 0x8700, 0xfc03, 0x2000 },
+  { 0x0700, 0xfc02, 0x0000 },
+  { 0x0700, 0xfc04, 0x0000 },
+  { 0x8700, 0xfc09, 0x3000 },
+  { 0x8700, 0xfc07, 0x2000 },
+  { 0x0700, 0xfc06, 0x0000 },
+  { 0x0700, 0xfc08, 0x0000 },
+  { 0x8700, 0xfc0b, 0x2000 },
+  { 0x0700, 0xfc0a, 0x0000 },
+  { 0x0700, 0xfc0c, 0x0000 },
+  { 0x8700, 0xfc2d, 0x6000 },
+  { 0x8700, 0xfc1d, 0x5000 },
+  { 0x8700, 0xfc15, 0x4000 },
+  { 0x8700, 0xfc11, 0x3000 },
+  { 0x8700, 0xfc0f, 0x2000 },
+  { 0x0700, 0xfc0e, 0x0000 },
+  { 0x0700, 0xfc10, 0x0000 },
+  { 0x8700, 0xfc13, 0x2000 },
+  { 0x0700, 0xfc12, 0x0000 },
+  { 0x0700, 0xfc14, 0x0000 },
+  { 0x8700, 0xfc19, 0x3000 },
+  { 0x8700, 0xfc17, 0x2000 },
+  { 0x0700, 0xfc16, 0x0000 },
+  { 0x0700, 0xfc18, 0x0000 },
+  { 0x8700, 0xfc1b, 0x2000 },
+  { 0x0700, 0xfc1a, 0x0000 },
+  { 0x0700, 0xfc1c, 0x0000 },
+  { 0x8700, 0xfc25, 0x4000 },
+  { 0x8700, 0xfc21, 0x3000 },
+  { 0x8700, 0xfc1f, 0x2000 },
+  { 0x0700, 0xfc1e, 0x0000 },
+  { 0x0700, 0xfc20, 0x0000 },
+  { 0x8700, 0xfc23, 0x2000 },
+  { 0x0700, 0xfc22, 0x0000 },
+  { 0x0700, 0xfc24, 0x0000 },
+  { 0x8700, 0xfc29, 0x3000 },
+  { 0x8700, 0xfc27, 0x2000 },
+  { 0x0700, 0xfc26, 0x0000 },
+  { 0x0700, 0xfc28, 0x0000 },
+  { 0x8700, 0xfc2b, 0x2000 },
+  { 0x0700, 0xfc2a, 0x0000 },
+  { 0x0700, 0xfc2c, 0x0000 },
+  { 0x8700, 0xfc3d, 0x5000 },
+  { 0x8700, 0xfc35, 0x4000 },
+  { 0x8700, 0xfc31, 0x3000 },
+  { 0x8700, 0xfc2f, 0x2000 },
+  { 0x0700, 0xfc2e, 0x0000 },
+  { 0x0700, 0xfc30, 0x0000 },
+  { 0x8700, 0xfc33, 0x2000 },
+  { 0x0700, 0xfc32, 0x0000 },
+  { 0x0700, 0xfc34, 0x0000 },
+  { 0x8700, 0xfc39, 0x3000 },
+  { 0x8700, 0xfc37, 0x2000 },
+  { 0x0700, 0xfc36, 0x0000 },
+  { 0x0700, 0xfc38, 0x0000 },
+  { 0x8700, 0xfc3b, 0x2000 },
+  { 0x0700, 0xfc3a, 0x0000 },
+  { 0x0700, 0xfc3c, 0x0000 },
+  { 0x8700, 0xfc45, 0x4000 },
+  { 0x8700, 0xfc41, 0x3000 },
+  { 0x8700, 0xfc3f, 0x2000 },
+  { 0x0700, 0xfc3e, 0x0000 },
+  { 0x0700, 0xfc40, 0x0000 },
+  { 0x8700, 0xfc43, 0x2000 },
+  { 0x0700, 0xfc42, 0x0000 },
+  { 0x0700, 0xfc44, 0x0000 },
+  { 0x8700, 0xfc49, 0x3000 },
+  { 0x8700, 0xfc47, 0x2000 },
+  { 0x0700, 0xfc46, 0x0000 },
+  { 0x0700, 0xfc48, 0x0000 },
+  { 0x8700, 0xfc4b, 0x2000 },
+  { 0x0700, 0xfc4a, 0x0000 },
+  { 0x0700, 0xfc4c, 0x0000 },
+  { 0x8700, 0xfeac, 0xa000 },
+  { 0x8700, 0xfd5d, 0x9000 },
+  { 0x8700, 0xfccd, 0x8000 },
+  { 0x8700, 0xfc8d, 0x7000 },
+  { 0x8700, 0xfc6d, 0x6000 },
+  { 0x8700, 0xfc5d, 0x5000 },
+  { 0x8700, 0xfc55, 0x4000 },
+  { 0x8700, 0xfc51, 0x3000 },
+  { 0x8700, 0xfc4f, 0x2000 },
+  { 0x0700, 0xfc4e, 0x0000 },
+  { 0x0700, 0xfc50, 0x0000 },
+  { 0x8700, 0xfc53, 0x2000 },
+  { 0x0700, 0xfc52, 0x0000 },
+  { 0x0700, 0xfc54, 0x0000 },
+  { 0x8700, 0xfc59, 0x3000 },
+  { 0x8700, 0xfc57, 0x2000 },
+  { 0x0700, 0xfc56, 0x0000 },
+  { 0x0700, 0xfc58, 0x0000 },
+  { 0x8700, 0xfc5b, 0x2000 },
+  { 0x0700, 0xfc5a, 0x0000 },
+  { 0x0700, 0xfc5c, 0x0000 },
+  { 0x8700, 0xfc65, 0x4000 },
+  { 0x8700, 0xfc61, 0x3000 },
+  { 0x8700, 0xfc5f, 0x2000 },
+  { 0x0700, 0xfc5e, 0x0000 },
+  { 0x0700, 0xfc60, 0x0000 },
+  { 0x8700, 0xfc63, 0x2000 },
+  { 0x0700, 0xfc62, 0x0000 },
+  { 0x0700, 0xfc64, 0x0000 },
+  { 0x8700, 0xfc69, 0x3000 },
+  { 0x8700, 0xfc67, 0x2000 },
+  { 0x0700, 0xfc66, 0x0000 },
+  { 0x0700, 0xfc68, 0x0000 },
+  { 0x8700, 0xfc6b, 0x2000 },
+  { 0x0700, 0xfc6a, 0x0000 },
+  { 0x0700, 0xfc6c, 0x0000 },
+  { 0x8700, 0xfc7d, 0x5000 },
+  { 0x8700, 0xfc75, 0x4000 },
+  { 0x8700, 0xfc71, 0x3000 },
+  { 0x8700, 0xfc6f, 0x2000 },
+  { 0x0700, 0xfc6e, 0x0000 },
+  { 0x0700, 0xfc70, 0x0000 },
+  { 0x8700, 0xfc73, 0x2000 },
+  { 0x0700, 0xfc72, 0x0000 },
+  { 0x0700, 0xfc74, 0x0000 },
+  { 0x8700, 0xfc79, 0x3000 },
+  { 0x8700, 0xfc77, 0x2000 },
+  { 0x0700, 0xfc76, 0x0000 },
+  { 0x0700, 0xfc78, 0x0000 },
+  { 0x8700, 0xfc7b, 0x2000 },
+  { 0x0700, 0xfc7a, 0x0000 },
+  { 0x0700, 0xfc7c, 0x0000 },
+  { 0x8700, 0xfc85, 0x4000 },
+  { 0x8700, 0xfc81, 0x3000 },
+  { 0x8700, 0xfc7f, 0x2000 },
+  { 0x0700, 0xfc7e, 0x0000 },
+  { 0x0700, 0xfc80, 0x0000 },
+  { 0x8700, 0xfc83, 0x2000 },
+  { 0x0700, 0xfc82, 0x0000 },
+  { 0x0700, 0xfc84, 0x0000 },
+  { 0x8700, 0xfc89, 0x3000 },
+  { 0x8700, 0xfc87, 0x2000 },
+  { 0x0700, 0xfc86, 0x0000 },
+  { 0x0700, 0xfc88, 0x0000 },
+  { 0x8700, 0xfc8b, 0x2000 },
+  { 0x0700, 0xfc8a, 0x0000 },
+  { 0x0700, 0xfc8c, 0x0000 },
+  { 0x8700, 0xfcad, 0x6000 },
+  { 0x8700, 0xfc9d, 0x5000 },
+  { 0x8700, 0xfc95, 0x4000 },
+  { 0x8700, 0xfc91, 0x3000 },
+  { 0x8700, 0xfc8f, 0x2000 },
+  { 0x0700, 0xfc8e, 0x0000 },
+  { 0x0700, 0xfc90, 0x0000 },
+  { 0x8700, 0xfc93, 0x2000 },
+  { 0x0700, 0xfc92, 0x0000 },
+  { 0x0700, 0xfc94, 0x0000 },
+  { 0x8700, 0xfc99, 0x3000 },
+  { 0x8700, 0xfc97, 0x2000 },
+  { 0x0700, 0xfc96, 0x0000 },
+  { 0x0700, 0xfc98, 0x0000 },
+  { 0x8700, 0xfc9b, 0x2000 },
+  { 0x0700, 0xfc9a, 0x0000 },
+  { 0x0700, 0xfc9c, 0x0000 },
+  { 0x8700, 0xfca5, 0x4000 },
+  { 0x8700, 0xfca1, 0x3000 },
+  { 0x8700, 0xfc9f, 0x2000 },
+  { 0x0700, 0xfc9e, 0x0000 },
+  { 0x0700, 0xfca0, 0x0000 },
+  { 0x8700, 0xfca3, 0x2000 },
+  { 0x0700, 0xfca2, 0x0000 },
+  { 0x0700, 0xfca4, 0x0000 },
+  { 0x8700, 0xfca9, 0x3000 },
+  { 0x8700, 0xfca7, 0x2000 },
+  { 0x0700, 0xfca6, 0x0000 },
+  { 0x0700, 0xfca8, 0x0000 },
+  { 0x8700, 0xfcab, 0x2000 },
+  { 0x0700, 0xfcaa, 0x0000 },
+  { 0x0700, 0xfcac, 0x0000 },
+  { 0x8700, 0xfcbd, 0x5000 },
+  { 0x8700, 0xfcb5, 0x4000 },
+  { 0x8700, 0xfcb1, 0x3000 },
+  { 0x8700, 0xfcaf, 0x2000 },
+  { 0x0700, 0xfcae, 0x0000 },
+  { 0x0700, 0xfcb0, 0x0000 },
+  { 0x8700, 0xfcb3, 0x2000 },
+  { 0x0700, 0xfcb2, 0x0000 },
+  { 0x0700, 0xfcb4, 0x0000 },
+  { 0x8700, 0xfcb9, 0x3000 },
+  { 0x8700, 0xfcb7, 0x2000 },
+  { 0x0700, 0xfcb6, 0x0000 },
+  { 0x0700, 0xfcb8, 0x0000 },
+  { 0x8700, 0xfcbb, 0x2000 },
+  { 0x0700, 0xfcba, 0x0000 },
+  { 0x0700, 0xfcbc, 0x0000 },
+  { 0x8700, 0xfcc5, 0x4000 },
+  { 0x8700, 0xfcc1, 0x3000 },
+  { 0x8700, 0xfcbf, 0x2000 },
+  { 0x0700, 0xfcbe, 0x0000 },
+  { 0x0700, 0xfcc0, 0x0000 },
+  { 0x8700, 0xfcc3, 0x2000 },
+  { 0x0700, 0xfcc2, 0x0000 },
+  { 0x0700, 0xfcc4, 0x0000 },
+  { 0x8700, 0xfcc9, 0x3000 },
+  { 0x8700, 0xfcc7, 0x2000 },
+  { 0x0700, 0xfcc6, 0x0000 },
+  { 0x0700, 0xfcc8, 0x0000 },
+  { 0x8700, 0xfccb, 0x2000 },
+  { 0x0700, 0xfcca, 0x0000 },
+  { 0x0700, 0xfccc, 0x0000 },
+  { 0x8700, 0xfd0d, 0x7000 },
+  { 0x8700, 0xfced, 0x6000 },
+  { 0x8700, 0xfcdd, 0x5000 },
+  { 0x8700, 0xfcd5, 0x4000 },
+  { 0x8700, 0xfcd1, 0x3000 },
+  { 0x8700, 0xfccf, 0x2000 },
+  { 0x0700, 0xfcce, 0x0000 },
+  { 0x0700, 0xfcd0, 0x0000 },
+  { 0x8700, 0xfcd3, 0x2000 },
+  { 0x0700, 0xfcd2, 0x0000 },
+  { 0x0700, 0xfcd4, 0x0000 },
+  { 0x8700, 0xfcd9, 0x3000 },
+  { 0x8700, 0xfcd7, 0x2000 },
+  { 0x0700, 0xfcd6, 0x0000 },
+  { 0x0700, 0xfcd8, 0x0000 },
+  { 0x8700, 0xfcdb, 0x2000 },
+  { 0x0700, 0xfcda, 0x0000 },
+  { 0x0700, 0xfcdc, 0x0000 },
+  { 0x8700, 0xfce5, 0x4000 },
+  { 0x8700, 0xfce1, 0x3000 },
+  { 0x8700, 0xfcdf, 0x2000 },
+  { 0x0700, 0xfcde, 0x0000 },
+  { 0x0700, 0xfce0, 0x0000 },
+  { 0x8700, 0xfce3, 0x2000 },
+  { 0x0700, 0xfce2, 0x0000 },
+  { 0x0700, 0xfce4, 0x0000 },
+  { 0x8700, 0xfce9, 0x3000 },
+  { 0x8700, 0xfce7, 0x2000 },
+  { 0x0700, 0xfce6, 0x0000 },
+  { 0x0700, 0xfce8, 0x0000 },
+  { 0x8700, 0xfceb, 0x2000 },
+  { 0x0700, 0xfcea, 0x0000 },
+  { 0x0700, 0xfcec, 0x0000 },
+  { 0x8700, 0xfcfd, 0x5000 },
+  { 0x8700, 0xfcf5, 0x4000 },
+  { 0x8700, 0xfcf1, 0x3000 },
+  { 0x8700, 0xfcef, 0x2000 },
+  { 0x0700, 0xfcee, 0x0000 },
+  { 0x0700, 0xfcf0, 0x0000 },
+  { 0x8700, 0xfcf3, 0x2000 },
+  { 0x0700, 0xfcf2, 0x0000 },
+  { 0x0700, 0xfcf4, 0x0000 },
+  { 0x8700, 0xfcf9, 0x3000 },
+  { 0x8700, 0xfcf7, 0x2000 },
+  { 0x0700, 0xfcf6, 0x0000 },
+  { 0x0700, 0xfcf8, 0x0000 },
+  { 0x8700, 0xfcfb, 0x2000 },
+  { 0x0700, 0xfcfa, 0x0000 },
+  { 0x0700, 0xfcfc, 0x0000 },
+  { 0x8700, 0xfd05, 0x4000 },
+  { 0x8700, 0xfd01, 0x3000 },
+  { 0x8700, 0xfcff, 0x2000 },
+  { 0x0700, 0xfcfe, 0x0000 },
+  { 0x0700, 0xfd00, 0x0000 },
+  { 0x8700, 0xfd03, 0x2000 },
+  { 0x0700, 0xfd02, 0x0000 },
+  { 0x0700, 0xfd04, 0x0000 },
+  { 0x8700, 0xfd09, 0x3000 },
+  { 0x8700, 0xfd07, 0x2000 },
+  { 0x0700, 0xfd06, 0x0000 },
+  { 0x0700, 0xfd08, 0x0000 },
+  { 0x8700, 0xfd0b, 0x2000 },
+  { 0x0700, 0xfd0a, 0x0000 },
+  { 0x0700, 0xfd0c, 0x0000 },
+  { 0x8700, 0xfd2d, 0x6000 },
+  { 0x8700, 0xfd1d, 0x5000 },
+  { 0x8700, 0xfd15, 0x4000 },
+  { 0x8700, 0xfd11, 0x3000 },
+  { 0x8700, 0xfd0f, 0x2000 },
+  { 0x0700, 0xfd0e, 0x0000 },
+  { 0x0700, 0xfd10, 0x0000 },
+  { 0x8700, 0xfd13, 0x2000 },
+  { 0x0700, 0xfd12, 0x0000 },
+  { 0x0700, 0xfd14, 0x0000 },
+  { 0x8700, 0xfd19, 0x3000 },
+  { 0x8700, 0xfd17, 0x2000 },
+  { 0x0700, 0xfd16, 0x0000 },
+  { 0x0700, 0xfd18, 0x0000 },
+  { 0x8700, 0xfd1b, 0x2000 },
+  { 0x0700, 0xfd1a, 0x0000 },
+  { 0x0700, 0xfd1c, 0x0000 },
+  { 0x8700, 0xfd25, 0x4000 },
+  { 0x8700, 0xfd21, 0x3000 },
+  { 0x8700, 0xfd1f, 0x2000 },
+  { 0x0700, 0xfd1e, 0x0000 },
+  { 0x0700, 0xfd20, 0x0000 },
+  { 0x8700, 0xfd23, 0x2000 },
+  { 0x0700, 0xfd22, 0x0000 },
+  { 0x0700, 0xfd24, 0x0000 },
+  { 0x8700, 0xfd29, 0x3000 },
+  { 0x8700, 0xfd27, 0x2000 },
+  { 0x0700, 0xfd26, 0x0000 },
+  { 0x0700, 0xfd28, 0x0000 },
+  { 0x8700, 0xfd2b, 0x2000 },
+  { 0x0700, 0xfd2a, 0x0000 },
+  { 0x0700, 0xfd2c, 0x0000 },
+  { 0x8700, 0xfd3d, 0x5000 },
+  { 0x8700, 0xfd35, 0x4000 },
+  { 0x8700, 0xfd31, 0x3000 },
+  { 0x8700, 0xfd2f, 0x2000 },
+  { 0x0700, 0xfd2e, 0x0000 },
+  { 0x0700, 0xfd30, 0x0000 },
+  { 0x8700, 0xfd33, 0x2000 },
+  { 0x0700, 0xfd32, 0x0000 },
+  { 0x0700, 0xfd34, 0x0000 },
+  { 0x8700, 0xfd39, 0x3000 },
+  { 0x8700, 0xfd37, 0x2000 },
+  { 0x0700, 0xfd36, 0x0000 },
+  { 0x0700, 0xfd38, 0x0000 },
+  { 0x8700, 0xfd3b, 0x2000 },
+  { 0x0700, 0xfd3a, 0x0000 },
+  { 0x0700, 0xfd3c, 0x0000 },
+  { 0x8700, 0xfd55, 0x4000 },
+  { 0x8700, 0xfd51, 0x3000 },
+  { 0x9200, 0xfd3f, 0x2000 },
+  { 0x1600, 0xfd3e, 0x0000 },
+  { 0x0700, 0xfd50, 0x0000 },
+  { 0x8700, 0xfd53, 0x2000 },
+  { 0x0700, 0xfd52, 0x0000 },
+  { 0x0700, 0xfd54, 0x0000 },
+  { 0x8700, 0xfd59, 0x3000 },
+  { 0x8700, 0xfd57, 0x2000 },
+  { 0x0700, 0xfd56, 0x0000 },
+  { 0x0700, 0xfd58, 0x0000 },
+  { 0x8700, 0xfd5b, 0x2000 },
+  { 0x0700, 0xfd5a, 0x0000 },
+  { 0x0700, 0xfd5c, 0x0000 },
+  { 0x8c00, 0xfe09, 0x8000 },
+  { 0x8700, 0xfd9f, 0x7000 },
+  { 0x8700, 0xfd7d, 0x6000 },
+  { 0x8700, 0xfd6d, 0x5000 },
+  { 0x8700, 0xfd65, 0x4000 },
+  { 0x8700, 0xfd61, 0x3000 },
+  { 0x8700, 0xfd5f, 0x2000 },
+  { 0x0700, 0xfd5e, 0x0000 },
+  { 0x0700, 0xfd60, 0x0000 },
+  { 0x8700, 0xfd63, 0x2000 },
+  { 0x0700, 0xfd62, 0x0000 },
+  { 0x0700, 0xfd64, 0x0000 },
+  { 0x8700, 0xfd69, 0x3000 },
+  { 0x8700, 0xfd67, 0x2000 },
+  { 0x0700, 0xfd66, 0x0000 },
+  { 0x0700, 0xfd68, 0x0000 },
+  { 0x8700, 0xfd6b, 0x2000 },
+  { 0x0700, 0xfd6a, 0x0000 },
+  { 0x0700, 0xfd6c, 0x0000 },
+  { 0x8700, 0xfd75, 0x4000 },
+  { 0x8700, 0xfd71, 0x3000 },
+  { 0x8700, 0xfd6f, 0x2000 },
+  { 0x0700, 0xfd6e, 0x0000 },
+  { 0x0700, 0xfd70, 0x0000 },
+  { 0x8700, 0xfd73, 0x2000 },
+  { 0x0700, 0xfd72, 0x0000 },
+  { 0x0700, 0xfd74, 0x0000 },
+  { 0x8700, 0xfd79, 0x3000 },
+  { 0x8700, 0xfd77, 0x2000 },
+  { 0x0700, 0xfd76, 0x0000 },
+  { 0x0700, 0xfd78, 0x0000 },
+  { 0x8700, 0xfd7b, 0x2000 },
+  { 0x0700, 0xfd7a, 0x0000 },
+  { 0x0700, 0xfd7c, 0x0000 },
+  { 0x8700, 0xfd8d, 0x5000 },
+  { 0x8700, 0xfd85, 0x4000 },
+  { 0x8700, 0xfd81, 0x3000 },
+  { 0x8700, 0xfd7f, 0x2000 },
+  { 0x0700, 0xfd7e, 0x0000 },
+  { 0x0700, 0xfd80, 0x0000 },
+  { 0x8700, 0xfd83, 0x2000 },
+  { 0x0700, 0xfd82, 0x0000 },
+  { 0x0700, 0xfd84, 0x0000 },
+  { 0x8700, 0xfd89, 0x3000 },
+  { 0x8700, 0xfd87, 0x2000 },
+  { 0x0700, 0xfd86, 0x0000 },
+  { 0x0700, 0xfd88, 0x0000 },
+  { 0x8700, 0xfd8b, 0x2000 },
+  { 0x0700, 0xfd8a, 0x0000 },
+  { 0x0700, 0xfd8c, 0x0000 },
+  { 0x8700, 0xfd97, 0x4000 },
+  { 0x8700, 0xfd93, 0x3000 },
+  { 0x8700, 0xfd8f, 0x2000 },
+  { 0x0700, 0xfd8e, 0x0000 },
+  { 0x0700, 0xfd92, 0x0000 },
+  { 0x8700, 0xfd95, 0x2000 },
+  { 0x0700, 0xfd94, 0x0000 },
+  { 0x0700, 0xfd96, 0x0000 },
+  { 0x8700, 0xfd9b, 0x3000 },
+  { 0x8700, 0xfd99, 0x2000 },
+  { 0x0700, 0xfd98, 0x0000 },
+  { 0x0700, 0xfd9a, 0x0000 },
+  { 0x8700, 0xfd9d, 0x2000 },
+  { 0x0700, 0xfd9c, 0x0000 },
+  { 0x0700, 0xfd9e, 0x0000 },
+  { 0x8700, 0xfdbf, 0x6000 },
+  { 0x8700, 0xfdaf, 0x5000 },
+  { 0x8700, 0xfda7, 0x4000 },
+  { 0x8700, 0xfda3, 0x3000 },
+  { 0x8700, 0xfda1, 0x2000 },
+  { 0x0700, 0xfda0, 0x0000 },
+  { 0x0700, 0xfda2, 0x0000 },
+  { 0x8700, 0xfda5, 0x2000 },
+  { 0x0700, 0xfda4, 0x0000 },
+  { 0x0700, 0xfda6, 0x0000 },
+  { 0x8700, 0xfdab, 0x3000 },
+  { 0x8700, 0xfda9, 0x2000 },
+  { 0x0700, 0xfda8, 0x0000 },
+  { 0x0700, 0xfdaa, 0x0000 },
+  { 0x8700, 0xfdad, 0x2000 },
+  { 0x0700, 0xfdac, 0x0000 },
+  { 0x0700, 0xfdae, 0x0000 },
+  { 0x8700, 0xfdb7, 0x4000 },
+  { 0x8700, 0xfdb3, 0x3000 },
+  { 0x8700, 0xfdb1, 0x2000 },
+  { 0x0700, 0xfdb0, 0x0000 },
+  { 0x0700, 0xfdb2, 0x0000 },
+  { 0x8700, 0xfdb5, 0x2000 },
+  { 0x0700, 0xfdb4, 0x0000 },
+  { 0x0700, 0xfdb6, 0x0000 },
+  { 0x8700, 0xfdbb, 0x3000 },
+  { 0x8700, 0xfdb9, 0x2000 },
+  { 0x0700, 0xfdb8, 0x0000 },
+  { 0x0700, 0xfdba, 0x0000 },
+  { 0x8700, 0xfdbd, 0x2000 },
+  { 0x0700, 0xfdbc, 0x0000 },
+  { 0x0700, 0xfdbe, 0x0000 },
+  { 0x8700, 0xfdf7, 0x5000 },
+  { 0x8700, 0xfdc7, 0x4000 },
+  { 0x8700, 0xfdc3, 0x3000 },
+  { 0x8700, 0xfdc1, 0x2000 },
+  { 0x0700, 0xfdc0, 0x0000 },
+  { 0x0700, 0xfdc2, 0x0000 },
+  { 0x8700, 0xfdc5, 0x2000 },
+  { 0x0700, 0xfdc4, 0x0000 },
+  { 0x0700, 0xfdc6, 0x0000 },
+  { 0x8700, 0xfdf3, 0x3000 },
+  { 0x8700, 0xfdf1, 0x2000 },
+  { 0x0700, 0xfdf0, 0x0000 },
+  { 0x0700, 0xfdf2, 0x0000 },
+  { 0x8700, 0xfdf5, 0x2000 },
+  { 0x0700, 0xfdf4, 0x0000 },
+  { 0x0700, 0xfdf6, 0x0000 },
+  { 0x8c00, 0xfe01, 0x4000 },
+  { 0x8700, 0xfdfb, 0x3000 },
+  { 0x8700, 0xfdf9, 0x2000 },
+  { 0x0700, 0xfdf8, 0x0000 },
+  { 0x0700, 0xfdfa, 0x0000 },
+  { 0x9a00, 0xfdfd, 0x2000 },
+  { 0x1700, 0xfdfc, 0x0000 },
+  { 0x0c00, 0xfe00, 0x0000 },
+  { 0x8c00, 0xfe05, 0x3000 },
+  { 0x8c00, 0xfe03, 0x2000 },
+  { 0x0c00, 0xfe02, 0x0000 },
+  { 0x0c00, 0xfe04, 0x0000 },
+  { 0x8c00, 0xfe07, 0x2000 },
+  { 0x0c00, 0xfe06, 0x0000 },
+  { 0x0c00, 0xfe08, 0x0000 },
+  { 0x9900, 0xfe66, 0x7000 },
+  { 0x9500, 0xfe45, 0x6000 },
+  { 0x9600, 0xfe35, 0x5000 },
+  { 0x8c00, 0xfe21, 0x4000 },
+  { 0x8c00, 0xfe0d, 0x3000 },
+  { 0x8c00, 0xfe0b, 0x2000 },
+  { 0x0c00, 0xfe0a, 0x0000 },
+  { 0x0c00, 0xfe0c, 0x0000 },
+  { 0x8c00, 0xfe0f, 0x2000 },
+  { 0x0c00, 0xfe0e, 0x0000 },
+  { 0x0c00, 0xfe20, 0x0000 },
+  { 0x9100, 0xfe31, 0x3000 },
+  { 0x8c00, 0xfe23, 0x2000 },
+  { 0x0c00, 0xfe22, 0x0000 },
+  { 0x1500, 0xfe30, 0x0000 },
+  { 0x9000, 0xfe33, 0x2000 },
+  { 0x1100, 0xfe32, 0x0000 },
+  { 0x1000, 0xfe34, 0x0000 },
+  { 0x9600, 0xfe3d, 0x4000 },
+  { 0x9600, 0xfe39, 0x3000 },
+  { 0x9600, 0xfe37, 0x2000 },
+  { 0x1200, 0xfe36, 0x0000 },
+  { 0x1200, 0xfe38, 0x0000 },
+  { 0x9600, 0xfe3b, 0x2000 },
+  { 0x1200, 0xfe3a, 0x0000 },
+  { 0x1200, 0xfe3c, 0x0000 },
+  { 0x9600, 0xfe41, 0x3000 },
+  { 0x9600, 0xfe3f, 0x2000 },
+  { 0x1200, 0xfe3e, 0x0000 },
+  { 0x1200, 0xfe40, 0x0000 },
+  { 0x9600, 0xfe43, 0x2000 },
+  { 0x1200, 0xfe42, 0x0000 },
+  { 0x1200, 0xfe44, 0x0000 },
+  { 0x9500, 0xfe56, 0x5000 },
+  { 0x9000, 0xfe4d, 0x4000 },
+  { 0x9500, 0xfe49, 0x3000 },
+  { 0x9600, 0xfe47, 0x2000 },
+  { 0x1500, 0xfe46, 0x0000 },
+  { 0x1200, 0xfe48, 0x0000 },
+  { 0x9500, 0xfe4b, 0x2000 },
+  { 0x1500, 0xfe4a, 0x0000 },
+  { 0x1500, 0xfe4c, 0x0000 },
+  { 0x9500, 0xfe51, 0x3000 },
+  { 0x9000, 0xfe4f, 0x2000 },
+  { 0x1000, 0xfe4e, 0x0000 },
+  { 0x1500, 0xfe50, 0x0000 },
+  { 0x9500, 0xfe54, 0x2000 },
+  { 0x1500, 0xfe52, 0x0000 },
+  { 0x1500, 0xfe55, 0x0000 },
+  { 0x9200, 0xfe5e, 0x4000 },
+  { 0x9200, 0xfe5a, 0x3000 },
+  { 0x9100, 0xfe58, 0x2000 },
+  { 0x1500, 0xfe57, 0x0000 },
+  { 0x1600, 0xfe59, 0x0000 },
+  { 0x9200, 0xfe5c, 0x2000 },
+  { 0x1600, 0xfe5b, 0x0000 },
+  { 0x1600, 0xfe5d, 0x0000 },
+  { 0x9900, 0xfe62, 0x3000 },
+  { 0x9500, 0xfe60, 0x2000 },
+  { 0x1500, 0xfe5f, 0x0000 },
+  { 0x1500, 0xfe61, 0x0000 },
+  { 0x9900, 0xfe64, 0x2000 },
+  { 0x1100, 0xfe63, 0x0000 },
+  { 0x1900, 0xfe65, 0x0000 },
+  { 0x8700, 0xfe8c, 0x6000 },
+  { 0x8700, 0xfe7c, 0x5000 },
+  { 0x8700, 0xfe73, 0x4000 },
+  { 0x9500, 0xfe6b, 0x3000 },
+  { 0x9700, 0xfe69, 0x2000 },
+  { 0x1500, 0xfe68, 0x0000 },
+  { 0x1500, 0xfe6a, 0x0000 },
+  { 0x8700, 0xfe71, 0x2000 },
+  { 0x0700, 0xfe70, 0x0000 },
+  { 0x0700, 0xfe72, 0x0000 },
+  { 0x8700, 0xfe78, 0x3000 },
+  { 0x8700, 0xfe76, 0x2000 },
+  { 0x0700, 0xfe74, 0x0000 },
+  { 0x0700, 0xfe77, 0x0000 },
+  { 0x8700, 0xfe7a, 0x2000 },
+  { 0x0700, 0xfe79, 0x0000 },
+  { 0x0700, 0xfe7b, 0x0000 },
+  { 0x8700, 0xfe84, 0x4000 },
+  { 0x8700, 0xfe80, 0x3000 },
+  { 0x8700, 0xfe7e, 0x2000 },
+  { 0x0700, 0xfe7d, 0x0000 },
+  { 0x0700, 0xfe7f, 0x0000 },
+  { 0x8700, 0xfe82, 0x2000 },
+  { 0x0700, 0xfe81, 0x0000 },
+  { 0x0700, 0xfe83, 0x0000 },
+  { 0x8700, 0xfe88, 0x3000 },
+  { 0x8700, 0xfe86, 0x2000 },
+  { 0x0700, 0xfe85, 0x0000 },
+  { 0x0700, 0xfe87, 0x0000 },
+  { 0x8700, 0xfe8a, 0x2000 },
+  { 0x0700, 0xfe89, 0x0000 },
+  { 0x0700, 0xfe8b, 0x0000 },
+  { 0x8700, 0xfe9c, 0x5000 },
+  { 0x8700, 0xfe94, 0x4000 },
+  { 0x8700, 0xfe90, 0x3000 },
+  { 0x8700, 0xfe8e, 0x2000 },
+  { 0x0700, 0xfe8d, 0x0000 },
+  { 0x0700, 0xfe8f, 0x0000 },
+  { 0x8700, 0xfe92, 0x2000 },
+  { 0x0700, 0xfe91, 0x0000 },
+  { 0x0700, 0xfe93, 0x0000 },
+  { 0x8700, 0xfe98, 0x3000 },
+  { 0x8700, 0xfe96, 0x2000 },
+  { 0x0700, 0xfe95, 0x0000 },
+  { 0x0700, 0xfe97, 0x0000 },
+  { 0x8700, 0xfe9a, 0x2000 },
+  { 0x0700, 0xfe99, 0x0000 },
+  { 0x0700, 0xfe9b, 0x0000 },
+  { 0x8700, 0xfea4, 0x4000 },
+  { 0x8700, 0xfea0, 0x3000 },
+  { 0x8700, 0xfe9e, 0x2000 },
+  { 0x0700, 0xfe9d, 0x0000 },
+  { 0x0700, 0xfe9f, 0x0000 },
+  { 0x8700, 0xfea2, 0x2000 },
+  { 0x0700, 0xfea1, 0x0000 },
+  { 0x0700, 0xfea3, 0x0000 },
+  { 0x8700, 0xfea8, 0x3000 },
+  { 0x8700, 0xfea6, 0x2000 },
+  { 0x0700, 0xfea5, 0x0000 },
+  { 0x0700, 0xfea7, 0x0000 },
+  { 0x8700, 0xfeaa, 0x2000 },
+  { 0x0700, 0xfea9, 0x0000 },
+  { 0x0700, 0xfeab, 0x0000 },
+  { 0x8700, 0xffaf, 0x9000 },
+  { 0x8900, 0xff2f, 0x8020 },
+  { 0x8700, 0xfeec, 0x7000 },
+  { 0x8700, 0xfecc, 0x6000 },
+  { 0x8700, 0xfebc, 0x5000 },
+  { 0x8700, 0xfeb4, 0x4000 },
+  { 0x8700, 0xfeb0, 0x3000 },
+  { 0x8700, 0xfeae, 0x2000 },
+  { 0x0700, 0xfead, 0x0000 },
+  { 0x0700, 0xfeaf, 0x0000 },
+  { 0x8700, 0xfeb2, 0x2000 },
+  { 0x0700, 0xfeb1, 0x0000 },
+  { 0x0700, 0xfeb3, 0x0000 },
+  { 0x8700, 0xfeb8, 0x3000 },
+  { 0x8700, 0xfeb6, 0x2000 },
+  { 0x0700, 0xfeb5, 0x0000 },
+  { 0x0700, 0xfeb7, 0x0000 },
+  { 0x8700, 0xfeba, 0x2000 },
+  { 0x0700, 0xfeb9, 0x0000 },
+  { 0x0700, 0xfebb, 0x0000 },
+  { 0x8700, 0xfec4, 0x4000 },
+  { 0x8700, 0xfec0, 0x3000 },
+  { 0x8700, 0xfebe, 0x2000 },
+  { 0x0700, 0xfebd, 0x0000 },
+  { 0x0700, 0xfebf, 0x0000 },
+  { 0x8700, 0xfec2, 0x2000 },
+  { 0x0700, 0xfec1, 0x0000 },
+  { 0x0700, 0xfec3, 0x0000 },
+  { 0x8700, 0xfec8, 0x3000 },
+  { 0x8700, 0xfec6, 0x2000 },
+  { 0x0700, 0xfec5, 0x0000 },
+  { 0x0700, 0xfec7, 0x0000 },
+  { 0x8700, 0xfeca, 0x2000 },
+  { 0x0700, 0xfec9, 0x0000 },
+  { 0x0700, 0xfecb, 0x0000 },
+  { 0x8700, 0xfedc, 0x5000 },
+  { 0x8700, 0xfed4, 0x4000 },
+  { 0x8700, 0xfed0, 0x3000 },
+  { 0x8700, 0xfece, 0x2000 },
+  { 0x0700, 0xfecd, 0x0000 },
+  { 0x0700, 0xfecf, 0x0000 },
+  { 0x8700, 0xfed2, 0x2000 },
+  { 0x0700, 0xfed1, 0x0000 },
+  { 0x0700, 0xfed3, 0x0000 },
+  { 0x8700, 0xfed8, 0x3000 },
+  { 0x8700, 0xfed6, 0x2000 },
+  { 0x0700, 0xfed5, 0x0000 },
+  { 0x0700, 0xfed7, 0x0000 },
+  { 0x8700, 0xfeda, 0x2000 },
+  { 0x0700, 0xfed9, 0x0000 },
+  { 0x0700, 0xfedb, 0x0000 },
+  { 0x8700, 0xfee4, 0x4000 },
+  { 0x8700, 0xfee0, 0x3000 },
+  { 0x8700, 0xfede, 0x2000 },
+  { 0x0700, 0xfedd, 0x0000 },
+  { 0x0700, 0xfedf, 0x0000 },
+  { 0x8700, 0xfee2, 0x2000 },
+  { 0x0700, 0xfee1, 0x0000 },
+  { 0x0700, 0xfee3, 0x0000 },
+  { 0x8700, 0xfee8, 0x3000 },
+  { 0x8700, 0xfee6, 0x2000 },
+  { 0x0700, 0xfee5, 0x0000 },
+  { 0x0700, 0xfee7, 0x0000 },
+  { 0x8700, 0xfeea, 0x2000 },
+  { 0x0700, 0xfee9, 0x0000 },
+  { 0x0700, 0xfeeb, 0x0000 },
+  { 0x9500, 0xff0f, 0x6000 },
+  { 0x8700, 0xfefc, 0x5000 },
+  { 0x8700, 0xfef4, 0x4000 },
+  { 0x8700, 0xfef0, 0x3000 },
+  { 0x8700, 0xfeee, 0x2000 },
+  { 0x0700, 0xfeed, 0x0000 },
+  { 0x0700, 0xfeef, 0x0000 },
+  { 0x8700, 0xfef2, 0x2000 },
+  { 0x0700, 0xfef1, 0x0000 },
+  { 0x0700, 0xfef3, 0x0000 },
+  { 0x8700, 0xfef8, 0x3000 },
+  { 0x8700, 0xfef6, 0x2000 },
+  { 0x0700, 0xfef5, 0x0000 },
+  { 0x0700, 0xfef7, 0x0000 },
+  { 0x8700, 0xfefa, 0x2000 },
+  { 0x0700, 0xfef9, 0x0000 },
+  { 0x0700, 0xfefb, 0x0000 },
+  { 0x9500, 0xff07, 0x4000 },
+  { 0x9500, 0xff03, 0x3000 },
+  { 0x9500, 0xff01, 0x2000 },
+  { 0x0100, 0xfeff, 0x0000 },
+  { 0x1500, 0xff02, 0x0000 },
+  { 0x9500, 0xff05, 0x2000 },
+  { 0x1700, 0xff04, 0x0000 },
+  { 0x1500, 0xff06, 0x0000 },
+  { 0x9900, 0xff0b, 0x3000 },
+  { 0x9200, 0xff09, 0x2000 },
+  { 0x1600, 0xff08, 0x0000 },
+  { 0x1500, 0xff0a, 0x0000 },
+  { 0x9100, 0xff0d, 0x2000 },
+  { 0x1500, 0xff0c, 0x0000 },
+  { 0x1500, 0xff0e, 0x0000 },
+  { 0x9500, 0xff1f, 0x5000 },
+  { 0x8d00, 0xff17, 0x4000 },
+  { 0x8d00, 0xff13, 0x3000 },
+  { 0x8d00, 0xff11, 0x2000 },
+  { 0x0d00, 0xff10, 0x0000 },
+  { 0x0d00, 0xff12, 0x0000 },
+  { 0x8d00, 0xff15, 0x2000 },
+  { 0x0d00, 0xff14, 0x0000 },
+  { 0x0d00, 0xff16, 0x0000 },
+  { 0x9500, 0xff1b, 0x3000 },
+  { 0x8d00, 0xff19, 0x2000 },
+  { 0x0d00, 0xff18, 0x0000 },
+  { 0x1500, 0xff1a, 0x0000 },
+  { 0x9900, 0xff1d, 0x2000 },
+  { 0x1900, 0xff1c, 0x0000 },
+  { 0x1900, 0xff1e, 0x0000 },
+  { 0x8900, 0xff27, 0x4020 },
+  { 0x8900, 0xff23, 0x3020 },
+  { 0x8900, 0xff21, 0x2020 },
+  { 0x1500, 0xff20, 0x0000 },
+  { 0x0900, 0xff22, 0x0020 },
+  { 0x8900, 0xff25, 0x2020 },
+  { 0x0900, 0xff24, 0x0020 },
+  { 0x0900, 0xff26, 0x0020 },
+  { 0x8900, 0xff2b, 0x3020 },
+  { 0x8900, 0xff29, 0x2020 },
+  { 0x0900, 0xff28, 0x0020 },
+  { 0x0900, 0xff2a, 0x0020 },
+  { 0x8900, 0xff2d, 0x2020 },
+  { 0x0900, 0xff2c, 0x0020 },
+  { 0x0900, 0xff2e, 0x0020 },
+  { 0x8700, 0xff6f, 0x7000 },
+  { 0x8500, 0xff4f, 0x6fe0 },
+  { 0x9000, 0xff3f, 0x5000 },
+  { 0x8900, 0xff37, 0x4020 },
+  { 0x8900, 0xff33, 0x3020 },
+  { 0x8900, 0xff31, 0x2020 },
+  { 0x0900, 0xff30, 0x0020 },
+  { 0x0900, 0xff32, 0x0020 },
+  { 0x8900, 0xff35, 0x2020 },
+  { 0x0900, 0xff34, 0x0020 },
+  { 0x0900, 0xff36, 0x0020 },
+  { 0x9600, 0xff3b, 0x3000 },
+  { 0x8900, 0xff39, 0x2020 },
+  { 0x0900, 0xff38, 0x0020 },
+  { 0x0900, 0xff3a, 0x0020 },
+  { 0x9200, 0xff3d, 0x2000 },
+  { 0x1500, 0xff3c, 0x0000 },
+  { 0x1800, 0xff3e, 0x0000 },
+  { 0x8500, 0xff47, 0x4fe0 },
+  { 0x8500, 0xff43, 0x3fe0 },
+  { 0x8500, 0xff41, 0x2fe0 },
+  { 0x1800, 0xff40, 0x0000 },
+  { 0x0500, 0xff42, 0x0fe0 },
+  { 0x8500, 0xff45, 0x2fe0 },
+  { 0x0500, 0xff44, 0x0fe0 },
+  { 0x0500, 0xff46, 0x0fe0 },
+  { 0x8500, 0xff4b, 0x3fe0 },
+  { 0x8500, 0xff49, 0x2fe0 },
+  { 0x0500, 0xff48, 0x0fe0 },
+  { 0x0500, 0xff4a, 0x0fe0 },
+  { 0x8500, 0xff4d, 0x2fe0 },
+  { 0x0500, 0xff4c, 0x0fe0 },
+  { 0x0500, 0xff4e, 0x0fe0 },
+  { 0x9600, 0xff5f, 0x5000 },
+  { 0x8500, 0xff57, 0x4fe0 },
+  { 0x8500, 0xff53, 0x3fe0 },
+  { 0x8500, 0xff51, 0x2fe0 },
+  { 0x0500, 0xff50, 0x0fe0 },
+  { 0x0500, 0xff52, 0x0fe0 },
+  { 0x8500, 0xff55, 0x2fe0 },
+  { 0x0500, 0xff54, 0x0fe0 },
+  { 0x0500, 0xff56, 0x0fe0 },
+  { 0x9600, 0xff5b, 0x3000 },
+  { 0x8500, 0xff59, 0x2fe0 },
+  { 0x0500, 0xff58, 0x0fe0 },
+  { 0x0500, 0xff5a, 0x0fe0 },
+  { 0x9200, 0xff5d, 0x2000 },
+  { 0x1900, 0xff5c, 0x0000 },
+  { 0x1900, 0xff5e, 0x0000 },
+  { 0x8700, 0xff67, 0x4000 },
+  { 0x9200, 0xff63, 0x3000 },
+  { 0x9500, 0xff61, 0x2000 },
+  { 0x1200, 0xff60, 0x0000 },
+  { 0x1600, 0xff62, 0x0000 },
+  { 0x9000, 0xff65, 0x2000 },
+  { 0x1500, 0xff64, 0x0000 },
+  { 0x0700, 0xff66, 0x0000 },
+  { 0x8700, 0xff6b, 0x3000 },
+  { 0x8700, 0xff69, 0x2000 },
+  { 0x0700, 0xff68, 0x0000 },
+  { 0x0700, 0xff6a, 0x0000 },
+  { 0x8700, 0xff6d, 0x2000 },
+  { 0x0700, 0xff6c, 0x0000 },
+  { 0x0700, 0xff6e, 0x0000 },
+  { 0x8700, 0xff8f, 0x6000 },
+  { 0x8700, 0xff7f, 0x5000 },
+  { 0x8700, 0xff77, 0x4000 },
+  { 0x8700, 0xff73, 0x3000 },
+  { 0x8700, 0xff71, 0x2000 },
+  { 0x0600, 0xff70, 0x0000 },
+  { 0x0700, 0xff72, 0x0000 },
+  { 0x8700, 0xff75, 0x2000 },
+  { 0x0700, 0xff74, 0x0000 },
+  { 0x0700, 0xff76, 0x0000 },
+  { 0x8700, 0xff7b, 0x3000 },
+  { 0x8700, 0xff79, 0x2000 },
+  { 0x0700, 0xff78, 0x0000 },
+  { 0x0700, 0xff7a, 0x0000 },
+  { 0x8700, 0xff7d, 0x2000 },
+  { 0x0700, 0xff7c, 0x0000 },
+  { 0x0700, 0xff7e, 0x0000 },
+  { 0x8700, 0xff87, 0x4000 },
+  { 0x8700, 0xff83, 0x3000 },
+  { 0x8700, 0xff81, 0x2000 },
+  { 0x0700, 0xff80, 0x0000 },
+  { 0x0700, 0xff82, 0x0000 },
+  { 0x8700, 0xff85, 0x2000 },
+  { 0x0700, 0xff84, 0x0000 },
+  { 0x0700, 0xff86, 0x0000 },
+  { 0x8700, 0xff8b, 0x3000 },
+  { 0x8700, 0xff89, 0x2000 },
+  { 0x0700, 0xff88, 0x0000 },
+  { 0x0700, 0xff8a, 0x0000 },
+  { 0x8700, 0xff8d, 0x2000 },
+  { 0x0700, 0xff8c, 0x0000 },
+  { 0x0700, 0xff8e, 0x0000 },
+  { 0x8600, 0xff9f, 0x5000 },
+  { 0x8700, 0xff97, 0x4000 },
+  { 0x8700, 0xff93, 0x3000 },
+  { 0x8700, 0xff91, 0x2000 },
+  { 0x0700, 0xff90, 0x0000 },
+  { 0x0700, 0xff92, 0x0000 },
+  { 0x8700, 0xff95, 0x2000 },
+  { 0x0700, 0xff94, 0x0000 },
+  { 0x0700, 0xff96, 0x0000 },
+  { 0x8700, 0xff9b, 0x3000 },
+  { 0x8700, 0xff99, 0x2000 },
+  { 0x0700, 0xff98, 0x0000 },
+  { 0x0700, 0xff9a, 0x0000 },
+  { 0x8700, 0xff9d, 0x2000 },
+  { 0x0700, 0xff9c, 0x0000 },
+  { 0x0600, 0xff9e, 0x0000 },
+  { 0x8700, 0xffa7, 0x4000 },
+  { 0x8700, 0xffa3, 0x3000 },
+  { 0x8700, 0xffa1, 0x2000 },
+  { 0x0700, 0xffa0, 0x0000 },
+  { 0x0700, 0xffa2, 0x0000 },
+  { 0x8700, 0xffa5, 0x2000 },
+  { 0x0700, 0xffa4, 0x0000 },
+  { 0x0700, 0xffa6, 0x0000 },
+  { 0x8700, 0xffab, 0x3000 },
+  { 0x8700, 0xffa9, 0x2000 },
+  { 0x0700, 0xffa8, 0x0000 },
+  { 0x0700, 0xffaa, 0x0000 },
+  { 0x8700, 0xffad, 0x2000 },
+  { 0x0700, 0xffac, 0x0000 },
+  { 0x0700, 0xffae, 0x0000 },
+  { 0x8701, 0x004c, 0x8000 },
+  { 0x8701, 0x0008, 0x7000 },
+  { 0x8700, 0xffd6, 0x6000 },
+  { 0x8700, 0xffc2, 0x5000 },
+  { 0x8700, 0xffb7, 0x4000 },
+  { 0x8700, 0xffb3, 0x3000 },
+  { 0x8700, 0xffb1, 0x2000 },
+  { 0x0700, 0xffb0, 0x0000 },
+  { 0x0700, 0xffb2, 0x0000 },
+  { 0x8700, 0xffb5, 0x2000 },
+  { 0x0700, 0xffb4, 0x0000 },
+  { 0x0700, 0xffb6, 0x0000 },
+  { 0x8700, 0xffbb, 0x3000 },
+  { 0x8700, 0xffb9, 0x2000 },
+  { 0x0700, 0xffb8, 0x0000 },
+  { 0x0700, 0xffba, 0x0000 },
+  { 0x8700, 0xffbd, 0x2000 },
+  { 0x0700, 0xffbc, 0x0000 },
+  { 0x0700, 0xffbe, 0x0000 },
+  { 0x8700, 0xffcc, 0x4000 },
+  { 0x8700, 0xffc6, 0x3000 },
+  { 0x8700, 0xffc4, 0x2000 },
+  { 0x0700, 0xffc3, 0x0000 },
+  { 0x0700, 0xffc5, 0x0000 },
+  { 0x8700, 0xffca, 0x2000 },
+  { 0x0700, 0xffc7, 0x0000 },
+  { 0x0700, 0xffcb, 0x0000 },
+  { 0x8700, 0xffd2, 0x3000 },
+  { 0x8700, 0xffce, 0x2000 },
+  { 0x0700, 0xffcd, 0x0000 },
+  { 0x0700, 0xffcf, 0x0000 },
+  { 0x8700, 0xffd4, 0x2000 },
+  { 0x0700, 0xffd3, 0x0000 },
+  { 0x0700, 0xffd5, 0x0000 },
+  { 0x9900, 0xffec, 0x5000 },
+  { 0x9800, 0xffe3, 0x4000 },
+  { 0x8700, 0xffdc, 0x3000 },
+  { 0x8700, 0xffda, 0x2000 },
+  { 0x0700, 0xffd7, 0x0000 },
+  { 0x0700, 0xffdb, 0x0000 },
+  { 0x9700, 0xffe1, 0x2000 },
+  { 0x1700, 0xffe0, 0x0000 },
+  { 0x1900, 0xffe2, 0x0000 },
+  { 0x9a00, 0xffe8, 0x3000 },
+  { 0x9700, 0xffe5, 0x2000 },
+  { 0x1a00, 0xffe4, 0x0000 },
+  { 0x1700, 0xffe6, 0x0000 },
+  { 0x9900, 0xffea, 0x2000 },
+  { 0x1900, 0xffe9, 0x0000 },
+  { 0x1900, 0xffeb, 0x0000 },
+  { 0x8701, 0x0000, 0x4000 },
+  { 0x8100, 0xfffa, 0x3000 },
+  { 0x9a00, 0xffee, 0x2000 },
+  { 0x1a00, 0xffed, 0x0000 },
+  { 0x0100, 0xfff9, 0x0000 },
+  { 0x9a00, 0xfffc, 0x2000 },
+  { 0x0100, 0xfffb, 0x0000 },
+  { 0x1a00, 0xfffd, 0x0000 },
+  { 0x8701, 0x0004, 0x3000 },
+  { 0x8701, 0x0002, 0x2000 },
+  { 0x0701, 0x0001, 0x0000 },
+  { 0x0701, 0x0003, 0x0000 },
+  { 0x8701, 0x0006, 0x2000 },
+  { 0x0701, 0x0005, 0x0000 },
+  { 0x0701, 0x0007, 0x0000 },
+  { 0x8701, 0x002a, 0x6000 },
+  { 0x8701, 0x0019, 0x5000 },
+  { 0x8701, 0x0011, 0x4000 },
+  { 0x8701, 0x000d, 0x3000 },
+  { 0x8701, 0x000a, 0x2000 },
+  { 0x0701, 0x0009, 0x0000 },
+  { 0x0701, 0x000b, 0x0000 },
+  { 0x8701, 0x000f, 0x2000 },
+  { 0x0701, 0x000e, 0x0000 },
+  { 0x0701, 0x0010, 0x0000 },
+  { 0x8701, 0x0015, 0x3000 },
+  { 0x8701, 0x0013, 0x2000 },
+  { 0x0701, 0x0012, 0x0000 },
+  { 0x0701, 0x0014, 0x0000 },
+  { 0x8701, 0x0017, 0x2000 },
+  { 0x0701, 0x0016, 0x0000 },
+  { 0x0701, 0x0018, 0x0000 },
+  { 0x8701, 0x0021, 0x4000 },
+  { 0x8701, 0x001d, 0x3000 },
+  { 0x8701, 0x001b, 0x2000 },
+  { 0x0701, 0x001a, 0x0000 },
+  { 0x0701, 0x001c, 0x0000 },
+  { 0x8701, 0x001f, 0x2000 },
+  { 0x0701, 0x001e, 0x0000 },
+  { 0x0701, 0x0020, 0x0000 },
+  { 0x8701, 0x0025, 0x3000 },
+  { 0x8701, 0x0023, 0x2000 },
+  { 0x0701, 0x0022, 0x0000 },
+  { 0x0701, 0x0024, 0x0000 },
+  { 0x8701, 0x0028, 0x2000 },
+  { 0x0701, 0x0026, 0x0000 },
+  { 0x0701, 0x0029, 0x0000 },
+  { 0x8701, 0x003a, 0x5000 },
+  { 0x8701, 0x0032, 0x4000 },
+  { 0x8701, 0x002e, 0x3000 },
+  { 0x8701, 0x002c, 0x2000 },
+  { 0x0701, 0x002b, 0x0000 },
+  { 0x0701, 0x002d, 0x0000 },
+  { 0x8701, 0x0030, 0x2000 },
+  { 0x0701, 0x002f, 0x0000 },
+  { 0x0701, 0x0031, 0x0000 },
+  { 0x8701, 0x0036, 0x3000 },
+  { 0x8701, 0x0034, 0x2000 },
+  { 0x0701, 0x0033, 0x0000 },
+  { 0x0701, 0x0035, 0x0000 },
+  { 0x8701, 0x0038, 0x2000 },
+  { 0x0701, 0x0037, 0x0000 },
+  { 0x0701, 0x0039, 0x0000 },
+  { 0x8701, 0x0044, 0x4000 },
+  { 0x8701, 0x0040, 0x3000 },
+  { 0x8701, 0x003d, 0x2000 },
+  { 0x0701, 0x003c, 0x0000 },
+  { 0x0701, 0x003f, 0x0000 },
+  { 0x8701, 0x0042, 0x2000 },
+  { 0x0701, 0x0041, 0x0000 },
+  { 0x0701, 0x0043, 0x0000 },
+  { 0x8701, 0x0048, 0x3000 },
+  { 0x8701, 0x0046, 0x2000 },
+  { 0x0701, 0x0045, 0x0000 },
+  { 0x0701, 0x0047, 0x0000 },
+  { 0x8701, 0x004a, 0x2000 },
+  { 0x0701, 0x0049, 0x0000 },
+  { 0x0701, 0x004b, 0x0000 },
+  { 0x8701, 0x00b0, 0x7000 },
+  { 0x8701, 0x0090, 0x6000 },
+  { 0x8701, 0x0080, 0x5000 },
+  { 0x8701, 0x0056, 0x4000 },
+  { 0x8701, 0x0052, 0x3000 },
+  { 0x8701, 0x0050, 0x2000 },
+  { 0x0701, 0x004d, 0x0000 },
+  { 0x0701, 0x0051, 0x0000 },
+  { 0x8701, 0x0054, 0x2000 },
+  { 0x0701, 0x0053, 0x0000 },
+  { 0x0701, 0x0055, 0x0000 },
+  { 0x8701, 0x005a, 0x3000 },
+  { 0x8701, 0x0058, 0x2000 },
+  { 0x0701, 0x0057, 0x0000 },
+  { 0x0701, 0x0059, 0x0000 },
+  { 0x8701, 0x005c, 0x2000 },
+  { 0x0701, 0x005b, 0x0000 },
+  { 0x0701, 0x005d, 0x0000 },
+  { 0x8701, 0x0088, 0x4000 },
+  { 0x8701, 0x0084, 0x3000 },
+  { 0x8701, 0x0082, 0x2000 },
+  { 0x0701, 0x0081, 0x0000 },
+  { 0x0701, 0x0083, 0x0000 },
+  { 0x8701, 0x0086, 0x2000 },
+  { 0x0701, 0x0085, 0x0000 },
+  { 0x0701, 0x0087, 0x0000 },
+  { 0x8701, 0x008c, 0x3000 },
+  { 0x8701, 0x008a, 0x2000 },
+  { 0x0701, 0x0089, 0x0000 },
+  { 0x0701, 0x008b, 0x0000 },
+  { 0x8701, 0x008e, 0x2000 },
+  { 0x0701, 0x008d, 0x0000 },
+  { 0x0701, 0x008f, 0x0000 },
+  { 0x8701, 0x00a0, 0x5000 },
+  { 0x8701, 0x0098, 0x4000 },
+  { 0x8701, 0x0094, 0x3000 },
+  { 0x8701, 0x0092, 0x2000 },
+  { 0x0701, 0x0091, 0x0000 },
+  { 0x0701, 0x0093, 0x0000 },
+  { 0x8701, 0x0096, 0x2000 },
+  { 0x0701, 0x0095, 0x0000 },
+  { 0x0701, 0x0097, 0x0000 },
+  { 0x8701, 0x009c, 0x3000 },
+  { 0x8701, 0x009a, 0x2000 },
+  { 0x0701, 0x0099, 0x0000 },
+  { 0x0701, 0x009b, 0x0000 },
+  { 0x8701, 0x009e, 0x2000 },
+  { 0x0701, 0x009d, 0x0000 },
+  { 0x0701, 0x009f, 0x0000 },
+  { 0x8701, 0x00a8, 0x4000 },
+  { 0x8701, 0x00a4, 0x3000 },
+  { 0x8701, 0x00a2, 0x2000 },
+  { 0x0701, 0x00a1, 0x0000 },
+  { 0x0701, 0x00a3, 0x0000 },
+  { 0x8701, 0x00a6, 0x2000 },
+  { 0x0701, 0x00a5, 0x0000 },
+  { 0x0701, 0x00a7, 0x0000 },
+  { 0x8701, 0x00ac, 0x3000 },
+  { 0x8701, 0x00aa, 0x2000 },
+  { 0x0701, 0x00a9, 0x0000 },
+  { 0x0701, 0x00ab, 0x0000 },
+  { 0x8701, 0x00ae, 0x2000 },
+  { 0x0701, 0x00ad, 0x0000 },
+  { 0x0701, 0x00af, 0x0000 },
+  { 0x8701, 0x00d0, 0x6000 },
+  { 0x8701, 0x00c0, 0x5000 },
+  { 0x8701, 0x00b8, 0x4000 },
+  { 0x8701, 0x00b4, 0x3000 },
+  { 0x8701, 0x00b2, 0x2000 },
+  { 0x0701, 0x00b1, 0x0000 },
+  { 0x0701, 0x00b3, 0x0000 },
+  { 0x8701, 0x00b6, 0x2000 },
+  { 0x0701, 0x00b5, 0x0000 },
+  { 0x0701, 0x00b7, 0x0000 },
+  { 0x8701, 0x00bc, 0x3000 },
+  { 0x8701, 0x00ba, 0x2000 },
+  { 0x0701, 0x00b9, 0x0000 },
+  { 0x0701, 0x00bb, 0x0000 },
+  { 0x8701, 0x00be, 0x2000 },
+  { 0x0701, 0x00bd, 0x0000 },
+  { 0x0701, 0x00bf, 0x0000 },
+  { 0x8701, 0x00c8, 0x4000 },
+  { 0x8701, 0x00c4, 0x3000 },
+  { 0x8701, 0x00c2, 0x2000 },
+  { 0x0701, 0x00c1, 0x0000 },
+  { 0x0701, 0x00c3, 0x0000 },
+  { 0x8701, 0x00c6, 0x2000 },
+  { 0x0701, 0x00c5, 0x0000 },
+  { 0x0701, 0x00c7, 0x0000 },
+  { 0x8701, 0x00cc, 0x3000 },
+  { 0x8701, 0x00ca, 0x2000 },
+  { 0x0701, 0x00c9, 0x0000 },
+  { 0x0701, 0x00cb, 0x0000 },
+  { 0x8701, 0x00ce, 0x2000 },
+  { 0x0701, 0x00cd, 0x0000 },
+  { 0x0701, 0x00cf, 0x0000 },
+  { 0x8701, 0x00e0, 0x5000 },
+  { 0x8701, 0x00d8, 0x4000 },
+  { 0x8701, 0x00d4, 0x3000 },
+  { 0x8701, 0x00d2, 0x2000 },
+  { 0x0701, 0x00d1, 0x0000 },
+  { 0x0701, 0x00d3, 0x0000 },
+  { 0x8701, 0x00d6, 0x2000 },
+  { 0x0701, 0x00d5, 0x0000 },
+  { 0x0701, 0x00d7, 0x0000 },
+  { 0x8701, 0x00dc, 0x3000 },
+  { 0x8701, 0x00da, 0x2000 },
+  { 0x0701, 0x00d9, 0x0000 },
+  { 0x0701, 0x00db, 0x0000 },
+  { 0x8701, 0x00de, 0x2000 },
+  { 0x0701, 0x00dd, 0x0000 },
+  { 0x0701, 0x00df, 0x0000 },
+  { 0x8701, 0x00e8, 0x4000 },
+  { 0x8701, 0x00e4, 0x3000 },
+  { 0x8701, 0x00e2, 0x2000 },
+  { 0x0701, 0x00e1, 0x0000 },
+  { 0x0701, 0x00e3, 0x0000 },
+  { 0x8701, 0x00e6, 0x2000 },
+  { 0x0701, 0x00e5, 0x0000 },
+  { 0x0701, 0x00e7, 0x0000 },
+  { 0x8701, 0x00ec, 0x3000 },
+  { 0x8701, 0x00ea, 0x2000 },
+  { 0x0701, 0x00e9, 0x0000 },
+  { 0x0701, 0x00eb, 0x0000 },
+  { 0x8701, 0x00ee, 0x2000 },
+  { 0x0701, 0x00ed, 0x0000 },
+  { 0x0701, 0x00ef, 0x0000 },
+  { 0x8501, 0xd459, 0xb000 },
+  { 0x9a01, 0xd080, 0xa000 },
+  { 0x8701, 0x045f, 0x9000 },
+  { 0x8701, 0x0349, 0x8000 },
+  { 0x9a01, 0x013c, 0x7000 },
+  { 0x8f01, 0x0119, 0x6000 },
+  { 0x8f01, 0x0109, 0x5000 },
+  { 0x8701, 0x00f8, 0x4000 },
+  { 0x8701, 0x00f4, 0x3000 },
+  { 0x8701, 0x00f2, 0x2000 },
+  { 0x0701, 0x00f1, 0x0000 },
+  { 0x0701, 0x00f3, 0x0000 },
+  { 0x8701, 0x00f6, 0x2000 },
+  { 0x0701, 0x00f5, 0x0000 },
+  { 0x0701, 0x00f7, 0x0000 },
+  { 0x9501, 0x0101, 0x3000 },
+  { 0x8701, 0x00fa, 0x2000 },
+  { 0x0701, 0x00f9, 0x0000 },
+  { 0x1501, 0x0100, 0x0000 },
+  { 0x8f01, 0x0107, 0x2000 },
+  { 0x1a01, 0x0102, 0x0000 },
+  { 0x0f01, 0x0108, 0x0000 },
+  { 0x8f01, 0x0111, 0x4000 },
+  { 0x8f01, 0x010d, 0x3000 },
+  { 0x8f01, 0x010b, 0x2000 },
+  { 0x0f01, 0x010a, 0x0000 },
+  { 0x0f01, 0x010c, 0x0000 },
+  { 0x8f01, 0x010f, 0x2000 },
+  { 0x0f01, 0x010e, 0x0000 },
+  { 0x0f01, 0x0110, 0x0000 },
+  { 0x8f01, 0x0115, 0x3000 },
+  { 0x8f01, 0x0113, 0x2000 },
+  { 0x0f01, 0x0112, 0x0000 },
+  { 0x0f01, 0x0114, 0x0000 },
+  { 0x8f01, 0x0117, 0x2000 },
+  { 0x0f01, 0x0116, 0x0000 },
+  { 0x0f01, 0x0118, 0x0000 },
+  { 0x8f01, 0x0129, 0x5000 },
+  { 0x8f01, 0x0121, 0x4000 },
+  { 0x8f01, 0x011d, 0x3000 },
+  { 0x8f01, 0x011b, 0x2000 },
+  { 0x0f01, 0x011a, 0x0000 },
+  { 0x0f01, 0x011c, 0x0000 },
+  { 0x8f01, 0x011f, 0x2000 },
+  { 0x0f01, 0x011e, 0x0000 },
+  { 0x0f01, 0x0120, 0x0000 },
+  { 0x8f01, 0x0125, 0x3000 },
+  { 0x8f01, 0x0123, 0x2000 },
+  { 0x0f01, 0x0122, 0x0000 },
+  { 0x0f01, 0x0124, 0x0000 },
+  { 0x8f01, 0x0127, 0x2000 },
+  { 0x0f01, 0x0126, 0x0000 },
+  { 0x0f01, 0x0128, 0x0000 },
+  { 0x8f01, 0x0131, 0x4000 },
+  { 0x8f01, 0x012d, 0x3000 },
+  { 0x8f01, 0x012b, 0x2000 },
+  { 0x0f01, 0x012a, 0x0000 },
+  { 0x0f01, 0x012c, 0x0000 },
+  { 0x8f01, 0x012f, 0x2000 },
+  { 0x0f01, 0x012e, 0x0000 },
+  { 0x0f01, 0x0130, 0x0000 },
+  { 0x9a01, 0x0138, 0x3000 },
+  { 0x8f01, 0x0133, 0x2000 },
+  { 0x0f01, 0x0132, 0x0000 },
+  { 0x1a01, 0x0137, 0x0000 },
+  { 0x9a01, 0x013a, 0x2000 },
+  { 0x1a01, 0x0139, 0x0000 },
+  { 0x1a01, 0x013b, 0x0000 },
+  { 0x8701, 0x031c, 0x6000 },
+  { 0x8701, 0x030c, 0x5000 },
+  { 0x8701, 0x0304, 0x4000 },
+  { 0x8701, 0x0300, 0x3000 },
+  { 0x9a01, 0x013e, 0x2000 },
+  { 0x1a01, 0x013d, 0x0000 },
+  { 0x1a01, 0x013f, 0x0000 },
+  { 0x8701, 0x0302, 0x2000 },
+  { 0x0701, 0x0301, 0x0000 },
+  { 0x0701, 0x0303, 0x0000 },
+  { 0x8701, 0x0308, 0x3000 },
+  { 0x8701, 0x0306, 0x2000 },
+  { 0x0701, 0x0305, 0x0000 },
+  { 0x0701, 0x0307, 0x0000 },
+  { 0x8701, 0x030a, 0x2000 },
+  { 0x0701, 0x0309, 0x0000 },
+  { 0x0701, 0x030b, 0x0000 },
+  { 0x8701, 0x0314, 0x4000 },
+  { 0x8701, 0x0310, 0x3000 },
+  { 0x8701, 0x030e, 0x2000 },
+  { 0x0701, 0x030d, 0x0000 },
+  { 0x0701, 0x030f, 0x0000 },
+  { 0x8701, 0x0312, 0x2000 },
+  { 0x0701, 0x0311, 0x0000 },
+  { 0x0701, 0x0313, 0x0000 },
+  { 0x8701, 0x0318, 0x3000 },
+  { 0x8701, 0x0316, 0x2000 },
+  { 0x0701, 0x0315, 0x0000 },
+  { 0x0701, 0x0317, 0x0000 },
+  { 0x8701, 0x031a, 0x2000 },
+  { 0x0701, 0x0319, 0x0000 },
+  { 0x0701, 0x031b, 0x0000 },
+  { 0x8701, 0x0339, 0x5000 },
+  { 0x8701, 0x0331, 0x4000 },
+  { 0x8f01, 0x0321, 0x3000 },
+  { 0x8701, 0x031e, 0x2000 },
+  { 0x0701, 0x031d, 0x0000 },
+  { 0x0f01, 0x0320, 0x0000 },
+  { 0x8f01, 0x0323, 0x2000 },
+  { 0x0f01, 0x0322, 0x0000 },
+  { 0x0701, 0x0330, 0x0000 },
+  { 0x8701, 0x0335, 0x3000 },
+  { 0x8701, 0x0333, 0x2000 },
+  { 0x0701, 0x0332, 0x0000 },
+  { 0x0701, 0x0334, 0x0000 },
+  { 0x8701, 0x0337, 0x2000 },
+  { 0x0701, 0x0336, 0x0000 },
+  { 0x0701, 0x0338, 0x0000 },
+  { 0x8701, 0x0341, 0x4000 },
+  { 0x8701, 0x033d, 0x3000 },
+  { 0x8701, 0x033b, 0x2000 },
+  { 0x0701, 0x033a, 0x0000 },
+  { 0x0701, 0x033c, 0x0000 },
+  { 0x8701, 0x033f, 0x2000 },
+  { 0x0701, 0x033e, 0x0000 },
+  { 0x0701, 0x0340, 0x0000 },
+  { 0x8701, 0x0345, 0x3000 },
+  { 0x8701, 0x0343, 0x2000 },
+  { 0x0701, 0x0342, 0x0000 },
+  { 0x0701, 0x0344, 0x0000 },
+  { 0x8701, 0x0347, 0x2000 },
+  { 0x0701, 0x0346, 0x0000 },
+  { 0x0701, 0x0348, 0x0000 },
+  { 0x8901, 0x041f, 0x7028 },
+  { 0x9501, 0x039f, 0x6000 },
+  { 0x8701, 0x038e, 0x5000 },
+  { 0x8701, 0x0386, 0x4000 },
+  { 0x8701, 0x0382, 0x3000 },
+  { 0x8701, 0x0380, 0x2000 },
+  { 0x0e01, 0x034a, 0x0000 },
+  { 0x0701, 0x0381, 0x0000 },
+  { 0x8701, 0x0384, 0x2000 },
+  { 0x0701, 0x0383, 0x0000 },
+  { 0x0701, 0x0385, 0x0000 },
+  { 0x8701, 0x038a, 0x3000 },
+  { 0x8701, 0x0388, 0x2000 },
+  { 0x0701, 0x0387, 0x0000 },
+  { 0x0701, 0x0389, 0x0000 },
+  { 0x8701, 0x038c, 0x2000 },
+  { 0x0701, 0x038b, 0x0000 },
+  { 0x0701, 0x038d, 0x0000 },
+  { 0x8701, 0x0396, 0x4000 },
+  { 0x8701, 0x0392, 0x3000 },
+  { 0x8701, 0x0390, 0x2000 },
+  { 0x0701, 0x038f, 0x0000 },
+  { 0x0701, 0x0391, 0x0000 },
+  { 0x8701, 0x0394, 0x2000 },
+  { 0x0701, 0x0393, 0x0000 },
+  { 0x0701, 0x0395, 0x0000 },
+  { 0x8701, 0x039a, 0x3000 },
+  { 0x8701, 0x0398, 0x2000 },
+  { 0x0701, 0x0397, 0x0000 },
+  { 0x0701, 0x0399, 0x0000 },
+  { 0x8701, 0x039c, 0x2000 },
+  { 0x0701, 0x039b, 0x0000 },
+  { 0x0701, 0x039d, 0x0000 },
+  { 0x8901, 0x040f, 0x5028 },
+  { 0x8901, 0x0407, 0x4028 },
+  { 0x8901, 0x0403, 0x3028 },
+  { 0x8901, 0x0401, 0x2028 },
+  { 0x0901, 0x0400, 0x0028 },
+  { 0x0901, 0x0402, 0x0028 },
+  { 0x8901, 0x0405, 0x2028 },
+  { 0x0901, 0x0404, 0x0028 },
+  { 0x0901, 0x0406, 0x0028 },
+  { 0x8901, 0x040b, 0x3028 },
+  { 0x8901, 0x0409, 0x2028 },
+  { 0x0901, 0x0408, 0x0028 },
+  { 0x0901, 0x040a, 0x0028 },
+  { 0x8901, 0x040d, 0x2028 },
+  { 0x0901, 0x040c, 0x0028 },
+  { 0x0901, 0x040e, 0x0028 },
+  { 0x8901, 0x0417, 0x4028 },
+  { 0x8901, 0x0413, 0x3028 },
+  { 0x8901, 0x0411, 0x2028 },
+  { 0x0901, 0x0410, 0x0028 },
+  { 0x0901, 0x0412, 0x0028 },
+  { 0x8901, 0x0415, 0x2028 },
+  { 0x0901, 0x0414, 0x0028 },
+  { 0x0901, 0x0416, 0x0028 },
+  { 0x8901, 0x041b, 0x3028 },
+  { 0x8901, 0x0419, 0x2028 },
+  { 0x0901, 0x0418, 0x0028 },
+  { 0x0901, 0x041a, 0x0028 },
+  { 0x8901, 0x041d, 0x2028 },
+  { 0x0901, 0x041c, 0x0028 },
+  { 0x0901, 0x041e, 0x0028 },
+  { 0x8501, 0x043f, 0x6fd8 },
+  { 0x8501, 0x042f, 0x5fd8 },
+  { 0x8901, 0x0427, 0x4028 },
+  { 0x8901, 0x0423, 0x3028 },
+  { 0x8901, 0x0421, 0x2028 },
+  { 0x0901, 0x0420, 0x0028 },
+  { 0x0901, 0x0422, 0x0028 },
+  { 0x8901, 0x0425, 0x2028 },
+  { 0x0901, 0x0424, 0x0028 },
+  { 0x0901, 0x0426, 0x0028 },
+  { 0x8501, 0x042b, 0x3fd8 },
+  { 0x8501, 0x0429, 0x2fd8 },
+  { 0x0501, 0x0428, 0x0fd8 },
+  { 0x0501, 0x042a, 0x0fd8 },
+  { 0x8501, 0x042d, 0x2fd8 },
+  { 0x0501, 0x042c, 0x0fd8 },
+  { 0x0501, 0x042e, 0x0fd8 },
+  { 0x8501, 0x0437, 0x4fd8 },
+  { 0x8501, 0x0433, 0x3fd8 },
+  { 0x8501, 0x0431, 0x2fd8 },
+  { 0x0501, 0x0430, 0x0fd8 },
+  { 0x0501, 0x0432, 0x0fd8 },
+  { 0x8501, 0x0435, 0x2fd8 },
+  { 0x0501, 0x0434, 0x0fd8 },
+  { 0x0501, 0x0436, 0x0fd8 },
+  { 0x8501, 0x043b, 0x3fd8 },
+  { 0x8501, 0x0439, 0x2fd8 },
+  { 0x0501, 0x0438, 0x0fd8 },
+  { 0x0501, 0x043a, 0x0fd8 },
+  { 0x8501, 0x043d, 0x2fd8 },
+  { 0x0501, 0x043c, 0x0fd8 },
+  { 0x0501, 0x043e, 0x0fd8 },
+  { 0x8501, 0x044f, 0x5fd8 },
+  { 0x8501, 0x0447, 0x4fd8 },
+  { 0x8501, 0x0443, 0x3fd8 },
+  { 0x8501, 0x0441, 0x2fd8 },
+  { 0x0501, 0x0440, 0x0fd8 },
+  { 0x0501, 0x0442, 0x0fd8 },
+  { 0x8501, 0x0445, 0x2fd8 },
+  { 0x0501, 0x0444, 0x0fd8 },
+  { 0x0501, 0x0446, 0x0fd8 },
+  { 0x8501, 0x044b, 0x3fd8 },
+  { 0x8501, 0x0449, 0x2fd8 },
+  { 0x0501, 0x0448, 0x0fd8 },
+  { 0x0501, 0x044a, 0x0fd8 },
+  { 0x8501, 0x044d, 0x2fd8 },
+  { 0x0501, 0x044c, 0x0fd8 },
+  { 0x0501, 0x044e, 0x0fd8 },
+  { 0x8701, 0x0457, 0x4000 },
+  { 0x8701, 0x0453, 0x3000 },
+  { 0x8701, 0x0451, 0x2000 },
+  { 0x0701, 0x0450, 0x0000 },
+  { 0x0701, 0x0452, 0x0000 },
+  { 0x8701, 0x0455, 0x2000 },
+  { 0x0701, 0x0454, 0x0000 },
+  { 0x0701, 0x0456, 0x0000 },
+  { 0x8701, 0x045b, 0x3000 },
+  { 0x8701, 0x0459, 0x2000 },
+  { 0x0701, 0x0458, 0x0000 },
+  { 0x0701, 0x045a, 0x0000 },
+  { 0x8701, 0x045d, 0x2000 },
+  { 0x0701, 0x045c, 0x0000 },
+  { 0x0701, 0x045e, 0x0000 },
+  { 0x9a01, 0xd000, 0x8000 },
+  { 0x8d01, 0x04a1, 0x7000 },
+  { 0x8701, 0x047f, 0x6000 },
+  { 0x8701, 0x046f, 0x5000 },
+  { 0x8701, 0x0467, 0x4000 },
+  { 0x8701, 0x0463, 0x3000 },
+  { 0x8701, 0x0461, 0x2000 },
+  { 0x0701, 0x0460, 0x0000 },
+  { 0x0701, 0x0462, 0x0000 },
+  { 0x8701, 0x0465, 0x2000 },
+  { 0x0701, 0x0464, 0x0000 },
+  { 0x0701, 0x0466, 0x0000 },
+  { 0x8701, 0x046b, 0x3000 },
+  { 0x8701, 0x0469, 0x2000 },
+  { 0x0701, 0x0468, 0x0000 },
+  { 0x0701, 0x046a, 0x0000 },
+  { 0x8701, 0x046d, 0x2000 },
+  { 0x0701, 0x046c, 0x0000 },
+  { 0x0701, 0x046e, 0x0000 },
+  { 0x8701, 0x0477, 0x4000 },
+  { 0x8701, 0x0473, 0x3000 },
+  { 0x8701, 0x0471, 0x2000 },
+  { 0x0701, 0x0470, 0x0000 },
+  { 0x0701, 0x0472, 0x0000 },
+  { 0x8701, 0x0475, 0x2000 },
+  { 0x0701, 0x0474, 0x0000 },
+  { 0x0701, 0x0476, 0x0000 },
+  { 0x8701, 0x047b, 0x3000 },
+  { 0x8701, 0x0479, 0x2000 },
+  { 0x0701, 0x0478, 0x0000 },
+  { 0x0701, 0x047a, 0x0000 },
+  { 0x8701, 0x047d, 0x2000 },
+  { 0x0701, 0x047c, 0x0000 },
+  { 0x0701, 0x047e, 0x0000 },
+  { 0x8701, 0x048f, 0x5000 },
+  { 0x8701, 0x0487, 0x4000 },
+  { 0x8701, 0x0483, 0x3000 },
+  { 0x8701, 0x0481, 0x2000 },
+  { 0x0701, 0x0480, 0x0000 },
+  { 0x0701, 0x0482, 0x0000 },
+  { 0x8701, 0x0485, 0x2000 },
+  { 0x0701, 0x0484, 0x0000 },
+  { 0x0701, 0x0486, 0x0000 },
+  { 0x8701, 0x048b, 0x3000 },
+  { 0x8701, 0x0489, 0x2000 },
+  { 0x0701, 0x0488, 0x0000 },
+  { 0x0701, 0x048a, 0x0000 },
+  { 0x8701, 0x048d, 0x2000 },
+  { 0x0701, 0x048c, 0x0000 },
+  { 0x0701, 0x048e, 0x0000 },
+  { 0x8701, 0x0497, 0x4000 },
+  { 0x8701, 0x0493, 0x3000 },
+  { 0x8701, 0x0491, 0x2000 },
+  { 0x0701, 0x0490, 0x0000 },
+  { 0x0701, 0x0492, 0x0000 },
+  { 0x8701, 0x0495, 0x2000 },
+  { 0x0701, 0x0494, 0x0000 },
+  { 0x0701, 0x0496, 0x0000 },
+  { 0x8701, 0x049b, 0x3000 },
+  { 0x8701, 0x0499, 0x2000 },
+  { 0x0701, 0x0498, 0x0000 },
+  { 0x0701, 0x049a, 0x0000 },
+  { 0x8701, 0x049d, 0x2000 },
+  { 0x0701, 0x049c, 0x0000 },
+  { 0x0d01, 0x04a0, 0x0000 },
+  { 0x8701, 0x081a, 0x6000 },
+  { 0x8701, 0x080a, 0x5000 },
+  { 0x8d01, 0x04a9, 0x4000 },
+  { 0x8d01, 0x04a5, 0x3000 },
+  { 0x8d01, 0x04a3, 0x2000 },
+  { 0x0d01, 0x04a2, 0x0000 },
+  { 0x0d01, 0x04a4, 0x0000 },
+  { 0x8d01, 0x04a7, 0x2000 },
+  { 0x0d01, 0x04a6, 0x0000 },
+  { 0x0d01, 0x04a8, 0x0000 },
+  { 0x8701, 0x0803, 0x3000 },
+  { 0x8701, 0x0801, 0x2000 },
+  { 0x0701, 0x0800, 0x0000 },
+  { 0x0701, 0x0802, 0x0000 },
+  { 0x8701, 0x0805, 0x2000 },
+  { 0x0701, 0x0804, 0x0000 },
+  { 0x0701, 0x0808, 0x0000 },
+  { 0x8701, 0x0812, 0x4000 },
+  { 0x8701, 0x080e, 0x3000 },
+  { 0x8701, 0x080c, 0x2000 },
+  { 0x0701, 0x080b, 0x0000 },
+  { 0x0701, 0x080d, 0x0000 },
+  { 0x8701, 0x0810, 0x2000 },
+  { 0x0701, 0x080f, 0x0000 },
+  { 0x0701, 0x0811, 0x0000 },
+  { 0x8701, 0x0816, 0x3000 },
+  { 0x8701, 0x0814, 0x2000 },
+  { 0x0701, 0x0813, 0x0000 },
+  { 0x0701, 0x0815, 0x0000 },
+  { 0x8701, 0x0818, 0x2000 },
+  { 0x0701, 0x0817, 0x0000 },
+  { 0x0701, 0x0819, 0x0000 },
+  { 0x8701, 0x082a, 0x5000 },
+  { 0x8701, 0x0822, 0x4000 },
+  { 0x8701, 0x081e, 0x3000 },
+  { 0x8701, 0x081c, 0x2000 },
+  { 0x0701, 0x081b, 0x0000 },
+  { 0x0701, 0x081d, 0x0000 },
+  { 0x8701, 0x0820, 0x2000 },
+  { 0x0701, 0x081f, 0x0000 },
+  { 0x0701, 0x0821, 0x0000 },
+  { 0x8701, 0x0826, 0x3000 },
+  { 0x8701, 0x0824, 0x2000 },
+  { 0x0701, 0x0823, 0x0000 },
+  { 0x0701, 0x0825, 0x0000 },
+  { 0x8701, 0x0828, 0x2000 },
+  { 0x0701, 0x0827, 0x0000 },
+  { 0x0701, 0x0829, 0x0000 },
+  { 0x8701, 0x0832, 0x4000 },
+  { 0x8701, 0x082e, 0x3000 },
+  { 0x8701, 0x082c, 0x2000 },
+  { 0x0701, 0x082b, 0x0000 },
+  { 0x0701, 0x082d, 0x0000 },
+  { 0x8701, 0x0830, 0x2000 },
+  { 0x0701, 0x082f, 0x0000 },
+  { 0x0701, 0x0831, 0x0000 },
+  { 0x8701, 0x0837, 0x3000 },
+  { 0x8701, 0x0834, 0x2000 },
+  { 0x0701, 0x0833, 0x0000 },
+  { 0x0701, 0x0835, 0x0000 },
+  { 0x8701, 0x083c, 0x2000 },
+  { 0x0701, 0x0838, 0x0000 },
+  { 0x0701, 0x083f, 0x0000 },
+  { 0x9a01, 0xd040, 0x7000 },
+  { 0x9a01, 0xd020, 0x6000 },
+  { 0x9a01, 0xd010, 0x5000 },
+  { 0x9a01, 0xd008, 0x4000 },
+  { 0x9a01, 0xd004, 0x3000 },
+  { 0x9a01, 0xd002, 0x2000 },
+  { 0x1a01, 0xd001, 0x0000 },
+  { 0x1a01, 0xd003, 0x0000 },
+  { 0x9a01, 0xd006, 0x2000 },
+  { 0x1a01, 0xd005, 0x0000 },
+  { 0x1a01, 0xd007, 0x0000 },
+  { 0x9a01, 0xd00c, 0x3000 },
+  { 0x9a01, 0xd00a, 0x2000 },
+  { 0x1a01, 0xd009, 0x0000 },
+  { 0x1a01, 0xd00b, 0x0000 },
+  { 0x9a01, 0xd00e, 0x2000 },
+  { 0x1a01, 0xd00d, 0x0000 },
+  { 0x1a01, 0xd00f, 0x0000 },
+  { 0x9a01, 0xd018, 0x4000 },
+  { 0x9a01, 0xd014, 0x3000 },
+  { 0x9a01, 0xd012, 0x2000 },
+  { 0x1a01, 0xd011, 0x0000 },
+  { 0x1a01, 0xd013, 0x0000 },
+  { 0x9a01, 0xd016, 0x2000 },
+  { 0x1a01, 0xd015, 0x0000 },
+  { 0x1a01, 0xd017, 0x0000 },
+  { 0x9a01, 0xd01c, 0x3000 },
+  { 0x9a01, 0xd01a, 0x2000 },
+  { 0x1a01, 0xd019, 0x0000 },
+  { 0x1a01, 0xd01b, 0x0000 },
+  { 0x9a01, 0xd01e, 0x2000 },
+  { 0x1a01, 0xd01d, 0x0000 },
+  { 0x1a01, 0xd01f, 0x0000 },
+  { 0x9a01, 0xd030, 0x5000 },
+  { 0x9a01, 0xd028, 0x4000 },
+  { 0x9a01, 0xd024, 0x3000 },
+  { 0x9a01, 0xd022, 0x2000 },
+  { 0x1a01, 0xd021, 0x0000 },
+  { 0x1a01, 0xd023, 0x0000 },
+  { 0x9a01, 0xd026, 0x2000 },
+  { 0x1a01, 0xd025, 0x0000 },
+  { 0x1a01, 0xd027, 0x0000 },
+  { 0x9a01, 0xd02c, 0x3000 },
+  { 0x9a01, 0xd02a, 0x2000 },
+  { 0x1a01, 0xd029, 0x0000 },
+  { 0x1a01, 0xd02b, 0x0000 },
+  { 0x9a01, 0xd02e, 0x2000 },
+  { 0x1a01, 0xd02d, 0x0000 },
+  { 0x1a01, 0xd02f, 0x0000 },
+  { 0x9a01, 0xd038, 0x4000 },
+  { 0x9a01, 0xd034, 0x3000 },
+  { 0x9a01, 0xd032, 0x2000 },
+  { 0x1a01, 0xd031, 0x0000 },
+  { 0x1a01, 0xd033, 0x0000 },
+  { 0x9a01, 0xd036, 0x2000 },
+  { 0x1a01, 0xd035, 0x0000 },
+  { 0x1a01, 0xd037, 0x0000 },
+  { 0x9a01, 0xd03c, 0x3000 },
+  { 0x9a01, 0xd03a, 0x2000 },
+  { 0x1a01, 0xd039, 0x0000 },
+  { 0x1a01, 0xd03b, 0x0000 },
+  { 0x9a01, 0xd03e, 0x2000 },
+  { 0x1a01, 0xd03d, 0x0000 },
+  { 0x1a01, 0xd03f, 0x0000 },
+  { 0x9a01, 0xd060, 0x6000 },
+  { 0x9a01, 0xd050, 0x5000 },
+  { 0x9a01, 0xd048, 0x4000 },
+  { 0x9a01, 0xd044, 0x3000 },
+  { 0x9a01, 0xd042, 0x2000 },
+  { 0x1a01, 0xd041, 0x0000 },
+  { 0x1a01, 0xd043, 0x0000 },
+  { 0x9a01, 0xd046, 0x2000 },
+  { 0x1a01, 0xd045, 0x0000 },
+  { 0x1a01, 0xd047, 0x0000 },
+  { 0x9a01, 0xd04c, 0x3000 },
+  { 0x9a01, 0xd04a, 0x2000 },
+  { 0x1a01, 0xd049, 0x0000 },
+  { 0x1a01, 0xd04b, 0x0000 },
+  { 0x9a01, 0xd04e, 0x2000 },
+  { 0x1a01, 0xd04d, 0x0000 },
+  { 0x1a01, 0xd04f, 0x0000 },
+  { 0x9a01, 0xd058, 0x4000 },
+  { 0x9a01, 0xd054, 0x3000 },
+  { 0x9a01, 0xd052, 0x2000 },
+  { 0x1a01, 0xd051, 0x0000 },
+  { 0x1a01, 0xd053, 0x0000 },
+  { 0x9a01, 0xd056, 0x2000 },
+  { 0x1a01, 0xd055, 0x0000 },
+  { 0x1a01, 0xd057, 0x0000 },
+  { 0x9a01, 0xd05c, 0x3000 },
+  { 0x9a01, 0xd05a, 0x2000 },
+  { 0x1a01, 0xd059, 0x0000 },
+  { 0x1a01, 0xd05b, 0x0000 },
+  { 0x9a01, 0xd05e, 0x2000 },
+  { 0x1a01, 0xd05d, 0x0000 },
+  { 0x1a01, 0xd05f, 0x0000 },
+  { 0x9a01, 0xd070, 0x5000 },
+  { 0x9a01, 0xd068, 0x4000 },
+  { 0x9a01, 0xd064, 0x3000 },
+  { 0x9a01, 0xd062, 0x2000 },
+  { 0x1a01, 0xd061, 0x0000 },
+  { 0x1a01, 0xd063, 0x0000 },
+  { 0x9a01, 0xd066, 0x2000 },
+  { 0x1a01, 0xd065, 0x0000 },
+  { 0x1a01, 0xd067, 0x0000 },
+  { 0x9a01, 0xd06c, 0x3000 },
+  { 0x9a01, 0xd06a, 0x2000 },
+  { 0x1a01, 0xd069, 0x0000 },
+  { 0x1a01, 0xd06b, 0x0000 },
+  { 0x9a01, 0xd06e, 0x2000 },
+  { 0x1a01, 0xd06d, 0x0000 },
+  { 0x1a01, 0xd06f, 0x0000 },
+  { 0x9a01, 0xd078, 0x4000 },
+  { 0x9a01, 0xd074, 0x3000 },
+  { 0x9a01, 0xd072, 0x2000 },
+  { 0x1a01, 0xd071, 0x0000 },
+  { 0x1a01, 0xd073, 0x0000 },
+  { 0x9a01, 0xd076, 0x2000 },
+  { 0x1a01, 0xd075, 0x0000 },
+  { 0x1a01, 0xd077, 0x0000 },
+  { 0x9a01, 0xd07c, 0x3000 },
+  { 0x9a01, 0xd07a, 0x2000 },
+  { 0x1a01, 0xd079, 0x0000 },
+  { 0x1a01, 0xd07b, 0x0000 },
+  { 0x9a01, 0xd07e, 0x2000 },
+  { 0x1a01, 0xd07d, 0x0000 },
+  { 0x1a01, 0xd07f, 0x0000 },
+  { 0x9a01, 0xd18d, 0x9000 },
+  { 0x9a01, 0xd10a, 0x8000 },
+  { 0x9a01, 0xd0c0, 0x7000 },
+  { 0x9a01, 0xd0a0, 0x6000 },
+  { 0x9a01, 0xd090, 0x5000 },
+  { 0x9a01, 0xd088, 0x4000 },
+  { 0x9a01, 0xd084, 0x3000 },
+  { 0x9a01, 0xd082, 0x2000 },
+  { 0x1a01, 0xd081, 0x0000 },
+  { 0x1a01, 0xd083, 0x0000 },
+  { 0x9a01, 0xd086, 0x2000 },
+  { 0x1a01, 0xd085, 0x0000 },
+  { 0x1a01, 0xd087, 0x0000 },
+  { 0x9a01, 0xd08c, 0x3000 },
+  { 0x9a01, 0xd08a, 0x2000 },
+  { 0x1a01, 0xd089, 0x0000 },
+  { 0x1a01, 0xd08b, 0x0000 },
+  { 0x9a01, 0xd08e, 0x2000 },
+  { 0x1a01, 0xd08d, 0x0000 },
+  { 0x1a01, 0xd08f, 0x0000 },
+  { 0x9a01, 0xd098, 0x4000 },
+  { 0x9a01, 0xd094, 0x3000 },
+  { 0x9a01, 0xd092, 0x2000 },
+  { 0x1a01, 0xd091, 0x0000 },
+  { 0x1a01, 0xd093, 0x0000 },
+  { 0x9a01, 0xd096, 0x2000 },
+  { 0x1a01, 0xd095, 0x0000 },
+  { 0x1a01, 0xd097, 0x0000 },
+  { 0x9a01, 0xd09c, 0x3000 },
+  { 0x9a01, 0xd09a, 0x2000 },
+  { 0x1a01, 0xd099, 0x0000 },
+  { 0x1a01, 0xd09b, 0x0000 },
+  { 0x9a01, 0xd09e, 0x2000 },
+  { 0x1a01, 0xd09d, 0x0000 },
+  { 0x1a01, 0xd09f, 0x0000 },
+  { 0x9a01, 0xd0b0, 0x5000 },
+  { 0x9a01, 0xd0a8, 0x4000 },
+  { 0x9a01, 0xd0a4, 0x3000 },
+  { 0x9a01, 0xd0a2, 0x2000 },
+  { 0x1a01, 0xd0a1, 0x0000 },
+  { 0x1a01, 0xd0a3, 0x0000 },
+  { 0x9a01, 0xd0a6, 0x2000 },
+  { 0x1a01, 0xd0a5, 0x0000 },
+  { 0x1a01, 0xd0a7, 0x0000 },
+  { 0x9a01, 0xd0ac, 0x3000 },
+  { 0x9a01, 0xd0aa, 0x2000 },
+  { 0x1a01, 0xd0a9, 0x0000 },
+  { 0x1a01, 0xd0ab, 0x0000 },
+  { 0x9a01, 0xd0ae, 0x2000 },
+  { 0x1a01, 0xd0ad, 0x0000 },
+  { 0x1a01, 0xd0af, 0x0000 },
+  { 0x9a01, 0xd0b8, 0x4000 },
+  { 0x9a01, 0xd0b4, 0x3000 },
+  { 0x9a01, 0xd0b2, 0x2000 },
+  { 0x1a01, 0xd0b1, 0x0000 },
+  { 0x1a01, 0xd0b3, 0x0000 },
+  { 0x9a01, 0xd0b6, 0x2000 },
+  { 0x1a01, 0xd0b5, 0x0000 },
+  { 0x1a01, 0xd0b7, 0x0000 },
+  { 0x9a01, 0xd0bc, 0x3000 },
+  { 0x9a01, 0xd0ba, 0x2000 },
+  { 0x1a01, 0xd0b9, 0x0000 },
+  { 0x1a01, 0xd0bb, 0x0000 },
+  { 0x9a01, 0xd0be, 0x2000 },
+  { 0x1a01, 0xd0bd, 0x0000 },
+  { 0x1a01, 0xd0bf, 0x0000 },
+  { 0x9a01, 0xd0e0, 0x6000 },
+  { 0x9a01, 0xd0d0, 0x5000 },
+  { 0x9a01, 0xd0c8, 0x4000 },
+  { 0x9a01, 0xd0c4, 0x3000 },
+  { 0x9a01, 0xd0c2, 0x2000 },
+  { 0x1a01, 0xd0c1, 0x0000 },
+  { 0x1a01, 0xd0c3, 0x0000 },
+  { 0x9a01, 0xd0c6, 0x2000 },
+  { 0x1a01, 0xd0c5, 0x0000 },
+  { 0x1a01, 0xd0c7, 0x0000 },
+  { 0x9a01, 0xd0cc, 0x3000 },
+  { 0x9a01, 0xd0ca, 0x2000 },
+  { 0x1a01, 0xd0c9, 0x0000 },
+  { 0x1a01, 0xd0cb, 0x0000 },
+  { 0x9a01, 0xd0ce, 0x2000 },
+  { 0x1a01, 0xd0cd, 0x0000 },
+  { 0x1a01, 0xd0cf, 0x0000 },
+  { 0x9a01, 0xd0d8, 0x4000 },
+  { 0x9a01, 0xd0d4, 0x3000 },
+  { 0x9a01, 0xd0d2, 0x2000 },
+  { 0x1a01, 0xd0d1, 0x0000 },
+  { 0x1a01, 0xd0d3, 0x0000 },
+  { 0x9a01, 0xd0d6, 0x2000 },
+  { 0x1a01, 0xd0d5, 0x0000 },
+  { 0x1a01, 0xd0d7, 0x0000 },
+  { 0x9a01, 0xd0dc, 0x3000 },
+  { 0x9a01, 0xd0da, 0x2000 },
+  { 0x1a01, 0xd0d9, 0x0000 },
+  { 0x1a01, 0xd0db, 0x0000 },
+  { 0x9a01, 0xd0de, 0x2000 },
+  { 0x1a01, 0xd0dd, 0x0000 },
+  { 0x1a01, 0xd0df, 0x0000 },
+  { 0x9a01, 0xd0f0, 0x5000 },
+  { 0x9a01, 0xd0e8, 0x4000 },
+  { 0x9a01, 0xd0e4, 0x3000 },
+  { 0x9a01, 0xd0e2, 0x2000 },
+  { 0x1a01, 0xd0e1, 0x0000 },
+  { 0x1a01, 0xd0e3, 0x0000 },
+  { 0x9a01, 0xd0e6, 0x2000 },
+  { 0x1a01, 0xd0e5, 0x0000 },
+  { 0x1a01, 0xd0e7, 0x0000 },
+  { 0x9a01, 0xd0ec, 0x3000 },
+  { 0x9a01, 0xd0ea, 0x2000 },
+  { 0x1a01, 0xd0e9, 0x0000 },
+  { 0x1a01, 0xd0eb, 0x0000 },
+  { 0x9a01, 0xd0ee, 0x2000 },
+  { 0x1a01, 0xd0ed, 0x0000 },
+  { 0x1a01, 0xd0ef, 0x0000 },
+  { 0x9a01, 0xd102, 0x4000 },
+  { 0x9a01, 0xd0f4, 0x3000 },
+  { 0x9a01, 0xd0f2, 0x2000 },
+  { 0x1a01, 0xd0f1, 0x0000 },
+  { 0x1a01, 0xd0f3, 0x0000 },
+  { 0x9a01, 0xd100, 0x2000 },
+  { 0x1a01, 0xd0f5, 0x0000 },
+  { 0x1a01, 0xd101, 0x0000 },
+  { 0x9a01, 0xd106, 0x3000 },
+  { 0x9a01, 0xd104, 0x2000 },
+  { 0x1a01, 0xd103, 0x0000 },
+  { 0x1a01, 0xd105, 0x0000 },
+  { 0x9a01, 0xd108, 0x2000 },
+  { 0x1a01, 0xd107, 0x0000 },
+  { 0x1a01, 0xd109, 0x0000 },
+  { 0x9a01, 0xd14d, 0x7000 },
+  { 0x9a01, 0xd12d, 0x6000 },
+  { 0x9a01, 0xd11a, 0x5000 },
+  { 0x9a01, 0xd112, 0x4000 },
+  { 0x9a01, 0xd10e, 0x3000 },
+  { 0x9a01, 0xd10c, 0x2000 },
+  { 0x1a01, 0xd10b, 0x0000 },
+  { 0x1a01, 0xd10d, 0x0000 },
+  { 0x9a01, 0xd110, 0x2000 },
+  { 0x1a01, 0xd10f, 0x0000 },
+  { 0x1a01, 0xd111, 0x0000 },
+  { 0x9a01, 0xd116, 0x3000 },
+  { 0x9a01, 0xd114, 0x2000 },
+  { 0x1a01, 0xd113, 0x0000 },
+  { 0x1a01, 0xd115, 0x0000 },
+  { 0x9a01, 0xd118, 0x2000 },
+  { 0x1a01, 0xd117, 0x0000 },
+  { 0x1a01, 0xd119, 0x0000 },
+  { 0x9a01, 0xd122, 0x4000 },
+  { 0x9a01, 0xd11e, 0x3000 },
+  { 0x9a01, 0xd11c, 0x2000 },
+  { 0x1a01, 0xd11b, 0x0000 },
+  { 0x1a01, 0xd11d, 0x0000 },
+  { 0x9a01, 0xd120, 0x2000 },
+  { 0x1a01, 0xd11f, 0x0000 },
+  { 0x1a01, 0xd121, 0x0000 },
+  { 0x9a01, 0xd126, 0x3000 },
+  { 0x9a01, 0xd124, 0x2000 },
+  { 0x1a01, 0xd123, 0x0000 },
+  { 0x1a01, 0xd125, 0x0000 },
+  { 0x9a01, 0xd12b, 0x2000 },
+  { 0x1a01, 0xd12a, 0x0000 },
+  { 0x1a01, 0xd12c, 0x0000 },
+  { 0x9a01, 0xd13d, 0x5000 },
+  { 0x9a01, 0xd135, 0x4000 },
+  { 0x9a01, 0xd131, 0x3000 },
+  { 0x9a01, 0xd12f, 0x2000 },
+  { 0x1a01, 0xd12e, 0x0000 },
+  { 0x1a01, 0xd130, 0x0000 },
+  { 0x9a01, 0xd133, 0x2000 },
+  { 0x1a01, 0xd132, 0x0000 },
+  { 0x1a01, 0xd134, 0x0000 },
+  { 0x9a01, 0xd139, 0x3000 },
+  { 0x9a01, 0xd137, 0x2000 },
+  { 0x1a01, 0xd136, 0x0000 },
+  { 0x1a01, 0xd138, 0x0000 },
+  { 0x9a01, 0xd13b, 0x2000 },
+  { 0x1a01, 0xd13a, 0x0000 },
+  { 0x1a01, 0xd13c, 0x0000 },
+  { 0x9a01, 0xd145, 0x4000 },
+  { 0x9a01, 0xd141, 0x3000 },
+  { 0x9a01, 0xd13f, 0x2000 },
+  { 0x1a01, 0xd13e, 0x0000 },
+  { 0x1a01, 0xd140, 0x0000 },
+  { 0x9a01, 0xd143, 0x2000 },
+  { 0x1a01, 0xd142, 0x0000 },
+  { 0x1a01, 0xd144, 0x0000 },
+  { 0x9a01, 0xd149, 0x3000 },
+  { 0x9a01, 0xd147, 0x2000 },
+  { 0x1a01, 0xd146, 0x0000 },
+  { 0x1a01, 0xd148, 0x0000 },
+  { 0x9a01, 0xd14b, 0x2000 },
+  { 0x1a01, 0xd14a, 0x0000 },
+  { 0x1a01, 0xd14c, 0x0000 },
+  { 0x8a01, 0xd16d, 0x6000 },
+  { 0x9a01, 0xd15d, 0x5000 },
+  { 0x9a01, 0xd155, 0x4000 },
+  { 0x9a01, 0xd151, 0x3000 },
+  { 0x9a01, 0xd14f, 0x2000 },
+  { 0x1a01, 0xd14e, 0x0000 },
+  { 0x1a01, 0xd150, 0x0000 },
+  { 0x9a01, 0xd153, 0x2000 },
+  { 0x1a01, 0xd152, 0x0000 },
+  { 0x1a01, 0xd154, 0x0000 },
+  { 0x9a01, 0xd159, 0x3000 },
+  { 0x9a01, 0xd157, 0x2000 },
+  { 0x1a01, 0xd156, 0x0000 },
+  { 0x1a01, 0xd158, 0x0000 },
+  { 0x9a01, 0xd15b, 0x2000 },
+  { 0x1a01, 0xd15a, 0x0000 },
+  { 0x1a01, 0xd15c, 0x0000 },
+  { 0x8a01, 0xd165, 0x4000 },
+  { 0x9a01, 0xd161, 0x3000 },
+  { 0x9a01, 0xd15f, 0x2000 },
+  { 0x1a01, 0xd15e, 0x0000 },
+  { 0x1a01, 0xd160, 0x0000 },
+  { 0x9a01, 0xd163, 0x2000 },
+  { 0x1a01, 0xd162, 0x0000 },
+  { 0x1a01, 0xd164, 0x0000 },
+  { 0x8c01, 0xd169, 0x3000 },
+  { 0x8c01, 0xd167, 0x2000 },
+  { 0x0a01, 0xd166, 0x0000 },
+  { 0x0c01, 0xd168, 0x0000 },
+  { 0x9a01, 0xd16b, 0x2000 },
+  { 0x1a01, 0xd16a, 0x0000 },
+  { 0x1a01, 0xd16c, 0x0000 },
+  { 0x8c01, 0xd17d, 0x5000 },
+  { 0x8101, 0xd175, 0x4000 },
+  { 0x8a01, 0xd171, 0x3000 },
+  { 0x8a01, 0xd16f, 0x2000 },
+  { 0x0a01, 0xd16e, 0x0000 },
+  { 0x0a01, 0xd170, 0x0000 },
+  { 0x8101, 0xd173, 0x2000 },
+  { 0x0a01, 0xd172, 0x0000 },
+  { 0x0101, 0xd174, 0x0000 },
+  { 0x8101, 0xd179, 0x3000 },
+  { 0x8101, 0xd177, 0x2000 },
+  { 0x0101, 0xd176, 0x0000 },
+  { 0x0101, 0xd178, 0x0000 },
+  { 0x8c01, 0xd17b, 0x2000 },
+  { 0x0101, 0xd17a, 0x0000 },
+  { 0x0c01, 0xd17c, 0x0000 },
+  { 0x8c01, 0xd185, 0x4000 },
+  { 0x8c01, 0xd181, 0x3000 },
+  { 0x8c01, 0xd17f, 0x2000 },
+  { 0x0c01, 0xd17e, 0x0000 },
+  { 0x0c01, 0xd180, 0x0000 },
+  { 0x9a01, 0xd183, 0x2000 },
+  { 0x0c01, 0xd182, 0x0000 },
+  { 0x1a01, 0xd184, 0x0000 },
+  { 0x8c01, 0xd189, 0x3000 },
+  { 0x8c01, 0xd187, 0x2000 },
+  { 0x0c01, 0xd186, 0x0000 },
+  { 0x0c01, 0xd188, 0x0000 },
+  { 0x8c01, 0xd18b, 0x2000 },
+  { 0x0c01, 0xd18a, 0x0000 },
+  { 0x1a01, 0xd18c, 0x0000 },
+  { 0x9a01, 0xd32f, 0x8000 },
+  { 0x9a01, 0xd1cd, 0x7000 },
+  { 0x8c01, 0xd1ad, 0x6000 },
+  { 0x9a01, 0xd19d, 0x5000 },
+  { 0x9a01, 0xd195, 0x4000 },
+  { 0x9a01, 0xd191, 0x3000 },
+  { 0x9a01, 0xd18f, 0x2000 },
+  { 0x1a01, 0xd18e, 0x0000 },
+  { 0x1a01, 0xd190, 0x0000 },
+  { 0x9a01, 0xd193, 0x2000 },
+  { 0x1a01, 0xd192, 0x0000 },
+  { 0x1a01, 0xd194, 0x0000 },
+  { 0x9a01, 0xd199, 0x3000 },
+  { 0x9a01, 0xd197, 0x2000 },
+  { 0x1a01, 0xd196, 0x0000 },
+  { 0x1a01, 0xd198, 0x0000 },
+  { 0x9a01, 0xd19b, 0x2000 },
+  { 0x1a01, 0xd19a, 0x0000 },
+  { 0x1a01, 0xd19c, 0x0000 },
+  { 0x9a01, 0xd1a5, 0x4000 },
+  { 0x9a01, 0xd1a1, 0x3000 },
+  { 0x9a01, 0xd19f, 0x2000 },
+  { 0x1a01, 0xd19e, 0x0000 },
+  { 0x1a01, 0xd1a0, 0x0000 },
+  { 0x9a01, 0xd1a3, 0x2000 },
+  { 0x1a01, 0xd1a2, 0x0000 },
+  { 0x1a01, 0xd1a4, 0x0000 },
+  { 0x9a01, 0xd1a9, 0x3000 },
+  { 0x9a01, 0xd1a7, 0x2000 },
+  { 0x1a01, 0xd1a6, 0x0000 },
+  { 0x1a01, 0xd1a8, 0x0000 },
+  { 0x8c01, 0xd1ab, 0x2000 },
+  { 0x0c01, 0xd1aa, 0x0000 },
+  { 0x0c01, 0xd1ac, 0x0000 },
+  { 0x9a01, 0xd1bd, 0x5000 },
+  { 0x9a01, 0xd1b5, 0x4000 },
+  { 0x9a01, 0xd1b1, 0x3000 },
+  { 0x9a01, 0xd1af, 0x2000 },
+  { 0x1a01, 0xd1ae, 0x0000 },
+  { 0x1a01, 0xd1b0, 0x0000 },
+  { 0x9a01, 0xd1b3, 0x2000 },
+  { 0x1a01, 0xd1b2, 0x0000 },
+  { 0x1a01, 0xd1b4, 0x0000 },
+  { 0x9a01, 0xd1b9, 0x3000 },
+  { 0x9a01, 0xd1b7, 0x2000 },
+  { 0x1a01, 0xd1b6, 0x0000 },
+  { 0x1a01, 0xd1b8, 0x0000 },
+  { 0x9a01, 0xd1bb, 0x2000 },
+  { 0x1a01, 0xd1ba, 0x0000 },
+  { 0x1a01, 0xd1bc, 0x0000 },
+  { 0x9a01, 0xd1c5, 0x4000 },
+  { 0x9a01, 0xd1c1, 0x3000 },
+  { 0x9a01, 0xd1bf, 0x2000 },
+  { 0x1a01, 0xd1be, 0x0000 },
+  { 0x1a01, 0xd1c0, 0x0000 },
+  { 0x9a01, 0xd1c3, 0x2000 },
+  { 0x1a01, 0xd1c2, 0x0000 },
+  { 0x1a01, 0xd1c4, 0x0000 },
+  { 0x9a01, 0xd1c9, 0x3000 },
+  { 0x9a01, 0xd1c7, 0x2000 },
+  { 0x1a01, 0xd1c6, 0x0000 },
+  { 0x1a01, 0xd1c8, 0x0000 },
+  { 0x9a01, 0xd1cb, 0x2000 },
+  { 0x1a01, 0xd1ca, 0x0000 },
+  { 0x1a01, 0xd1cc, 0x0000 },
+  { 0x9a01, 0xd30f, 0x6000 },
+  { 0x9a01, 0xd1dd, 0x5000 },
+  { 0x9a01, 0xd1d5, 0x4000 },
+  { 0x9a01, 0xd1d1, 0x3000 },
+  { 0x9a01, 0xd1cf, 0x2000 },
+  { 0x1a01, 0xd1ce, 0x0000 },
+  { 0x1a01, 0xd1d0, 0x0000 },
+  { 0x9a01, 0xd1d3, 0x2000 },
+  { 0x1a01, 0xd1d2, 0x0000 },
+  { 0x1a01, 0xd1d4, 0x0000 },
+  { 0x9a01, 0xd1d9, 0x3000 },
+  { 0x9a01, 0xd1d7, 0x2000 },
+  { 0x1a01, 0xd1d6, 0x0000 },
+  { 0x1a01, 0xd1d8, 0x0000 },
+  { 0x9a01, 0xd1db, 0x2000 },
+  { 0x1a01, 0xd1da, 0x0000 },
+  { 0x1a01, 0xd1dc, 0x0000 },
+  { 0x9a01, 0xd307, 0x4000 },
+  { 0x9a01, 0xd303, 0x3000 },
+  { 0x9a01, 0xd301, 0x2000 },
+  { 0x1a01, 0xd300, 0x0000 },
+  { 0x1a01, 0xd302, 0x0000 },
+  { 0x9a01, 0xd305, 0x2000 },
+  { 0x1a01, 0xd304, 0x0000 },
+  { 0x1a01, 0xd306, 0x0000 },
+  { 0x9a01, 0xd30b, 0x3000 },
+  { 0x9a01, 0xd309, 0x2000 },
+  { 0x1a01, 0xd308, 0x0000 },
+  { 0x1a01, 0xd30a, 0x0000 },
+  { 0x9a01, 0xd30d, 0x2000 },
+  { 0x1a01, 0xd30c, 0x0000 },
+  { 0x1a01, 0xd30e, 0x0000 },
+  { 0x9a01, 0xd31f, 0x5000 },
+  { 0x9a01, 0xd317, 0x4000 },
+  { 0x9a01, 0xd313, 0x3000 },
+  { 0x9a01, 0xd311, 0x2000 },
+  { 0x1a01, 0xd310, 0x0000 },
+  { 0x1a01, 0xd312, 0x0000 },
+  { 0x9a01, 0xd315, 0x2000 },
+  { 0x1a01, 0xd314, 0x0000 },
+  { 0x1a01, 0xd316, 0x0000 },
+  { 0x9a01, 0xd31b, 0x3000 },
+  { 0x9a01, 0xd319, 0x2000 },
+  { 0x1a01, 0xd318, 0x0000 },
+  { 0x1a01, 0xd31a, 0x0000 },
+  { 0x9a01, 0xd31d, 0x2000 },
+  { 0x1a01, 0xd31c, 0x0000 },
+  { 0x1a01, 0xd31e, 0x0000 },
+  { 0x9a01, 0xd327, 0x4000 },
+  { 0x9a01, 0xd323, 0x3000 },
+  { 0x9a01, 0xd321, 0x2000 },
+  { 0x1a01, 0xd320, 0x0000 },
+  { 0x1a01, 0xd322, 0x0000 },
+  { 0x9a01, 0xd325, 0x2000 },
+  { 0x1a01, 0xd324, 0x0000 },
+  { 0x1a01, 0xd326, 0x0000 },
+  { 0x9a01, 0xd32b, 0x3000 },
+  { 0x9a01, 0xd329, 0x2000 },
+  { 0x1a01, 0xd328, 0x0000 },
+  { 0x1a01, 0xd32a, 0x0000 },
+  { 0x9a01, 0xd32d, 0x2000 },
+  { 0x1a01, 0xd32c, 0x0000 },
+  { 0x1a01, 0xd32e, 0x0000 },
+  { 0x8901, 0xd418, 0x7000 },
+  { 0x9a01, 0xd34f, 0x6000 },
+  { 0x9a01, 0xd33f, 0x5000 },
+  { 0x9a01, 0xd337, 0x4000 },
+  { 0x9a01, 0xd333, 0x3000 },
+  { 0x9a01, 0xd331, 0x2000 },
+  { 0x1a01, 0xd330, 0x0000 },
+  { 0x1a01, 0xd332, 0x0000 },
+  { 0x9a01, 0xd335, 0x2000 },
+  { 0x1a01, 0xd334, 0x0000 },
+  { 0x1a01, 0xd336, 0x0000 },
+  { 0x9a01, 0xd33b, 0x3000 },
+  { 0x9a01, 0xd339, 0x2000 },
+  { 0x1a01, 0xd338, 0x0000 },
+  { 0x1a01, 0xd33a, 0x0000 },
+  { 0x9a01, 0xd33d, 0x2000 },
+  { 0x1a01, 0xd33c, 0x0000 },
+  { 0x1a01, 0xd33e, 0x0000 },
+  { 0x9a01, 0xd347, 0x4000 },
+  { 0x9a01, 0xd343, 0x3000 },
+  { 0x9a01, 0xd341, 0x2000 },
+  { 0x1a01, 0xd340, 0x0000 },
+  { 0x1a01, 0xd342, 0x0000 },
+  { 0x9a01, 0xd345, 0x2000 },
+  { 0x1a01, 0xd344, 0x0000 },
+  { 0x1a01, 0xd346, 0x0000 },
+  { 0x9a01, 0xd34b, 0x3000 },
+  { 0x9a01, 0xd349, 0x2000 },
+  { 0x1a01, 0xd348, 0x0000 },
+  { 0x1a01, 0xd34a, 0x0000 },
+  { 0x9a01, 0xd34d, 0x2000 },
+  { 0x1a01, 0xd34c, 0x0000 },
+  { 0x1a01, 0xd34e, 0x0000 },
+  { 0x8901, 0xd408, 0x5000 },
+  { 0x8901, 0xd400, 0x4000 },
+  { 0x9a01, 0xd353, 0x3000 },
+  { 0x9a01, 0xd351, 0x2000 },
+  { 0x1a01, 0xd350, 0x0000 },
+  { 0x1a01, 0xd352, 0x0000 },
+  { 0x9a01, 0xd355, 0x2000 },
+  { 0x1a01, 0xd354, 0x0000 },
+  { 0x1a01, 0xd356, 0x0000 },
+  { 0x8901, 0xd404, 0x3000 },
+  { 0x8901, 0xd402, 0x2000 },
+  { 0x0901, 0xd401, 0x0000 },
+  { 0x0901, 0xd403, 0x0000 },
+  { 0x8901, 0xd406, 0x2000 },
+  { 0x0901, 0xd405, 0x0000 },
+  { 0x0901, 0xd407, 0x0000 },
+  { 0x8901, 0xd410, 0x4000 },
+  { 0x8901, 0xd40c, 0x3000 },
+  { 0x8901, 0xd40a, 0x2000 },
+  { 0x0901, 0xd409, 0x0000 },
+  { 0x0901, 0xd40b, 0x0000 },
+  { 0x8901, 0xd40e, 0x2000 },
+  { 0x0901, 0xd40d, 0x0000 },
+  { 0x0901, 0xd40f, 0x0000 },
+  { 0x8901, 0xd414, 0x3000 },
+  { 0x8901, 0xd412, 0x2000 },
+  { 0x0901, 0xd411, 0x0000 },
+  { 0x0901, 0xd413, 0x0000 },
+  { 0x8901, 0xd416, 0x2000 },
+  { 0x0901, 0xd415, 0x0000 },
+  { 0x0901, 0xd417, 0x0000 },
+  { 0x8901, 0xd438, 0x6000 },
+  { 0x8501, 0xd428, 0x5000 },
+  { 0x8501, 0xd420, 0x4000 },
+  { 0x8501, 0xd41c, 0x3000 },
+  { 0x8501, 0xd41a, 0x2000 },
+  { 0x0901, 0xd419, 0x0000 },
+  { 0x0501, 0xd41b, 0x0000 },
+  { 0x8501, 0xd41e, 0x2000 },
+  { 0x0501, 0xd41d, 0x0000 },
+  { 0x0501, 0xd41f, 0x0000 },
+  { 0x8501, 0xd424, 0x3000 },
+  { 0x8501, 0xd422, 0x2000 },
+  { 0x0501, 0xd421, 0x0000 },
+  { 0x0501, 0xd423, 0x0000 },
+  { 0x8501, 0xd426, 0x2000 },
+  { 0x0501, 0xd425, 0x0000 },
+  { 0x0501, 0xd427, 0x0000 },
+  { 0x8501, 0xd430, 0x4000 },
+  { 0x8501, 0xd42c, 0x3000 },
+  { 0x8501, 0xd42a, 0x2000 },
+  { 0x0501, 0xd429, 0x0000 },
+  { 0x0501, 0xd42b, 0x0000 },
+  { 0x8501, 0xd42e, 0x2000 },
+  { 0x0501, 0xd42d, 0x0000 },
+  { 0x0501, 0xd42f, 0x0000 },
+  { 0x8901, 0xd434, 0x3000 },
+  { 0x8501, 0xd432, 0x2000 },
+  { 0x0501, 0xd431, 0x0000 },
+  { 0x0501, 0xd433, 0x0000 },
+  { 0x8901, 0xd436, 0x2000 },
+  { 0x0901, 0xd435, 0x0000 },
+  { 0x0901, 0xd437, 0x0000 },
+  { 0x8901, 0xd448, 0x5000 },
+  { 0x8901, 0xd440, 0x4000 },
+  { 0x8901, 0xd43c, 0x3000 },
+  { 0x8901, 0xd43a, 0x2000 },
+  { 0x0901, 0xd439, 0x0000 },
+  { 0x0901, 0xd43b, 0x0000 },
+  { 0x8901, 0xd43e, 0x2000 },
+  { 0x0901, 0xd43d, 0x0000 },
+  { 0x0901, 0xd43f, 0x0000 },
+  { 0x8901, 0xd444, 0x3000 },
+  { 0x8901, 0xd442, 0x2000 },
+  { 0x0901, 0xd441, 0x0000 },
+  { 0x0901, 0xd443, 0x0000 },
+  { 0x8901, 0xd446, 0x2000 },
+  { 0x0901, 0xd445, 0x0000 },
+  { 0x0901, 0xd447, 0x0000 },
+  { 0x8501, 0xd450, 0x4000 },
+  { 0x8901, 0xd44c, 0x3000 },
+  { 0x8901, 0xd44a, 0x2000 },
+  { 0x0901, 0xd449, 0x0000 },
+  { 0x0901, 0xd44b, 0x0000 },
+  { 0x8501, 0xd44e, 0x2000 },
+  { 0x0901, 0xd44d, 0x0000 },
+  { 0x0501, 0xd44f, 0x0000 },
+  { 0x8501, 0xd454, 0x3000 },
+  { 0x8501, 0xd452, 0x2000 },
+  { 0x0501, 0xd451, 0x0000 },
+  { 0x0501, 0xd453, 0x0000 },
+  { 0x8501, 0xd457, 0x2000 },
+  { 0x0501, 0xd456, 0x0000 },
+  { 0x0501, 0xd458, 0x0000 },
+  { 0x8702, 0xf876, 0xb000 },
+  { 0x8901, 0xd670, 0xa000 },
+  { 0x8901, 0xd570, 0x9000 },
+  { 0x8901, 0xd4e4, 0x8000 },
+  { 0x8501, 0xd499, 0x7000 },
+  { 0x8901, 0xd479, 0x6000 },
+  { 0x8901, 0xd469, 0x5000 },
+  { 0x8501, 0xd461, 0x4000 },
+  { 0x8501, 0xd45d, 0x3000 },
+  { 0x8501, 0xd45b, 0x2000 },
+  { 0x0501, 0xd45a, 0x0000 },
+  { 0x0501, 0xd45c, 0x0000 },
+  { 0x8501, 0xd45f, 0x2000 },
+  { 0x0501, 0xd45e, 0x0000 },
+  { 0x0501, 0xd460, 0x0000 },
+  { 0x8501, 0xd465, 0x3000 },
+  { 0x8501, 0xd463, 0x2000 },
+  { 0x0501, 0xd462, 0x0000 },
+  { 0x0501, 0xd464, 0x0000 },
+  { 0x8501, 0xd467, 0x2000 },
+  { 0x0501, 0xd466, 0x0000 },
+  { 0x0901, 0xd468, 0x0000 },
+  { 0x8901, 0xd471, 0x4000 },
+  { 0x8901, 0xd46d, 0x3000 },
+  { 0x8901, 0xd46b, 0x2000 },
+  { 0x0901, 0xd46a, 0x0000 },
+  { 0x0901, 0xd46c, 0x0000 },
+  { 0x8901, 0xd46f, 0x2000 },
+  { 0x0901, 0xd46e, 0x0000 },
+  { 0x0901, 0xd470, 0x0000 },
+  { 0x8901, 0xd475, 0x3000 },
+  { 0x8901, 0xd473, 0x2000 },
+  { 0x0901, 0xd472, 0x0000 },
+  { 0x0901, 0xd474, 0x0000 },
+  { 0x8901, 0xd477, 0x2000 },
+  { 0x0901, 0xd476, 0x0000 },
+  { 0x0901, 0xd478, 0x0000 },
+  { 0x8501, 0xd489, 0x5000 },
+  { 0x8901, 0xd481, 0x4000 },
+  { 0x8901, 0xd47d, 0x3000 },
+  { 0x8901, 0xd47b, 0x2000 },
+  { 0x0901, 0xd47a, 0x0000 },
+  { 0x0901, 0xd47c, 0x0000 },
+  { 0x8901, 0xd47f, 0x2000 },
+  { 0x0901, 0xd47e, 0x0000 },
+  { 0x0901, 0xd480, 0x0000 },
+  { 0x8501, 0xd485, 0x3000 },
+  { 0x8501, 0xd483, 0x2000 },
+  { 0x0501, 0xd482, 0x0000 },
+  { 0x0501, 0xd484, 0x0000 },
+  { 0x8501, 0xd487, 0x2000 },
+  { 0x0501, 0xd486, 0x0000 },
+  { 0x0501, 0xd488, 0x0000 },
+  { 0x8501, 0xd491, 0x4000 },
+  { 0x8501, 0xd48d, 0x3000 },
+  { 0x8501, 0xd48b, 0x2000 },
+  { 0x0501, 0xd48a, 0x0000 },
+  { 0x0501, 0xd48c, 0x0000 },
+  { 0x8501, 0xd48f, 0x2000 },
+  { 0x0501, 0xd48e, 0x0000 },
+  { 0x0501, 0xd490, 0x0000 },
+  { 0x8501, 0xd495, 0x3000 },
+  { 0x8501, 0xd493, 0x2000 },
+  { 0x0501, 0xd492, 0x0000 },
+  { 0x0501, 0xd494, 0x0000 },
+  { 0x8501, 0xd497, 0x2000 },
+  { 0x0501, 0xd496, 0x0000 },
+  { 0x0501, 0xd498, 0x0000 },
+  { 0x8501, 0xd4c3, 0x6000 },
+  { 0x8901, 0xd4b1, 0x5000 },
+  { 0x8901, 0xd4a6, 0x4000 },
+  { 0x8901, 0xd49e, 0x3000 },
+  { 0x8501, 0xd49b, 0x2000 },
+  { 0x0501, 0xd49a, 0x0000 },
+  { 0x0901, 0xd49c, 0x0000 },
+  { 0x8901, 0xd4a2, 0x2000 },
+  { 0x0901, 0xd49f, 0x0000 },
+  { 0x0901, 0xd4a5, 0x0000 },
+  { 0x8901, 0xd4ac, 0x3000 },
+  { 0x8901, 0xd4aa, 0x2000 },
+  { 0x0901, 0xd4a9, 0x0000 },
+  { 0x0901, 0xd4ab, 0x0000 },
+  { 0x8901, 0xd4af, 0x2000 },
+  { 0x0901, 0xd4ae, 0x0000 },
+  { 0x0901, 0xd4b0, 0x0000 },
+  { 0x8501, 0xd4b9, 0x4000 },
+  { 0x8901, 0xd4b5, 0x3000 },
+  { 0x8901, 0xd4b3, 0x2000 },
+  { 0x0901, 0xd4b2, 0x0000 },
+  { 0x0901, 0xd4b4, 0x0000 },
+  { 0x8501, 0xd4b7, 0x2000 },
+  { 0x0501, 0xd4b6, 0x0000 },
+  { 0x0501, 0xd4b8, 0x0000 },
+  { 0x8501, 0xd4bf, 0x3000 },
+  { 0x8501, 0xd4bd, 0x2000 },
+  { 0x0501, 0xd4bb, 0x0000 },
+  { 0x0501, 0xd4be, 0x0000 },
+  { 0x8501, 0xd4c1, 0x2000 },
+  { 0x0501, 0xd4c0, 0x0000 },
+  { 0x0501, 0xd4c2, 0x0000 },
+  { 0x8901, 0xd4d4, 0x5000 },
+  { 0x8501, 0xd4cc, 0x4000 },
+  { 0x8501, 0xd4c8, 0x3000 },
+  { 0x8501, 0xd4c6, 0x2000 },
+  { 0x0501, 0xd4c5, 0x0000 },
+  { 0x0501, 0xd4c7, 0x0000 },
+  { 0x8501, 0xd4ca, 0x2000 },
+  { 0x0501, 0xd4c9, 0x0000 },
+  { 0x0501, 0xd4cb, 0x0000 },
+  { 0x8901, 0xd4d0, 0x3000 },
+  { 0x8501, 0xd4ce, 0x2000 },
+  { 0x0501, 0xd4cd, 0x0000 },
+  { 0x0501, 0xd4cf, 0x0000 },
+  { 0x8901, 0xd4d2, 0x2000 },
+  { 0x0901, 0xd4d1, 0x0000 },
+  { 0x0901, 0xd4d3, 0x0000 },
+  { 0x8901, 0xd4dc, 0x4000 },
+  { 0x8901, 0xd4d8, 0x3000 },
+  { 0x8901, 0xd4d6, 0x2000 },
+  { 0x0901, 0xd4d5, 0x0000 },
+  { 0x0901, 0xd4d7, 0x0000 },
+  { 0x8901, 0xd4da, 0x2000 },
+  { 0x0901, 0xd4d9, 0x0000 },
+  { 0x0901, 0xd4db, 0x0000 },
+  { 0x8901, 0xd4e0, 0x3000 },
+  { 0x8901, 0xd4de, 0x2000 },
+  { 0x0901, 0xd4dd, 0x0000 },
+  { 0x0901, 0xd4df, 0x0000 },
+  { 0x8901, 0xd4e2, 0x2000 },
+  { 0x0901, 0xd4e1, 0x0000 },
+  { 0x0901, 0xd4e3, 0x0000 },
+  { 0x8501, 0xd529, 0x7000 },
+  { 0x8901, 0xd504, 0x6000 },
+  { 0x8501, 0xd4f4, 0x5000 },
+  { 0x8501, 0xd4ec, 0x4000 },
+  { 0x8901, 0xd4e8, 0x3000 },
+  { 0x8901, 0xd4e6, 0x2000 },
+  { 0x0901, 0xd4e5, 0x0000 },
+  { 0x0901, 0xd4e7, 0x0000 },
+  { 0x8501, 0xd4ea, 0x2000 },
+  { 0x0901, 0xd4e9, 0x0000 },
+  { 0x0501, 0xd4eb, 0x0000 },
+  { 0x8501, 0xd4f0, 0x3000 },
+  { 0x8501, 0xd4ee, 0x2000 },
+  { 0x0501, 0xd4ed, 0x0000 },
+  { 0x0501, 0xd4ef, 0x0000 },
+  { 0x8501, 0xd4f2, 0x2000 },
+  { 0x0501, 0xd4f1, 0x0000 },
+  { 0x0501, 0xd4f3, 0x0000 },
+  { 0x8501, 0xd4fc, 0x4000 },
+  { 0x8501, 0xd4f8, 0x3000 },
+  { 0x8501, 0xd4f6, 0x2000 },
+  { 0x0501, 0xd4f5, 0x0000 },
+  { 0x0501, 0xd4f7, 0x0000 },
+  { 0x8501, 0xd4fa, 0x2000 },
+  { 0x0501, 0xd4f9, 0x0000 },
+  { 0x0501, 0xd4fb, 0x0000 },
+  { 0x8501, 0xd500, 0x3000 },
+  { 0x8501, 0xd4fe, 0x2000 },
+  { 0x0501, 0xd4fd, 0x0000 },
+  { 0x0501, 0xd4ff, 0x0000 },
+  { 0x8501, 0xd502, 0x2000 },
+  { 0x0501, 0xd501, 0x0000 },
+  { 0x0501, 0xd503, 0x0000 },
+  { 0x8901, 0xd518, 0x5000 },
+  { 0x8901, 0xd50f, 0x4000 },
+  { 0x8901, 0xd509, 0x3000 },
+  { 0x8901, 0xd507, 0x2000 },
+  { 0x0901, 0xd505, 0x0000 },
+  { 0x0901, 0xd508, 0x0000 },
+  { 0x8901, 0xd50d, 0x2000 },
+  { 0x0901, 0xd50a, 0x0000 },
+  { 0x0901, 0xd50e, 0x0000 },
+  { 0x8901, 0xd513, 0x3000 },
+  { 0x8901, 0xd511, 0x2000 },
+  { 0x0901, 0xd510, 0x0000 },
+  { 0x0901, 0xd512, 0x0000 },
+  { 0x8901, 0xd516, 0x2000 },
+  { 0x0901, 0xd514, 0x0000 },
+  { 0x0901, 0xd517, 0x0000 },
+  { 0x8501, 0xd521, 0x4000 },
+  { 0x8901, 0xd51c, 0x3000 },
+  { 0x8901, 0xd51a, 0x2000 },
+  { 0x0901, 0xd519, 0x0000 },
+  { 0x0901, 0xd51b, 0x0000 },
+  { 0x8501, 0xd51f, 0x2000 },
+  { 0x0501, 0xd51e, 0x0000 },
+  { 0x0501, 0xd520, 0x0000 },
+  { 0x8501, 0xd525, 0x3000 },
+  { 0x8501, 0xd523, 0x2000 },
+  { 0x0501, 0xd522, 0x0000 },
+  { 0x0501, 0xd524, 0x0000 },
+  { 0x8501, 0xd527, 0x2000 },
+  { 0x0501, 0xd526, 0x0000 },
+  { 0x0501, 0xd528, 0x0000 },
+  { 0x8901, 0xd54f, 0x6000 },
+  { 0x8901, 0xd539, 0x5000 },
+  { 0x8501, 0xd531, 0x4000 },
+  { 0x8501, 0xd52d, 0x3000 },
+  { 0x8501, 0xd52b, 0x2000 },
+  { 0x0501, 0xd52a, 0x0000 },
+  { 0x0501, 0xd52c, 0x0000 },
+  { 0x8501, 0xd52f, 0x2000 },
+  { 0x0501, 0xd52e, 0x0000 },
+  { 0x0501, 0xd530, 0x0000 },
+  { 0x8501, 0xd535, 0x3000 },
+  { 0x8501, 0xd533, 0x2000 },
+  { 0x0501, 0xd532, 0x0000 },
+  { 0x0501, 0xd534, 0x0000 },
+  { 0x8501, 0xd537, 0x2000 },
+  { 0x0501, 0xd536, 0x0000 },
+  { 0x0901, 0xd538, 0x0000 },
+  { 0x8901, 0xd543, 0x4000 },
+  { 0x8901, 0xd53e, 0x3000 },
+  { 0x8901, 0xd53c, 0x2000 },
+  { 0x0901, 0xd53b, 0x0000 },
+  { 0x0901, 0xd53d, 0x0000 },
+  { 0x8901, 0xd541, 0x2000 },
+  { 0x0901, 0xd540, 0x0000 },
+  { 0x0901, 0xd542, 0x0000 },
+  { 0x8901, 0xd54b, 0x3000 },
+  { 0x8901, 0xd546, 0x2000 },
+  { 0x0901, 0xd544, 0x0000 },
+  { 0x0901, 0xd54a, 0x0000 },
+  { 0x8901, 0xd54d, 0x2000 },
+  { 0x0901, 0xd54c, 0x0000 },
+  { 0x0901, 0xd54e, 0x0000 },
+  { 0x8501, 0xd560, 0x5000 },
+  { 0x8501, 0xd558, 0x4000 },
+  { 0x8501, 0xd554, 0x3000 },
+  { 0x8501, 0xd552, 0x2000 },
+  { 0x0901, 0xd550, 0x0000 },
+  { 0x0501, 0xd553, 0x0000 },
+  { 0x8501, 0xd556, 0x2000 },
+  { 0x0501, 0xd555, 0x0000 },
+  { 0x0501, 0xd557, 0x0000 },
+  { 0x8501, 0xd55c, 0x3000 },
+  { 0x8501, 0xd55a, 0x2000 },
+  { 0x0501, 0xd559, 0x0000 },
+  { 0x0501, 0xd55b, 0x0000 },
+  { 0x8501, 0xd55e, 0x2000 },
+  { 0x0501, 0xd55d, 0x0000 },
+  { 0x0501, 0xd55f, 0x0000 },
+  { 0x8501, 0xd568, 0x4000 },
+  { 0x8501, 0xd564, 0x3000 },
+  { 0x8501, 0xd562, 0x2000 },
+  { 0x0501, 0xd561, 0x0000 },
+  { 0x0501, 0xd563, 0x0000 },
+  { 0x8501, 0xd566, 0x2000 },
+  { 0x0501, 0xd565, 0x0000 },
+  { 0x0501, 0xd567, 0x0000 },
+  { 0x8901, 0xd56c, 0x3000 },
+  { 0x8501, 0xd56a, 0x2000 },
+  { 0x0501, 0xd569, 0x0000 },
+  { 0x0501, 0xd56b, 0x0000 },
+  { 0x8901, 0xd56e, 0x2000 },
+  { 0x0901, 0xd56d, 0x0000 },
+  { 0x0901, 0xd56f, 0x0000 },
+  { 0x8501, 0xd5f0, 0x8000 },
+  { 0x8901, 0xd5b0, 0x7000 },
+  { 0x8501, 0xd590, 0x6000 },
+  { 0x8901, 0xd580, 0x5000 },
+  { 0x8901, 0xd578, 0x4000 },
+  { 0x8901, 0xd574, 0x3000 },
+  { 0x8901, 0xd572, 0x2000 },
+  { 0x0901, 0xd571, 0x0000 },
+  { 0x0901, 0xd573, 0x0000 },
+  { 0x8901, 0xd576, 0x2000 },
+  { 0x0901, 0xd575, 0x0000 },
+  { 0x0901, 0xd577, 0x0000 },
+  { 0x8901, 0xd57c, 0x3000 },
+  { 0x8901, 0xd57a, 0x2000 },
+  { 0x0901, 0xd579, 0x0000 },
+  { 0x0901, 0xd57b, 0x0000 },
+  { 0x8901, 0xd57e, 0x2000 },
+  { 0x0901, 0xd57d, 0x0000 },
+  { 0x0901, 0xd57f, 0x0000 },
+  { 0x8501, 0xd588, 0x4000 },
+  { 0x8901, 0xd584, 0x3000 },
+  { 0x8901, 0xd582, 0x2000 },
+  { 0x0901, 0xd581, 0x0000 },
+  { 0x0901, 0xd583, 0x0000 },
+  { 0x8501, 0xd586, 0x2000 },
+  { 0x0901, 0xd585, 0x0000 },
+  { 0x0501, 0xd587, 0x0000 },
+  { 0x8501, 0xd58c, 0x3000 },
+  { 0x8501, 0xd58a, 0x2000 },
+  { 0x0501, 0xd589, 0x0000 },
+  { 0x0501, 0xd58b, 0x0000 },
+  { 0x8501, 0xd58e, 0x2000 },
+  { 0x0501, 0xd58d, 0x0000 },
+  { 0x0501, 0xd58f, 0x0000 },
+  { 0x8901, 0xd5a0, 0x5000 },
+  { 0x8501, 0xd598, 0x4000 },
+  { 0x8501, 0xd594, 0x3000 },
+  { 0x8501, 0xd592, 0x2000 },
+  { 0x0501, 0xd591, 0x0000 },
+  { 0x0501, 0xd593, 0x0000 },
+  { 0x8501, 0xd596, 0x2000 },
+  { 0x0501, 0xd595, 0x0000 },
+  { 0x0501, 0xd597, 0x0000 },
+  { 0x8501, 0xd59c, 0x3000 },
+  { 0x8501, 0xd59a, 0x2000 },
+  { 0x0501, 0xd599, 0x0000 },
+  { 0x0501, 0xd59b, 0x0000 },
+  { 0x8501, 0xd59e, 0x2000 },
+  { 0x0501, 0xd59d, 0x0000 },
+  { 0x0501, 0xd59f, 0x0000 },
+  { 0x8901, 0xd5a8, 0x4000 },
+  { 0x8901, 0xd5a4, 0x3000 },
+  { 0x8901, 0xd5a2, 0x2000 },
+  { 0x0901, 0xd5a1, 0x0000 },
+  { 0x0901, 0xd5a3, 0x0000 },
+  { 0x8901, 0xd5a6, 0x2000 },
+  { 0x0901, 0xd5a5, 0x0000 },
+  { 0x0901, 0xd5a7, 0x0000 },
+  { 0x8901, 0xd5ac, 0x3000 },
+  { 0x8901, 0xd5aa, 0x2000 },
+  { 0x0901, 0xd5a9, 0x0000 },
+  { 0x0901, 0xd5ab, 0x0000 },
+  { 0x8901, 0xd5ae, 0x2000 },
+  { 0x0901, 0xd5ad, 0x0000 },
+  { 0x0901, 0xd5af, 0x0000 },
+  { 0x8501, 0xd5d0, 0x6000 },
+  { 0x8501, 0xd5c0, 0x5000 },
+  { 0x8901, 0xd5b8, 0x4000 },
+  { 0x8901, 0xd5b4, 0x3000 },
+  { 0x8901, 0xd5b2, 0x2000 },
+  { 0x0901, 0xd5b1, 0x0000 },
+  { 0x0901, 0xd5b3, 0x0000 },
+  { 0x8901, 0xd5b6, 0x2000 },
+  { 0x0901, 0xd5b5, 0x0000 },
+  { 0x0901, 0xd5b7, 0x0000 },
+  { 0x8501, 0xd5bc, 0x3000 },
+  { 0x8501, 0xd5ba, 0x2000 },
+  { 0x0901, 0xd5b9, 0x0000 },
+  { 0x0501, 0xd5bb, 0x0000 },
+  { 0x8501, 0xd5be, 0x2000 },
+  { 0x0501, 0xd5bd, 0x0000 },
+  { 0x0501, 0xd5bf, 0x0000 },
+  { 0x8501, 0xd5c8, 0x4000 },
+  { 0x8501, 0xd5c4, 0x3000 },
+  { 0x8501, 0xd5c2, 0x2000 },
+  { 0x0501, 0xd5c1, 0x0000 },
+  { 0x0501, 0xd5c3, 0x0000 },
+  { 0x8501, 0xd5c6, 0x2000 },
+  { 0x0501, 0xd5c5, 0x0000 },
+  { 0x0501, 0xd5c7, 0x0000 },
+  { 0x8501, 0xd5cc, 0x3000 },
+  { 0x8501, 0xd5ca, 0x2000 },
+  { 0x0501, 0xd5c9, 0x0000 },
+  { 0x0501, 0xd5cb, 0x0000 },
+  { 0x8501, 0xd5ce, 0x2000 },
+  { 0x0501, 0xd5cd, 0x0000 },
+  { 0x0501, 0xd5cf, 0x0000 },
+  { 0x8901, 0xd5e0, 0x5000 },
+  { 0x8901, 0xd5d8, 0x4000 },
+  { 0x8901, 0xd5d4, 0x3000 },
+  { 0x8501, 0xd5d2, 0x2000 },
+  { 0x0501, 0xd5d1, 0x0000 },
+  { 0x0501, 0xd5d3, 0x0000 },
+  { 0x8901, 0xd5d6, 0x2000 },
+  { 0x0901, 0xd5d5, 0x0000 },
+  { 0x0901, 0xd5d7, 0x0000 },
+  { 0x8901, 0xd5dc, 0x3000 },
+  { 0x8901, 0xd5da, 0x2000 },
+  { 0x0901, 0xd5d9, 0x0000 },
+  { 0x0901, 0xd5db, 0x0000 },
+  { 0x8901, 0xd5de, 0x2000 },
+  { 0x0901, 0xd5dd, 0x0000 },
+  { 0x0901, 0xd5df, 0x0000 },
+  { 0x8901, 0xd5e8, 0x4000 },
+  { 0x8901, 0xd5e4, 0x3000 },
+  { 0x8901, 0xd5e2, 0x2000 },
+  { 0x0901, 0xd5e1, 0x0000 },
+  { 0x0901, 0xd5e3, 0x0000 },
+  { 0x8901, 0xd5e6, 0x2000 },
+  { 0x0901, 0xd5e5, 0x0000 },
+  { 0x0901, 0xd5e7, 0x0000 },
+  { 0x8901, 0xd5ec, 0x3000 },
+  { 0x8901, 0xd5ea, 0x2000 },
+  { 0x0901, 0xd5e9, 0x0000 },
+  { 0x0901, 0xd5eb, 0x0000 },
+  { 0x8501, 0xd5ee, 0x2000 },
+  { 0x0901, 0xd5ed, 0x0000 },
+  { 0x0501, 0xd5ef, 0x0000 },
+  { 0x8501, 0xd630, 0x7000 },
+  { 0x8901, 0xd610, 0x6000 },
+  { 0x8501, 0xd600, 0x5000 },
+  { 0x8501, 0xd5f8, 0x4000 },
+  { 0x8501, 0xd5f4, 0x3000 },
+  { 0x8501, 0xd5f2, 0x2000 },
+  { 0x0501, 0xd5f1, 0x0000 },
+  { 0x0501, 0xd5f3, 0x0000 },
+  { 0x8501, 0xd5f6, 0x2000 },
+  { 0x0501, 0xd5f5, 0x0000 },
+  { 0x0501, 0xd5f7, 0x0000 },
+  { 0x8501, 0xd5fc, 0x3000 },
+  { 0x8501, 0xd5fa, 0x2000 },
+  { 0x0501, 0xd5f9, 0x0000 },
+  { 0x0501, 0xd5fb, 0x0000 },
+  { 0x8501, 0xd5fe, 0x2000 },
+  { 0x0501, 0xd5fd, 0x0000 },
+  { 0x0501, 0xd5ff, 0x0000 },
+  { 0x8901, 0xd608, 0x4000 },
+  { 0x8501, 0xd604, 0x3000 },
+  { 0x8501, 0xd602, 0x2000 },
+  { 0x0501, 0xd601, 0x0000 },
+  { 0x0501, 0xd603, 0x0000 },
+  { 0x8501, 0xd606, 0x2000 },
+  { 0x0501, 0xd605, 0x0000 },
+  { 0x0501, 0xd607, 0x0000 },
+  { 0x8901, 0xd60c, 0x3000 },
+  { 0x8901, 0xd60a, 0x2000 },
+  { 0x0901, 0xd609, 0x0000 },
+  { 0x0901, 0xd60b, 0x0000 },
+  { 0x8901, 0xd60e, 0x2000 },
+  { 0x0901, 0xd60d, 0x0000 },
+  { 0x0901, 0xd60f, 0x0000 },
+  { 0x8901, 0xd620, 0x5000 },
+  { 0x8901, 0xd618, 0x4000 },
+  { 0x8901, 0xd614, 0x3000 },
+  { 0x8901, 0xd612, 0x2000 },
+  { 0x0901, 0xd611, 0x0000 },
+  { 0x0901, 0xd613, 0x0000 },
+  { 0x8901, 0xd616, 0x2000 },
+  { 0x0901, 0xd615, 0x0000 },
+  { 0x0901, 0xd617, 0x0000 },
+  { 0x8901, 0xd61c, 0x3000 },
+  { 0x8901, 0xd61a, 0x2000 },
+  { 0x0901, 0xd619, 0x0000 },
+  { 0x0901, 0xd61b, 0x0000 },
+  { 0x8901, 0xd61e, 0x2000 },
+  { 0x0901, 0xd61d, 0x0000 },
+  { 0x0901, 0xd61f, 0x0000 },
+  { 0x8501, 0xd628, 0x4000 },
+  { 0x8501, 0xd624, 0x3000 },
+  { 0x8501, 0xd622, 0x2000 },
+  { 0x0901, 0xd621, 0x0000 },
+  { 0x0501, 0xd623, 0x0000 },
+  { 0x8501, 0xd626, 0x2000 },
+  { 0x0501, 0xd625, 0x0000 },
+  { 0x0501, 0xd627, 0x0000 },
+  { 0x8501, 0xd62c, 0x3000 },
+  { 0x8501, 0xd62a, 0x2000 },
+  { 0x0501, 0xd629, 0x0000 },
+  { 0x0501, 0xd62b, 0x0000 },
+  { 0x8501, 0xd62e, 0x2000 },
+  { 0x0501, 0xd62d, 0x0000 },
+  { 0x0501, 0xd62f, 0x0000 },
+  { 0x8901, 0xd650, 0x6000 },
+  { 0x8901, 0xd640, 0x5000 },
+  { 0x8501, 0xd638, 0x4000 },
+  { 0x8501, 0xd634, 0x3000 },
+  { 0x8501, 0xd632, 0x2000 },
+  { 0x0501, 0xd631, 0x0000 },
+  { 0x0501, 0xd633, 0x0000 },
+  { 0x8501, 0xd636, 0x2000 },
+  { 0x0501, 0xd635, 0x0000 },
+  { 0x0501, 0xd637, 0x0000 },
+  { 0x8901, 0xd63c, 0x3000 },
+  { 0x8501, 0xd63a, 0x2000 },
+  { 0x0501, 0xd639, 0x0000 },
+  { 0x0501, 0xd63b, 0x0000 },
+  { 0x8901, 0xd63e, 0x2000 },
+  { 0x0901, 0xd63d, 0x0000 },
+  { 0x0901, 0xd63f, 0x0000 },
+  { 0x8901, 0xd648, 0x4000 },
+  { 0x8901, 0xd644, 0x3000 },
+  { 0x8901, 0xd642, 0x2000 },
+  { 0x0901, 0xd641, 0x0000 },
+  { 0x0901, 0xd643, 0x0000 },
+  { 0x8901, 0xd646, 0x2000 },
+  { 0x0901, 0xd645, 0x0000 },
+  { 0x0901, 0xd647, 0x0000 },
+  { 0x8901, 0xd64c, 0x3000 },
+  { 0x8901, 0xd64a, 0x2000 },
+  { 0x0901, 0xd649, 0x0000 },
+  { 0x0901, 0xd64b, 0x0000 },
+  { 0x8901, 0xd64e, 0x2000 },
+  { 0x0901, 0xd64d, 0x0000 },
+  { 0x0901, 0xd64f, 0x0000 },
+  { 0x8501, 0xd660, 0x5000 },
+  { 0x8501, 0xd658, 0x4000 },
+  { 0x8901, 0xd654, 0x3000 },
+  { 0x8901, 0xd652, 0x2000 },
+  { 0x0901, 0xd651, 0x0000 },
+  { 0x0901, 0xd653, 0x0000 },
+  { 0x8501, 0xd656, 0x2000 },
+  { 0x0901, 0xd655, 0x0000 },
+  { 0x0501, 0xd657, 0x0000 },
+  { 0x8501, 0xd65c, 0x3000 },
+  { 0x8501, 0xd65a, 0x2000 },
+  { 0x0501, 0xd659, 0x0000 },
+  { 0x0501, 0xd65b, 0x0000 },
+  { 0x8501, 0xd65e, 0x2000 },
+  { 0x0501, 0xd65d, 0x0000 },
+  { 0x0501, 0xd65f, 0x0000 },
+  { 0x8501, 0xd668, 0x4000 },
+  { 0x8501, 0xd664, 0x3000 },
+  { 0x8501, 0xd662, 0x2000 },
+  { 0x0501, 0xd661, 0x0000 },
+  { 0x0501, 0xd663, 0x0000 },
+  { 0x8501, 0xd666, 0x2000 },
+  { 0x0501, 0xd665, 0x0000 },
+  { 0x0501, 0xd667, 0x0000 },
+  { 0x8501, 0xd66c, 0x3000 },
+  { 0x8501, 0xd66a, 0x2000 },
+  { 0x0501, 0xd669, 0x0000 },
+  { 0x0501, 0xd66b, 0x0000 },
+  { 0x8501, 0xd66e, 0x2000 },
+  { 0x0501, 0xd66d, 0x0000 },
+  { 0x0501, 0xd66f, 0x0000 },
+  { 0x8501, 0xd774, 0x9000 },
+  { 0x8901, 0xd6f4, 0x8000 },
+  { 0x8901, 0xd6b4, 0x7000 },
+  { 0x8501, 0xd690, 0x6000 },
+  { 0x8901, 0xd680, 0x5000 },
+  { 0x8901, 0xd678, 0x4000 },
+  { 0x8901, 0xd674, 0x3000 },
+  { 0x8901, 0xd672, 0x2000 },
+  { 0x0901, 0xd671, 0x0000 },
+  { 0x0901, 0xd673, 0x0000 },
+  { 0x8901, 0xd676, 0x2000 },
+  { 0x0901, 0xd675, 0x0000 },
+  { 0x0901, 0xd677, 0x0000 },
+  { 0x8901, 0xd67c, 0x3000 },
+  { 0x8901, 0xd67a, 0x2000 },
+  { 0x0901, 0xd679, 0x0000 },
+  { 0x0901, 0xd67b, 0x0000 },
+  { 0x8901, 0xd67e, 0x2000 },
+  { 0x0901, 0xd67d, 0x0000 },
+  { 0x0901, 0xd67f, 0x0000 },
+  { 0x8901, 0xd688, 0x4000 },
+  { 0x8901, 0xd684, 0x3000 },
+  { 0x8901, 0xd682, 0x2000 },
+  { 0x0901, 0xd681, 0x0000 },
+  { 0x0901, 0xd683, 0x0000 },
+  { 0x8901, 0xd686, 0x2000 },
+  { 0x0901, 0xd685, 0x0000 },
+  { 0x0901, 0xd687, 0x0000 },
+  { 0x8501, 0xd68c, 0x3000 },
+  { 0x8501, 0xd68a, 0x2000 },
+  { 0x0901, 0xd689, 0x0000 },
+  { 0x0501, 0xd68b, 0x0000 },
+  { 0x8501, 0xd68e, 0x2000 },
+  { 0x0501, 0xd68d, 0x0000 },
+  { 0x0501, 0xd68f, 0x0000 },
+  { 0x8501, 0xd6a0, 0x5000 },
+  { 0x8501, 0xd698, 0x4000 },
+  { 0x8501, 0xd694, 0x3000 },
+  { 0x8501, 0xd692, 0x2000 },
+  { 0x0501, 0xd691, 0x0000 },
+  { 0x0501, 0xd693, 0x0000 },
+  { 0x8501, 0xd696, 0x2000 },
+  { 0x0501, 0xd695, 0x0000 },
+  { 0x0501, 0xd697, 0x0000 },
+  { 0x8501, 0xd69c, 0x3000 },
+  { 0x8501, 0xd69a, 0x2000 },
+  { 0x0501, 0xd699, 0x0000 },
+  { 0x0501, 0xd69b, 0x0000 },
+  { 0x8501, 0xd69e, 0x2000 },
+  { 0x0501, 0xd69d, 0x0000 },
+  { 0x0501, 0xd69f, 0x0000 },
+  { 0x8901, 0xd6ac, 0x4000 },
+  { 0x8901, 0xd6a8, 0x3000 },
+  { 0x8501, 0xd6a2, 0x2000 },
+  { 0x0501, 0xd6a1, 0x0000 },
+  { 0x0501, 0xd6a3, 0x0000 },
+  { 0x8901, 0xd6aa, 0x2000 },
+  { 0x0901, 0xd6a9, 0x0000 },
+  { 0x0901, 0xd6ab, 0x0000 },
+  { 0x8901, 0xd6b0, 0x3000 },
+  { 0x8901, 0xd6ae, 0x2000 },
+  { 0x0901, 0xd6ad, 0x0000 },
+  { 0x0901, 0xd6af, 0x0000 },
+  { 0x8901, 0xd6b2, 0x2000 },
+  { 0x0901, 0xd6b1, 0x0000 },
+  { 0x0901, 0xd6b3, 0x0000 },
+  { 0x8501, 0xd6d4, 0x6000 },
+  { 0x8501, 0xd6c4, 0x5000 },
+  { 0x8901, 0xd6bc, 0x4000 },
+  { 0x8901, 0xd6b8, 0x3000 },
+  { 0x8901, 0xd6b6, 0x2000 },
+  { 0x0901, 0xd6b5, 0x0000 },
+  { 0x0901, 0xd6b7, 0x0000 },
+  { 0x8901, 0xd6ba, 0x2000 },
+  { 0x0901, 0xd6b9, 0x0000 },
+  { 0x0901, 0xd6bb, 0x0000 },
+  { 0x8901, 0xd6c0, 0x3000 },
+  { 0x8901, 0xd6be, 0x2000 },
+  { 0x0901, 0xd6bd, 0x0000 },
+  { 0x0901, 0xd6bf, 0x0000 },
+  { 0x8501, 0xd6c2, 0x2000 },
+  { 0x1901, 0xd6c1, 0x0000 },
+  { 0x0501, 0xd6c3, 0x0000 },
+  { 0x8501, 0xd6cc, 0x4000 },
+  { 0x8501, 0xd6c8, 0x3000 },
+  { 0x8501, 0xd6c6, 0x2000 },
+  { 0x0501, 0xd6c5, 0x0000 },
+  { 0x0501, 0xd6c7, 0x0000 },
+  { 0x8501, 0xd6ca, 0x2000 },
+  { 0x0501, 0xd6c9, 0x0000 },
+  { 0x0501, 0xd6cb, 0x0000 },
+  { 0x8501, 0xd6d0, 0x3000 },
+  { 0x8501, 0xd6ce, 0x2000 },
+  { 0x0501, 0xd6cd, 0x0000 },
+  { 0x0501, 0xd6cf, 0x0000 },
+  { 0x8501, 0xd6d2, 0x2000 },
+  { 0x0501, 0xd6d1, 0x0000 },
+  { 0x0501, 0xd6d3, 0x0000 },
+  { 0x8901, 0xd6e4, 0x5000 },
+  { 0x8501, 0xd6dc, 0x4000 },
+  { 0x8501, 0xd6d8, 0x3000 },
+  { 0x8501, 0xd6d6, 0x2000 },
+  { 0x0501, 0xd6d5, 0x0000 },
+  { 0x0501, 0xd6d7, 0x0000 },
+  { 0x8501, 0xd6da, 0x2000 },
+  { 0x0501, 0xd6d9, 0x0000 },
+  { 0x1901, 0xd6db, 0x0000 },
+  { 0x8501, 0xd6e0, 0x3000 },
+  { 0x8501, 0xd6de, 0x2000 },
+  { 0x0501, 0xd6dd, 0x0000 },
+  { 0x0501, 0xd6df, 0x0000 },
+  { 0x8901, 0xd6e2, 0x2000 },
+  { 0x0501, 0xd6e1, 0x0000 },
+  { 0x0901, 0xd6e3, 0x0000 },
+  { 0x8901, 0xd6ec, 0x4000 },
+  { 0x8901, 0xd6e8, 0x3000 },
+  { 0x8901, 0xd6e6, 0x2000 },
+  { 0x0901, 0xd6e5, 0x0000 },
+  { 0x0901, 0xd6e7, 0x0000 },
+  { 0x8901, 0xd6ea, 0x2000 },
+  { 0x0901, 0xd6e9, 0x0000 },
+  { 0x0901, 0xd6eb, 0x0000 },
+  { 0x8901, 0xd6f0, 0x3000 },
+  { 0x8901, 0xd6ee, 0x2000 },
+  { 0x0901, 0xd6ed, 0x0000 },
+  { 0x0901, 0xd6ef, 0x0000 },
+  { 0x8901, 0xd6f2, 0x2000 },
+  { 0x0901, 0xd6f1, 0x0000 },
+  { 0x0901, 0xd6f3, 0x0000 },
+  { 0x8901, 0xd734, 0x7000 },
+  { 0x8501, 0xd714, 0x6000 },
+  { 0x8501, 0xd704, 0x5000 },
+  { 0x8501, 0xd6fc, 0x4000 },
+  { 0x8901, 0xd6f8, 0x3000 },
+  { 0x8901, 0xd6f6, 0x2000 },
+  { 0x0901, 0xd6f5, 0x0000 },
+  { 0x0901, 0xd6f7, 0x0000 },
+  { 0x8901, 0xd6fa, 0x2000 },
+  { 0x0901, 0xd6f9, 0x0000 },
+  { 0x1901, 0xd6fb, 0x0000 },
+  { 0x8501, 0xd700, 0x3000 },
+  { 0x8501, 0xd6fe, 0x2000 },
+  { 0x0501, 0xd6fd, 0x0000 },
+  { 0x0501, 0xd6ff, 0x0000 },
+  { 0x8501, 0xd702, 0x2000 },
+  { 0x0501, 0xd701, 0x0000 },
+  { 0x0501, 0xd703, 0x0000 },
+  { 0x8501, 0xd70c, 0x4000 },
+  { 0x8501, 0xd708, 0x3000 },
+  { 0x8501, 0xd706, 0x2000 },
+  { 0x0501, 0xd705, 0x0000 },
+  { 0x0501, 0xd707, 0x0000 },
+  { 0x8501, 0xd70a, 0x2000 },
+  { 0x0501, 0xd709, 0x0000 },
+  { 0x0501, 0xd70b, 0x0000 },
+  { 0x8501, 0xd710, 0x3000 },
+  { 0x8501, 0xd70e, 0x2000 },
+  { 0x0501, 0xd70d, 0x0000 },
+  { 0x0501, 0xd70f, 0x0000 },
+  { 0x8501, 0xd712, 0x2000 },
+  { 0x0501, 0xd711, 0x0000 },
+  { 0x0501, 0xd713, 0x0000 },
+  { 0x8901, 0xd724, 0x5000 },
+  { 0x8901, 0xd71c, 0x4000 },
+  { 0x8501, 0xd718, 0x3000 },
+  { 0x8501, 0xd716, 0x2000 },
+  { 0x1901, 0xd715, 0x0000 },
+  { 0x0501, 0xd717, 0x0000 },
+  { 0x8501, 0xd71a, 0x2000 },
+  { 0x0501, 0xd719, 0x0000 },
+  { 0x0501, 0xd71b, 0x0000 },
+  { 0x8901, 0xd720, 0x3000 },
+  { 0x8901, 0xd71e, 0x2000 },
+  { 0x0901, 0xd71d, 0x0000 },
+  { 0x0901, 0xd71f, 0x0000 },
+  { 0x8901, 0xd722, 0x2000 },
+  { 0x0901, 0xd721, 0x0000 },
+  { 0x0901, 0xd723, 0x0000 },
+  { 0x8901, 0xd72c, 0x4000 },
+  { 0x8901, 0xd728, 0x3000 },
+  { 0x8901, 0xd726, 0x2000 },
+  { 0x0901, 0xd725, 0x0000 },
+  { 0x0901, 0xd727, 0x0000 },
+  { 0x8901, 0xd72a, 0x2000 },
+  { 0x0901, 0xd729, 0x0000 },
+  { 0x0901, 0xd72b, 0x0000 },
+  { 0x8901, 0xd730, 0x3000 },
+  { 0x8901, 0xd72e, 0x2000 },
+  { 0x0901, 0xd72d, 0x0000 },
+  { 0x0901, 0xd72f, 0x0000 },
+  { 0x8901, 0xd732, 0x2000 },
+  { 0x0901, 0xd731, 0x0000 },
+  { 0x0901, 0xd733, 0x0000 },
+  { 0x8501, 0xd754, 0x6000 },
+  { 0x8501, 0xd744, 0x5000 },
+  { 0x8501, 0xd73c, 0x4000 },
+  { 0x8501, 0xd738, 0x3000 },
+  { 0x8501, 0xd736, 0x2000 },
+  { 0x1901, 0xd735, 0x0000 },
+  { 0x0501, 0xd737, 0x0000 },
+  { 0x8501, 0xd73a, 0x2000 },
+  { 0x0501, 0xd739, 0x0000 },
+  { 0x0501, 0xd73b, 0x0000 },
+  { 0x8501, 0xd740, 0x3000 },
+  { 0x8501, 0xd73e, 0x2000 },
+  { 0x0501, 0xd73d, 0x0000 },
+  { 0x0501, 0xd73f, 0x0000 },
+  { 0x8501, 0xd742, 0x2000 },
+  { 0x0501, 0xd741, 0x0000 },
+  { 0x0501, 0xd743, 0x0000 },
+  { 0x8501, 0xd74c, 0x4000 },
+  { 0x8501, 0xd748, 0x3000 },
+  { 0x8501, 0xd746, 0x2000 },
+  { 0x0501, 0xd745, 0x0000 },
+  { 0x0501, 0xd747, 0x0000 },
+  { 0x8501, 0xd74a, 0x2000 },
+  { 0x0501, 0xd749, 0x0000 },
+  { 0x0501, 0xd74b, 0x0000 },
+  { 0x8501, 0xd750, 0x3000 },
+  { 0x8501, 0xd74e, 0x2000 },
+  { 0x0501, 0xd74d, 0x0000 },
+  { 0x1901, 0xd74f, 0x0000 },
+  { 0x8501, 0xd752, 0x2000 },
+  { 0x0501, 0xd751, 0x0000 },
+  { 0x0501, 0xd753, 0x0000 },
+  { 0x8901, 0xd764, 0x5000 },
+  { 0x8901, 0xd75c, 0x4000 },
+  { 0x8901, 0xd758, 0x3000 },
+  { 0x8901, 0xd756, 0x2000 },
+  { 0x0501, 0xd755, 0x0000 },
+  { 0x0901, 0xd757, 0x0000 },
+  { 0x8901, 0xd75a, 0x2000 },
+  { 0x0901, 0xd759, 0x0000 },
+  { 0x0901, 0xd75b, 0x0000 },
+  { 0x8901, 0xd760, 0x3000 },
+  { 0x8901, 0xd75e, 0x2000 },
+  { 0x0901, 0xd75d, 0x0000 },
+  { 0x0901, 0xd75f, 0x0000 },
+  { 0x8901, 0xd762, 0x2000 },
+  { 0x0901, 0xd761, 0x0000 },
+  { 0x0901, 0xd763, 0x0000 },
+  { 0x8901, 0xd76c, 0x4000 },
+  { 0x8901, 0xd768, 0x3000 },
+  { 0x8901, 0xd766, 0x2000 },
+  { 0x0901, 0xd765, 0x0000 },
+  { 0x0901, 0xd767, 0x0000 },
+  { 0x8901, 0xd76a, 0x2000 },
+  { 0x0901, 0xd769, 0x0000 },
+  { 0x0901, 0xd76b, 0x0000 },
+  { 0x8501, 0xd770, 0x3000 },
+  { 0x8901, 0xd76e, 0x2000 },
+  { 0x0901, 0xd76d, 0x0000 },
+  { 0x1901, 0xd76f, 0x0000 },
+  { 0x8501, 0xd772, 0x2000 },
+  { 0x0501, 0xd771, 0x0000 },
+  { 0x0501, 0xd773, 0x0000 },
+  { 0x8d01, 0xd7f8, 0x8000 },
+  { 0x8501, 0xd7b4, 0x7000 },
+  { 0x8901, 0xd794, 0x6000 },
+  { 0x8501, 0xd784, 0x5000 },
+  { 0x8501, 0xd77c, 0x4000 },
+  { 0x8501, 0xd778, 0x3000 },
+  { 0x8501, 0xd776, 0x2000 },
+  { 0x0501, 0xd775, 0x0000 },
+  { 0x0501, 0xd777, 0x0000 },
+  { 0x8501, 0xd77a, 0x2000 },
+  { 0x0501, 0xd779, 0x0000 },
+  { 0x0501, 0xd77b, 0x0000 },
+  { 0x8501, 0xd780, 0x3000 },
+  { 0x8501, 0xd77e, 0x2000 },
+  { 0x0501, 0xd77d, 0x0000 },
+  { 0x0501, 0xd77f, 0x0000 },
+  { 0x8501, 0xd782, 0x2000 },
+  { 0x0501, 0xd781, 0x0000 },
+  { 0x0501, 0xd783, 0x0000 },
+  { 0x8501, 0xd78c, 0x4000 },
+  { 0x8501, 0xd788, 0x3000 },
+  { 0x8501, 0xd786, 0x2000 },
+  { 0x0501, 0xd785, 0x0000 },
+  { 0x0501, 0xd787, 0x0000 },
+  { 0x8501, 0xd78a, 0x2000 },
+  { 0x1901, 0xd789, 0x0000 },
+  { 0x0501, 0xd78b, 0x0000 },
+  { 0x8901, 0xd790, 0x3000 },
+  { 0x8501, 0xd78e, 0x2000 },
+  { 0x0501, 0xd78d, 0x0000 },
+  { 0x0501, 0xd78f, 0x0000 },
+  { 0x8901, 0xd792, 0x2000 },
+  { 0x0901, 0xd791, 0x0000 },
+  { 0x0901, 0xd793, 0x0000 },
+  { 0x8901, 0xd7a4, 0x5000 },
+  { 0x8901, 0xd79c, 0x4000 },
+  { 0x8901, 0xd798, 0x3000 },
+  { 0x8901, 0xd796, 0x2000 },
+  { 0x0901, 0xd795, 0x0000 },
+  { 0x0901, 0xd797, 0x0000 },
+  { 0x8901, 0xd79a, 0x2000 },
+  { 0x0901, 0xd799, 0x0000 },
+  { 0x0901, 0xd79b, 0x0000 },
+  { 0x8901, 0xd7a0, 0x3000 },
+  { 0x8901, 0xd79e, 0x2000 },
+  { 0x0901, 0xd79d, 0x0000 },
+  { 0x0901, 0xd79f, 0x0000 },
+  { 0x8901, 0xd7a2, 0x2000 },
+  { 0x0901, 0xd7a1, 0x0000 },
+  { 0x0901, 0xd7a3, 0x0000 },
+  { 0x8501, 0xd7ac, 0x4000 },
+  { 0x8901, 0xd7a8, 0x3000 },
+  { 0x8901, 0xd7a6, 0x2000 },
+  { 0x0901, 0xd7a5, 0x0000 },
+  { 0x0901, 0xd7a7, 0x0000 },
+  { 0x8501, 0xd7aa, 0x2000 },
+  { 0x1901, 0xd7a9, 0x0000 },
+  { 0x0501, 0xd7ab, 0x0000 },
+  { 0x8501, 0xd7b0, 0x3000 },
+  { 0x8501, 0xd7ae, 0x2000 },
+  { 0x0501, 0xd7ad, 0x0000 },
+  { 0x0501, 0xd7af, 0x0000 },
+  { 0x8501, 0xd7b2, 0x2000 },
+  { 0x0501, 0xd7b1, 0x0000 },
+  { 0x0501, 0xd7b3, 0x0000 },
+  { 0x8d01, 0xd7d8, 0x6000 },
+  { 0x8501, 0xd7c4, 0x5000 },
+  { 0x8501, 0xd7bc, 0x4000 },
+  { 0x8501, 0xd7b8, 0x3000 },
+  { 0x8501, 0xd7b6, 0x2000 },
+  { 0x0501, 0xd7b5, 0x0000 },
+  { 0x0501, 0xd7b7, 0x0000 },
+  { 0x8501, 0xd7ba, 0x2000 },
+  { 0x0501, 0xd7b9, 0x0000 },
+  { 0x0501, 0xd7bb, 0x0000 },
+  { 0x8501, 0xd7c0, 0x3000 },
+  { 0x8501, 0xd7be, 0x2000 },
+  { 0x0501, 0xd7bd, 0x0000 },
+  { 0x0501, 0xd7bf, 0x0000 },
+  { 0x8501, 0xd7c2, 0x2000 },
+  { 0x0501, 0xd7c1, 0x0000 },
+  { 0x1901, 0xd7c3, 0x0000 },
+  { 0x8d01, 0xd7d0, 0x4000 },
+  { 0x8501, 0xd7c8, 0x3000 },
+  { 0x8501, 0xd7c6, 0x2000 },
+  { 0x0501, 0xd7c5, 0x0000 },
+  { 0x0501, 0xd7c7, 0x0000 },
+  { 0x8d01, 0xd7ce, 0x2000 },
+  { 0x0501, 0xd7c9, 0x0000 },
+  { 0x0d01, 0xd7cf, 0x0000 },
+  { 0x8d01, 0xd7d4, 0x3000 },
+  { 0x8d01, 0xd7d2, 0x2000 },
+  { 0x0d01, 0xd7d1, 0x0000 },
+  { 0x0d01, 0xd7d3, 0x0000 },
+  { 0x8d01, 0xd7d6, 0x2000 },
+  { 0x0d01, 0xd7d5, 0x0000 },
+  { 0x0d01, 0xd7d7, 0x0000 },
+  { 0x8d01, 0xd7e8, 0x5000 },
+  { 0x8d01, 0xd7e0, 0x4000 },
+  { 0x8d01, 0xd7dc, 0x3000 },
+  { 0x8d01, 0xd7da, 0x2000 },
+  { 0x0d01, 0xd7d9, 0x0000 },
+  { 0x0d01, 0xd7db, 0x0000 },
+  { 0x8d01, 0xd7de, 0x2000 },
+  { 0x0d01, 0xd7dd, 0x0000 },
+  { 0x0d01, 0xd7df, 0x0000 },
+  { 0x8d01, 0xd7e4, 0x3000 },
+  { 0x8d01, 0xd7e2, 0x2000 },
+  { 0x0d01, 0xd7e1, 0x0000 },
+  { 0x0d01, 0xd7e3, 0x0000 },
+  { 0x8d01, 0xd7e6, 0x2000 },
+  { 0x0d01, 0xd7e5, 0x0000 },
+  { 0x0d01, 0xd7e7, 0x0000 },
+  { 0x8d01, 0xd7f0, 0x4000 },
+  { 0x8d01, 0xd7ec, 0x3000 },
+  { 0x8d01, 0xd7ea, 0x2000 },
+  { 0x0d01, 0xd7e9, 0x0000 },
+  { 0x0d01, 0xd7eb, 0x0000 },
+  { 0x8d01, 0xd7ee, 0x2000 },
+  { 0x0d01, 0xd7ed, 0x0000 },
+  { 0x0d01, 0xd7ef, 0x0000 },
+  { 0x8d01, 0xd7f4, 0x3000 },
+  { 0x8d01, 0xd7f2, 0x2000 },
+  { 0x0d01, 0xd7f1, 0x0000 },
+  { 0x0d01, 0xd7f3, 0x0000 },
+  { 0x8d01, 0xd7f6, 0x2000 },
+  { 0x0d01, 0xd7f5, 0x0000 },
+  { 0x0d01, 0xd7f7, 0x0000 },
+  { 0x8702, 0xf836, 0x7000 },
+  { 0x8702, 0xf816, 0x6000 },
+  { 0x8702, 0xf806, 0x5000 },
+  { 0x8702, 0x0000, 0x4000 },
+  { 0x8d01, 0xd7fc, 0x3000 },
+  { 0x8d01, 0xd7fa, 0x2000 },
+  { 0x0d01, 0xd7f9, 0x0000 },
+  { 0x0d01, 0xd7fb, 0x0000 },
+  { 0x8d01, 0xd7fe, 0x2000 },
+  { 0x0d01, 0xd7fd, 0x0000 },
+  { 0x0d01, 0xd7ff, 0x0000 },
+  { 0x8702, 0xf802, 0x3000 },
+  { 0x8702, 0xf800, 0x2000 },
+  { 0x0702, 0xa6d6, 0x0000 },
+  { 0x0702, 0xf801, 0x0000 },
+  { 0x8702, 0xf804, 0x2000 },
+  { 0x0702, 0xf803, 0x0000 },
+  { 0x0702, 0xf805, 0x0000 },
+  { 0x8702, 0xf80e, 0x4000 },
+  { 0x8702, 0xf80a, 0x3000 },
+  { 0x8702, 0xf808, 0x2000 },
+  { 0x0702, 0xf807, 0x0000 },
+  { 0x0702, 0xf809, 0x0000 },
+  { 0x8702, 0xf80c, 0x2000 },
+  { 0x0702, 0xf80b, 0x0000 },
+  { 0x0702, 0xf80d, 0x0000 },
+  { 0x8702, 0xf812, 0x3000 },
+  { 0x8702, 0xf810, 0x2000 },
+  { 0x0702, 0xf80f, 0x0000 },
+  { 0x0702, 0xf811, 0x0000 },
+  { 0x8702, 0xf814, 0x2000 },
+  { 0x0702, 0xf813, 0x0000 },
+  { 0x0702, 0xf815, 0x0000 },
+  { 0x8702, 0xf826, 0x5000 },
+  { 0x8702, 0xf81e, 0x4000 },
+  { 0x8702, 0xf81a, 0x3000 },
+  { 0x8702, 0xf818, 0x2000 },
+  { 0x0702, 0xf817, 0x0000 },
+  { 0x0702, 0xf819, 0x0000 },
+  { 0x8702, 0xf81c, 0x2000 },
+  { 0x0702, 0xf81b, 0x0000 },
+  { 0x0702, 0xf81d, 0x0000 },
+  { 0x8702, 0xf822, 0x3000 },
+  { 0x8702, 0xf820, 0x2000 },
+  { 0x0702, 0xf81f, 0x0000 },
+  { 0x0702, 0xf821, 0x0000 },
+  { 0x8702, 0xf824, 0x2000 },
+  { 0x0702, 0xf823, 0x0000 },
+  { 0x0702, 0xf825, 0x0000 },
+  { 0x8702, 0xf82e, 0x4000 },
+  { 0x8702, 0xf82a, 0x3000 },
+  { 0x8702, 0xf828, 0x2000 },
+  { 0x0702, 0xf827, 0x0000 },
+  { 0x0702, 0xf829, 0x0000 },
+  { 0x8702, 0xf82c, 0x2000 },
+  { 0x0702, 0xf82b, 0x0000 },
+  { 0x0702, 0xf82d, 0x0000 },
+  { 0x8702, 0xf832, 0x3000 },
+  { 0x8702, 0xf830, 0x2000 },
+  { 0x0702, 0xf82f, 0x0000 },
+  { 0x0702, 0xf831, 0x0000 },
+  { 0x8702, 0xf834, 0x2000 },
+  { 0x0702, 0xf833, 0x0000 },
+  { 0x0702, 0xf835, 0x0000 },
+  { 0x8702, 0xf856, 0x6000 },
+  { 0x8702, 0xf846, 0x5000 },
+  { 0x8702, 0xf83e, 0x4000 },
+  { 0x8702, 0xf83a, 0x3000 },
+  { 0x8702, 0xf838, 0x2000 },
+  { 0x0702, 0xf837, 0x0000 },
+  { 0x0702, 0xf839, 0x0000 },
+  { 0x8702, 0xf83c, 0x2000 },
+  { 0x0702, 0xf83b, 0x0000 },
+  { 0x0702, 0xf83d, 0x0000 },
+  { 0x8702, 0xf842, 0x3000 },
+  { 0x8702, 0xf840, 0x2000 },
+  { 0x0702, 0xf83f, 0x0000 },
+  { 0x0702, 0xf841, 0x0000 },
+  { 0x8702, 0xf844, 0x2000 },
+  { 0x0702, 0xf843, 0x0000 },
+  { 0x0702, 0xf845, 0x0000 },
+  { 0x8702, 0xf84e, 0x4000 },
+  { 0x8702, 0xf84a, 0x3000 },
+  { 0x8702, 0xf848, 0x2000 },
+  { 0x0702, 0xf847, 0x0000 },
+  { 0x0702, 0xf849, 0x0000 },
+  { 0x8702, 0xf84c, 0x2000 },
+  { 0x0702, 0xf84b, 0x0000 },
+  { 0x0702, 0xf84d, 0x0000 },
+  { 0x8702, 0xf852, 0x3000 },
+  { 0x8702, 0xf850, 0x2000 },
+  { 0x0702, 0xf84f, 0x0000 },
+  { 0x0702, 0xf851, 0x0000 },
+  { 0x8702, 0xf854, 0x2000 },
+  { 0x0702, 0xf853, 0x0000 },
+  { 0x0702, 0xf855, 0x0000 },
+  { 0x8702, 0xf866, 0x5000 },
+  { 0x8702, 0xf85e, 0x4000 },
+  { 0x8702, 0xf85a, 0x3000 },
+  { 0x8702, 0xf858, 0x2000 },
+  { 0x0702, 0xf857, 0x0000 },
+  { 0x0702, 0xf859, 0x0000 },
+  { 0x8702, 0xf85c, 0x2000 },
+  { 0x0702, 0xf85b, 0x0000 },
+  { 0x0702, 0xf85d, 0x0000 },
+  { 0x8702, 0xf862, 0x3000 },
+  { 0x8702, 0xf860, 0x2000 },
+  { 0x0702, 0xf85f, 0x0000 },
+  { 0x0702, 0xf861, 0x0000 },
+  { 0x8702, 0xf864, 0x2000 },
+  { 0x0702, 0xf863, 0x0000 },
+  { 0x0702, 0xf865, 0x0000 },
+  { 0x8702, 0xf86e, 0x4000 },
+  { 0x8702, 0xf86a, 0x3000 },
+  { 0x8702, 0xf868, 0x2000 },
+  { 0x0702, 0xf867, 0x0000 },
+  { 0x0702, 0xf869, 0x0000 },
+  { 0x8702, 0xf86c, 0x2000 },
+  { 0x0702, 0xf86b, 0x0000 },
+  { 0x0702, 0xf86d, 0x0000 },
+  { 0x8702, 0xf872, 0x3000 },
+  { 0x8702, 0xf870, 0x2000 },
+  { 0x0702, 0xf86f, 0x0000 },
+  { 0x0702, 0xf871, 0x0000 },
+  { 0x8702, 0xf874, 0x2000 },
+  { 0x0702, 0xf873, 0x0000 },
+  { 0x0702, 0xf875, 0x0000 },
+  { 0x8702, 0xf976, 0x9000 },
+  { 0x8702, 0xf8f6, 0x8000 },
+  { 0x8702, 0xf8b6, 0x7000 },
+  { 0x8702, 0xf896, 0x6000 },
+  { 0x8702, 0xf886, 0x5000 },
+  { 0x8702, 0xf87e, 0x4000 },
+  { 0x8702, 0xf87a, 0x3000 },
+  { 0x8702, 0xf878, 0x2000 },
+  { 0x0702, 0xf877, 0x0000 },
+  { 0x0702, 0xf879, 0x0000 },
+  { 0x8702, 0xf87c, 0x2000 },
+  { 0x0702, 0xf87b, 0x0000 },
+  { 0x0702, 0xf87d, 0x0000 },
+  { 0x8702, 0xf882, 0x3000 },
+  { 0x8702, 0xf880, 0x2000 },
+  { 0x0702, 0xf87f, 0x0000 },
+  { 0x0702, 0xf881, 0x0000 },
+  { 0x8702, 0xf884, 0x2000 },
+  { 0x0702, 0xf883, 0x0000 },
+  { 0x0702, 0xf885, 0x0000 },
+  { 0x8702, 0xf88e, 0x4000 },
+  { 0x8702, 0xf88a, 0x3000 },
+  { 0x8702, 0xf888, 0x2000 },
+  { 0x0702, 0xf887, 0x0000 },
+  { 0x0702, 0xf889, 0x0000 },
+  { 0x8702, 0xf88c, 0x2000 },
+  { 0x0702, 0xf88b, 0x0000 },
+  { 0x0702, 0xf88d, 0x0000 },
+  { 0x8702, 0xf892, 0x3000 },
+  { 0x8702, 0xf890, 0x2000 },
+  { 0x0702, 0xf88f, 0x0000 },
+  { 0x0702, 0xf891, 0x0000 },
+  { 0x8702, 0xf894, 0x2000 },
+  { 0x0702, 0xf893, 0x0000 },
+  { 0x0702, 0xf895, 0x0000 },
+  { 0x8702, 0xf8a6, 0x5000 },
+  { 0x8702, 0xf89e, 0x4000 },
+  { 0x8702, 0xf89a, 0x3000 },
+  { 0x8702, 0xf898, 0x2000 },
+  { 0x0702, 0xf897, 0x0000 },
+  { 0x0702, 0xf899, 0x0000 },
+  { 0x8702, 0xf89c, 0x2000 },
+  { 0x0702, 0xf89b, 0x0000 },
+  { 0x0702, 0xf89d, 0x0000 },
+  { 0x8702, 0xf8a2, 0x3000 },
+  { 0x8702, 0xf8a0, 0x2000 },
+  { 0x0702, 0xf89f, 0x0000 },
+  { 0x0702, 0xf8a1, 0x0000 },
+  { 0x8702, 0xf8a4, 0x2000 },
+  { 0x0702, 0xf8a3, 0x0000 },
+  { 0x0702, 0xf8a5, 0x0000 },
+  { 0x8702, 0xf8ae, 0x4000 },
+  { 0x8702, 0xf8aa, 0x3000 },
+  { 0x8702, 0xf8a8, 0x2000 },
+  { 0x0702, 0xf8a7, 0x0000 },
+  { 0x0702, 0xf8a9, 0x0000 },
+  { 0x8702, 0xf8ac, 0x2000 },
+  { 0x0702, 0xf8ab, 0x0000 },
+  { 0x0702, 0xf8ad, 0x0000 },
+  { 0x8702, 0xf8b2, 0x3000 },
+  { 0x8702, 0xf8b0, 0x2000 },
+  { 0x0702, 0xf8af, 0x0000 },
+  { 0x0702, 0xf8b1, 0x0000 },
+  { 0x8702, 0xf8b4, 0x2000 },
+  { 0x0702, 0xf8b3, 0x0000 },
+  { 0x0702, 0xf8b5, 0x0000 },
+  { 0x8702, 0xf8d6, 0x6000 },
+  { 0x8702, 0xf8c6, 0x5000 },
+  { 0x8702, 0xf8be, 0x4000 },
+  { 0x8702, 0xf8ba, 0x3000 },
+  { 0x8702, 0xf8b8, 0x2000 },
+  { 0x0702, 0xf8b7, 0x0000 },
+  { 0x0702, 0xf8b9, 0x0000 },
+  { 0x8702, 0xf8bc, 0x2000 },
+  { 0x0702, 0xf8bb, 0x0000 },
+  { 0x0702, 0xf8bd, 0x0000 },
+  { 0x8702, 0xf8c2, 0x3000 },
+  { 0x8702, 0xf8c0, 0x2000 },
+  { 0x0702, 0xf8bf, 0x0000 },
+  { 0x0702, 0xf8c1, 0x0000 },
+  { 0x8702, 0xf8c4, 0x2000 },
+  { 0x0702, 0xf8c3, 0x0000 },
+  { 0x0702, 0xf8c5, 0x0000 },
+  { 0x8702, 0xf8ce, 0x4000 },
+  { 0x8702, 0xf8ca, 0x3000 },
+  { 0x8702, 0xf8c8, 0x2000 },
+  { 0x0702, 0xf8c7, 0x0000 },
+  { 0x0702, 0xf8c9, 0x0000 },
+  { 0x8702, 0xf8cc, 0x2000 },
+  { 0x0702, 0xf8cb, 0x0000 },
+  { 0x0702, 0xf8cd, 0x0000 },
+  { 0x8702, 0xf8d2, 0x3000 },
+  { 0x8702, 0xf8d0, 0x2000 },
+  { 0x0702, 0xf8cf, 0x0000 },
+  { 0x0702, 0xf8d1, 0x0000 },
+  { 0x8702, 0xf8d4, 0x2000 },
+  { 0x0702, 0xf8d3, 0x0000 },
+  { 0x0702, 0xf8d5, 0x0000 },
+  { 0x8702, 0xf8e6, 0x5000 },
+  { 0x8702, 0xf8de, 0x4000 },
+  { 0x8702, 0xf8da, 0x3000 },
+  { 0x8702, 0xf8d8, 0x2000 },
+  { 0x0702, 0xf8d7, 0x0000 },
+  { 0x0702, 0xf8d9, 0x0000 },
+  { 0x8702, 0xf8dc, 0x2000 },
+  { 0x0702, 0xf8db, 0x0000 },
+  { 0x0702, 0xf8dd, 0x0000 },
+  { 0x8702, 0xf8e2, 0x3000 },
+  { 0x8702, 0xf8e0, 0x2000 },
+  { 0x0702, 0xf8df, 0x0000 },
+  { 0x0702, 0xf8e1, 0x0000 },
+  { 0x8702, 0xf8e4, 0x2000 },
+  { 0x0702, 0xf8e3, 0x0000 },
+  { 0x0702, 0xf8e5, 0x0000 },
+  { 0x8702, 0xf8ee, 0x4000 },
+  { 0x8702, 0xf8ea, 0x3000 },
+  { 0x8702, 0xf8e8, 0x2000 },
+  { 0x0702, 0xf8e7, 0x0000 },
+  { 0x0702, 0xf8e9, 0x0000 },
+  { 0x8702, 0xf8ec, 0x2000 },
+  { 0x0702, 0xf8eb, 0x0000 },
+  { 0x0702, 0xf8ed, 0x0000 },
+  { 0x8702, 0xf8f2, 0x3000 },
+  { 0x8702, 0xf8f0, 0x2000 },
+  { 0x0702, 0xf8ef, 0x0000 },
+  { 0x0702, 0xf8f1, 0x0000 },
+  { 0x8702, 0xf8f4, 0x2000 },
+  { 0x0702, 0xf8f3, 0x0000 },
+  { 0x0702, 0xf8f5, 0x0000 },
+  { 0x8702, 0xf936, 0x7000 },
+  { 0x8702, 0xf916, 0x6000 },
+  { 0x8702, 0xf906, 0x5000 },
+  { 0x8702, 0xf8fe, 0x4000 },
+  { 0x8702, 0xf8fa, 0x3000 },
+  { 0x8702, 0xf8f8, 0x2000 },
+  { 0x0702, 0xf8f7, 0x0000 },
+  { 0x0702, 0xf8f9, 0x0000 },
+  { 0x8702, 0xf8fc, 0x2000 },
+  { 0x0702, 0xf8fb, 0x0000 },
+  { 0x0702, 0xf8fd, 0x0000 },
+  { 0x8702, 0xf902, 0x3000 },
+  { 0x8702, 0xf900, 0x2000 },
+  { 0x0702, 0xf8ff, 0x0000 },
+  { 0x0702, 0xf901, 0x0000 },
+  { 0x8702, 0xf904, 0x2000 },
+  { 0x0702, 0xf903, 0x0000 },
+  { 0x0702, 0xf905, 0x0000 },
+  { 0x8702, 0xf90e, 0x4000 },
+  { 0x8702, 0xf90a, 0x3000 },
+  { 0x8702, 0xf908, 0x2000 },
+  { 0x0702, 0xf907, 0x0000 },
+  { 0x0702, 0xf909, 0x0000 },
+  { 0x8702, 0xf90c, 0x2000 },
+  { 0x0702, 0xf90b, 0x0000 },
+  { 0x0702, 0xf90d, 0x0000 },
+  { 0x8702, 0xf912, 0x3000 },
+  { 0x8702, 0xf910, 0x2000 },
+  { 0x0702, 0xf90f, 0x0000 },
+  { 0x0702, 0xf911, 0x0000 },
+  { 0x8702, 0xf914, 0x2000 },
+  { 0x0702, 0xf913, 0x0000 },
+  { 0x0702, 0xf915, 0x0000 },
+  { 0x8702, 0xf926, 0x5000 },
+  { 0x8702, 0xf91e, 0x4000 },
+  { 0x8702, 0xf91a, 0x3000 },
+  { 0x8702, 0xf918, 0x2000 },
+  { 0x0702, 0xf917, 0x0000 },
+  { 0x0702, 0xf919, 0x0000 },
+  { 0x8702, 0xf91c, 0x2000 },
+  { 0x0702, 0xf91b, 0x0000 },
+  { 0x0702, 0xf91d, 0x0000 },
+  { 0x8702, 0xf922, 0x3000 },
+  { 0x8702, 0xf920, 0x2000 },
+  { 0x0702, 0xf91f, 0x0000 },
+  { 0x0702, 0xf921, 0x0000 },
+  { 0x8702, 0xf924, 0x2000 },
+  { 0x0702, 0xf923, 0x0000 },
+  { 0x0702, 0xf925, 0x0000 },
+  { 0x8702, 0xf92e, 0x4000 },
+  { 0x8702, 0xf92a, 0x3000 },
+  { 0x8702, 0xf928, 0x2000 },
+  { 0x0702, 0xf927, 0x0000 },
+  { 0x0702, 0xf929, 0x0000 },
+  { 0x8702, 0xf92c, 0x2000 },
+  { 0x0702, 0xf92b, 0x0000 },
+  { 0x0702, 0xf92d, 0x0000 },
+  { 0x8702, 0xf932, 0x3000 },
+  { 0x8702, 0xf930, 0x2000 },
+  { 0x0702, 0xf92f, 0x0000 },
+  { 0x0702, 0xf931, 0x0000 },
+  { 0x8702, 0xf934, 0x2000 },
+  { 0x0702, 0xf933, 0x0000 },
+  { 0x0702, 0xf935, 0x0000 },
+  { 0x8702, 0xf956, 0x6000 },
+  { 0x8702, 0xf946, 0x5000 },
+  { 0x8702, 0xf93e, 0x4000 },
+  { 0x8702, 0xf93a, 0x3000 },
+  { 0x8702, 0xf938, 0x2000 },
+  { 0x0702, 0xf937, 0x0000 },
+  { 0x0702, 0xf939, 0x0000 },
+  { 0x8702, 0xf93c, 0x2000 },
+  { 0x0702, 0xf93b, 0x0000 },
+  { 0x0702, 0xf93d, 0x0000 },
+  { 0x8702, 0xf942, 0x3000 },
+  { 0x8702, 0xf940, 0x2000 },
+  { 0x0702, 0xf93f, 0x0000 },
+  { 0x0702, 0xf941, 0x0000 },
+  { 0x8702, 0xf944, 0x2000 },
+  { 0x0702, 0xf943, 0x0000 },
+  { 0x0702, 0xf945, 0x0000 },
+  { 0x8702, 0xf94e, 0x4000 },
+  { 0x8702, 0xf94a, 0x3000 },
+  { 0x8702, 0xf948, 0x2000 },
+  { 0x0702, 0xf947, 0x0000 },
+  { 0x0702, 0xf949, 0x0000 },
+  { 0x8702, 0xf94c, 0x2000 },
+  { 0x0702, 0xf94b, 0x0000 },
+  { 0x0702, 0xf94d, 0x0000 },
+  { 0x8702, 0xf952, 0x3000 },
+  { 0x8702, 0xf950, 0x2000 },
+  { 0x0702, 0xf94f, 0x0000 },
+  { 0x0702, 0xf951, 0x0000 },
+  { 0x8702, 0xf954, 0x2000 },
+  { 0x0702, 0xf953, 0x0000 },
+  { 0x0702, 0xf955, 0x0000 },
+  { 0x8702, 0xf966, 0x5000 },
+  { 0x8702, 0xf95e, 0x4000 },
+  { 0x8702, 0xf95a, 0x3000 },
+  { 0x8702, 0xf958, 0x2000 },
+  { 0x0702, 0xf957, 0x0000 },
+  { 0x0702, 0xf959, 0x0000 },
+  { 0x8702, 0xf95c, 0x2000 },
+  { 0x0702, 0xf95b, 0x0000 },
+  { 0x0702, 0xf95d, 0x0000 },
+  { 0x8702, 0xf962, 0x3000 },
+  { 0x8702, 0xf960, 0x2000 },
+  { 0x0702, 0xf95f, 0x0000 },
+  { 0x0702, 0xf961, 0x0000 },
+  { 0x8702, 0xf964, 0x2000 },
+  { 0x0702, 0xf963, 0x0000 },
+  { 0x0702, 0xf965, 0x0000 },
+  { 0x8702, 0xf96e, 0x4000 },
+  { 0x8702, 0xf96a, 0x3000 },
+  { 0x8702, 0xf968, 0x2000 },
+  { 0x0702, 0xf967, 0x0000 },
+  { 0x0702, 0xf969, 0x0000 },
+  { 0x8702, 0xf96c, 0x2000 },
+  { 0x0702, 0xf96b, 0x0000 },
+  { 0x0702, 0xf96d, 0x0000 },
+  { 0x8702, 0xf972, 0x3000 },
+  { 0x8702, 0xf970, 0x2000 },
+  { 0x0702, 0xf96f, 0x0000 },
+  { 0x0702, 0xf971, 0x0000 },
+  { 0x8702, 0xf974, 0x2000 },
+  { 0x0702, 0xf973, 0x0000 },
+  { 0x0702, 0xf975, 0x0000 },
+  { 0x810e, 0x0077, 0x9000 },
+  { 0x8702, 0xf9f6, 0x8000 },
+  { 0x8702, 0xf9b6, 0x7000 },
+  { 0x8702, 0xf996, 0x6000 },
+  { 0x8702, 0xf986, 0x5000 },
+  { 0x8702, 0xf97e, 0x4000 },
+  { 0x8702, 0xf97a, 0x3000 },
+  { 0x8702, 0xf978, 0x2000 },
+  { 0x0702, 0xf977, 0x0000 },
+  { 0x0702, 0xf979, 0x0000 },
+  { 0x8702, 0xf97c, 0x2000 },
+  { 0x0702, 0xf97b, 0x0000 },
+  { 0x0702, 0xf97d, 0x0000 },
+  { 0x8702, 0xf982, 0x3000 },
+  { 0x8702, 0xf980, 0x2000 },
+  { 0x0702, 0xf97f, 0x0000 },
+  { 0x0702, 0xf981, 0x0000 },
+  { 0x8702, 0xf984, 0x2000 },
+  { 0x0702, 0xf983, 0x0000 },
+  { 0x0702, 0xf985, 0x0000 },
+  { 0x8702, 0xf98e, 0x4000 },
+  { 0x8702, 0xf98a, 0x3000 },
+  { 0x8702, 0xf988, 0x2000 },
+  { 0x0702, 0xf987, 0x0000 },
+  { 0x0702, 0xf989, 0x0000 },
+  { 0x8702, 0xf98c, 0x2000 },
+  { 0x0702, 0xf98b, 0x0000 },
+  { 0x0702, 0xf98d, 0x0000 },
+  { 0x8702, 0xf992, 0x3000 },
+  { 0x8702, 0xf990, 0x2000 },
+  { 0x0702, 0xf98f, 0x0000 },
+  { 0x0702, 0xf991, 0x0000 },
+  { 0x8702, 0xf994, 0x2000 },
+  { 0x0702, 0xf993, 0x0000 },
+  { 0x0702, 0xf995, 0x0000 },
+  { 0x8702, 0xf9a6, 0x5000 },
+  { 0x8702, 0xf99e, 0x4000 },
+  { 0x8702, 0xf99a, 0x3000 },
+  { 0x8702, 0xf998, 0x2000 },
+  { 0x0702, 0xf997, 0x0000 },
+  { 0x0702, 0xf999, 0x0000 },
+  { 0x8702, 0xf99c, 0x2000 },
+  { 0x0702, 0xf99b, 0x0000 },
+  { 0x0702, 0xf99d, 0x0000 },
+  { 0x8702, 0xf9a2, 0x3000 },
+  { 0x8702, 0xf9a0, 0x2000 },
+  { 0x0702, 0xf99f, 0x0000 },
+  { 0x0702, 0xf9a1, 0x0000 },
+  { 0x8702, 0xf9a4, 0x2000 },
+  { 0x0702, 0xf9a3, 0x0000 },
+  { 0x0702, 0xf9a5, 0x0000 },
+  { 0x8702, 0xf9ae, 0x4000 },
+  { 0x8702, 0xf9aa, 0x3000 },
+  { 0x8702, 0xf9a8, 0x2000 },
+  { 0x0702, 0xf9a7, 0x0000 },
+  { 0x0702, 0xf9a9, 0x0000 },
+  { 0x8702, 0xf9ac, 0x2000 },
+  { 0x0702, 0xf9ab, 0x0000 },
+  { 0x0702, 0xf9ad, 0x0000 },
+  { 0x8702, 0xf9b2, 0x3000 },
+  { 0x8702, 0xf9b0, 0x2000 },
+  { 0x0702, 0xf9af, 0x0000 },
+  { 0x0702, 0xf9b1, 0x0000 },
+  { 0x8702, 0xf9b4, 0x2000 },
+  { 0x0702, 0xf9b3, 0x0000 },
+  { 0x0702, 0xf9b5, 0x0000 },
+  { 0x8702, 0xf9d6, 0x6000 },
+  { 0x8702, 0xf9c6, 0x5000 },
+  { 0x8702, 0xf9be, 0x4000 },
+  { 0x8702, 0xf9ba, 0x3000 },
+  { 0x8702, 0xf9b8, 0x2000 },
+  { 0x0702, 0xf9b7, 0x0000 },
+  { 0x0702, 0xf9b9, 0x0000 },
+  { 0x8702, 0xf9bc, 0x2000 },
+  { 0x0702, 0xf9bb, 0x0000 },
+  { 0x0702, 0xf9bd, 0x0000 },
+  { 0x8702, 0xf9c2, 0x3000 },
+  { 0x8702, 0xf9c0, 0x2000 },
+  { 0x0702, 0xf9bf, 0x0000 },
+  { 0x0702, 0xf9c1, 0x0000 },
+  { 0x8702, 0xf9c4, 0x2000 },
+  { 0x0702, 0xf9c3, 0x0000 },
+  { 0x0702, 0xf9c5, 0x0000 },
+  { 0x8702, 0xf9ce, 0x4000 },
+  { 0x8702, 0xf9ca, 0x3000 },
+  { 0x8702, 0xf9c8, 0x2000 },
+  { 0x0702, 0xf9c7, 0x0000 },
+  { 0x0702, 0xf9c9, 0x0000 },
+  { 0x8702, 0xf9cc, 0x2000 },
+  { 0x0702, 0xf9cb, 0x0000 },
+  { 0x0702, 0xf9cd, 0x0000 },
+  { 0x8702, 0xf9d2, 0x3000 },
+  { 0x8702, 0xf9d0, 0x2000 },
+  { 0x0702, 0xf9cf, 0x0000 },
+  { 0x0702, 0xf9d1, 0x0000 },
+  { 0x8702, 0xf9d4, 0x2000 },
+  { 0x0702, 0xf9d3, 0x0000 },
+  { 0x0702, 0xf9d5, 0x0000 },
+  { 0x8702, 0xf9e6, 0x5000 },
+  { 0x8702, 0xf9de, 0x4000 },
+  { 0x8702, 0xf9da, 0x3000 },
+  { 0x8702, 0xf9d8, 0x2000 },
+  { 0x0702, 0xf9d7, 0x0000 },
+  { 0x0702, 0xf9d9, 0x0000 },
+  { 0x8702, 0xf9dc, 0x2000 },
+  { 0x0702, 0xf9db, 0x0000 },
+  { 0x0702, 0xf9dd, 0x0000 },
+  { 0x8702, 0xf9e2, 0x3000 },
+  { 0x8702, 0xf9e0, 0x2000 },
+  { 0x0702, 0xf9df, 0x0000 },
+  { 0x0702, 0xf9e1, 0x0000 },
+  { 0x8702, 0xf9e4, 0x2000 },
+  { 0x0702, 0xf9e3, 0x0000 },
+  { 0x0702, 0xf9e5, 0x0000 },
+  { 0x8702, 0xf9ee, 0x4000 },
+  { 0x8702, 0xf9ea, 0x3000 },
+  { 0x8702, 0xf9e8, 0x2000 },
+  { 0x0702, 0xf9e7, 0x0000 },
+  { 0x0702, 0xf9e9, 0x0000 },
+  { 0x8702, 0xf9ec, 0x2000 },
+  { 0x0702, 0xf9eb, 0x0000 },
+  { 0x0702, 0xf9ed, 0x0000 },
+  { 0x8702, 0xf9f2, 0x3000 },
+  { 0x8702, 0xf9f0, 0x2000 },
+  { 0x0702, 0xf9ef, 0x0000 },
+  { 0x0702, 0xf9f1, 0x0000 },
+  { 0x8702, 0xf9f4, 0x2000 },
+  { 0x0702, 0xf9f3, 0x0000 },
+  { 0x0702, 0xf9f5, 0x0000 },
+  { 0x810e, 0x0037, 0x7000 },
+  { 0x8702, 0xfa16, 0x6000 },
+  { 0x8702, 0xfa06, 0x5000 },
+  { 0x8702, 0xf9fe, 0x4000 },
+  { 0x8702, 0xf9fa, 0x3000 },
+  { 0x8702, 0xf9f8, 0x2000 },
+  { 0x0702, 0xf9f7, 0x0000 },
+  { 0x0702, 0xf9f9, 0x0000 },
+  { 0x8702, 0xf9fc, 0x2000 },
+  { 0x0702, 0xf9fb, 0x0000 },
+  { 0x0702, 0xf9fd, 0x0000 },
+  { 0x8702, 0xfa02, 0x3000 },
+  { 0x8702, 0xfa00, 0x2000 },
+  { 0x0702, 0xf9ff, 0x0000 },
+  { 0x0702, 0xfa01, 0x0000 },
+  { 0x8702, 0xfa04, 0x2000 },
+  { 0x0702, 0xfa03, 0x0000 },
+  { 0x0702, 0xfa05, 0x0000 },
+  { 0x8702, 0xfa0e, 0x4000 },
+  { 0x8702, 0xfa0a, 0x3000 },
+  { 0x8702, 0xfa08, 0x2000 },
+  { 0x0702, 0xfa07, 0x0000 },
+  { 0x0702, 0xfa09, 0x0000 },
+  { 0x8702, 0xfa0c, 0x2000 },
+  { 0x0702, 0xfa0b, 0x0000 },
+  { 0x0702, 0xfa0d, 0x0000 },
+  { 0x8702, 0xfa12, 0x3000 },
+  { 0x8702, 0xfa10, 0x2000 },
+  { 0x0702, 0xfa0f, 0x0000 },
+  { 0x0702, 0xfa11, 0x0000 },
+  { 0x8702, 0xfa14, 0x2000 },
+  { 0x0702, 0xfa13, 0x0000 },
+  { 0x0702, 0xfa15, 0x0000 },
+  { 0x810e, 0x0027, 0x5000 },
+  { 0x810e, 0x0001, 0x4000 },
+  { 0x8702, 0xfa1a, 0x3000 },
+  { 0x8702, 0xfa18, 0x2000 },
+  { 0x0702, 0xfa17, 0x0000 },
+  { 0x0702, 0xfa19, 0x0000 },
+  { 0x8702, 0xfa1c, 0x2000 },
+  { 0x0702, 0xfa1b, 0x0000 },
+  { 0x0702, 0xfa1d, 0x0000 },
+  { 0x810e, 0x0023, 0x3000 },
+  { 0x810e, 0x0021, 0x2000 },
+  { 0x010e, 0x0020, 0x0000 },
+  { 0x010e, 0x0022, 0x0000 },
+  { 0x810e, 0x0025, 0x2000 },
+  { 0x010e, 0x0024, 0x0000 },
+  { 0x010e, 0x0026, 0x0000 },
+  { 0x810e, 0x002f, 0x4000 },
+  { 0x810e, 0x002b, 0x3000 },
+  { 0x810e, 0x0029, 0x2000 },
+  { 0x010e, 0x0028, 0x0000 },
+  { 0x010e, 0x002a, 0x0000 },
+  { 0x810e, 0x002d, 0x2000 },
+  { 0x010e, 0x002c, 0x0000 },
+  { 0x010e, 0x002e, 0x0000 },
+  { 0x810e, 0x0033, 0x3000 },
+  { 0x810e, 0x0031, 0x2000 },
+  { 0x010e, 0x0030, 0x0000 },
+  { 0x010e, 0x0032, 0x0000 },
+  { 0x810e, 0x0035, 0x2000 },
+  { 0x010e, 0x0034, 0x0000 },
+  { 0x010e, 0x0036, 0x0000 },
+  { 0x810e, 0x0057, 0x6000 },
+  { 0x810e, 0x0047, 0x5000 },
+  { 0x810e, 0x003f, 0x4000 },
+  { 0x810e, 0x003b, 0x3000 },
+  { 0x810e, 0x0039, 0x2000 },
+  { 0x010e, 0x0038, 0x0000 },
+  { 0x010e, 0x003a, 0x0000 },
+  { 0x810e, 0x003d, 0x2000 },
+  { 0x010e, 0x003c, 0x0000 },
+  { 0x010e, 0x003e, 0x0000 },
+  { 0x810e, 0x0043, 0x3000 },
+  { 0x810e, 0x0041, 0x2000 },
+  { 0x010e, 0x0040, 0x0000 },
+  { 0x010e, 0x0042, 0x0000 },
+  { 0x810e, 0x0045, 0x2000 },
+  { 0x010e, 0x0044, 0x0000 },
+  { 0x010e, 0x0046, 0x0000 },
+  { 0x810e, 0x004f, 0x4000 },
+  { 0x810e, 0x004b, 0x3000 },
+  { 0x810e, 0x0049, 0x2000 },
+  { 0x010e, 0x0048, 0x0000 },
+  { 0x010e, 0x004a, 0x0000 },
+  { 0x810e, 0x004d, 0x2000 },
+  { 0x010e, 0x004c, 0x0000 },
+  { 0x010e, 0x004e, 0x0000 },
+  { 0x810e, 0x0053, 0x3000 },
+  { 0x810e, 0x0051, 0x2000 },
+  { 0x010e, 0x0050, 0x0000 },
+  { 0x010e, 0x0052, 0x0000 },
+  { 0x810e, 0x0055, 0x2000 },
+  { 0x010e, 0x0054, 0x0000 },
+  { 0x010e, 0x0056, 0x0000 },
+  { 0x810e, 0x0067, 0x5000 },
+  { 0x810e, 0x005f, 0x4000 },
+  { 0x810e, 0x005b, 0x3000 },
+  { 0x810e, 0x0059, 0x2000 },
+  { 0x010e, 0x0058, 0x0000 },
+  { 0x010e, 0x005a, 0x0000 },
+  { 0x810e, 0x005d, 0x2000 },
+  { 0x010e, 0x005c, 0x0000 },
+  { 0x010e, 0x005e, 0x0000 },
+  { 0x810e, 0x0063, 0x3000 },
+  { 0x810e, 0x0061, 0x2000 },
+  { 0x010e, 0x0060, 0x0000 },
+  { 0x010e, 0x0062, 0x0000 },
+  { 0x810e, 0x0065, 0x2000 },
+  { 0x010e, 0x0064, 0x0000 },
+  { 0x010e, 0x0066, 0x0000 },
+  { 0x810e, 0x006f, 0x4000 },
+  { 0x810e, 0x006b, 0x3000 },
+  { 0x810e, 0x0069, 0x2000 },
+  { 0x010e, 0x0068, 0x0000 },
+  { 0x010e, 0x006a, 0x0000 },
+  { 0x810e, 0x006d, 0x2000 },
+  { 0x010e, 0x006c, 0x0000 },
+  { 0x010e, 0x006e, 0x0000 },
+  { 0x810e, 0x0073, 0x3000 },
+  { 0x810e, 0x0071, 0x2000 },
+  { 0x010e, 0x0070, 0x0000 },
+  { 0x010e, 0x0072, 0x0000 },
+  { 0x810e, 0x0075, 0x2000 },
+  { 0x010e, 0x0074, 0x0000 },
+  { 0x010e, 0x0076, 0x0000 },
+  { 0x8c0e, 0x0177, 0x8000 },
+  { 0x8c0e, 0x0137, 0x7000 },
+  { 0x8c0e, 0x0117, 0x6000 },
+  { 0x8c0e, 0x0107, 0x5000 },
+  { 0x810e, 0x007f, 0x4000 },
+  { 0x810e, 0x007b, 0x3000 },
+  { 0x810e, 0x0079, 0x2000 },
+  { 0x010e, 0x0078, 0x0000 },
+  { 0x010e, 0x007a, 0x0000 },
+  { 0x810e, 0x007d, 0x2000 },
+  { 0x010e, 0x007c, 0x0000 },
+  { 0x010e, 0x007e, 0x0000 },
+  { 0x8c0e, 0x0103, 0x3000 },
+  { 0x8c0e, 0x0101, 0x2000 },
+  { 0x0c0e, 0x0100, 0x0000 },
+  { 0x0c0e, 0x0102, 0x0000 },
+  { 0x8c0e, 0x0105, 0x2000 },
+  { 0x0c0e, 0x0104, 0x0000 },
+  { 0x0c0e, 0x0106, 0x0000 },
+  { 0x8c0e, 0x010f, 0x4000 },
+  { 0x8c0e, 0x010b, 0x3000 },
+  { 0x8c0e, 0x0109, 0x2000 },
+  { 0x0c0e, 0x0108, 0x0000 },
+  { 0x0c0e, 0x010a, 0x0000 },
+  { 0x8c0e, 0x010d, 0x2000 },
+  { 0x0c0e, 0x010c, 0x0000 },
+  { 0x0c0e, 0x010e, 0x0000 },
+  { 0x8c0e, 0x0113, 0x3000 },
+  { 0x8c0e, 0x0111, 0x2000 },
+  { 0x0c0e, 0x0110, 0x0000 },
+  { 0x0c0e, 0x0112, 0x0000 },
+  { 0x8c0e, 0x0115, 0x2000 },
+  { 0x0c0e, 0x0114, 0x0000 },
+  { 0x0c0e, 0x0116, 0x0000 },
+  { 0x8c0e, 0x0127, 0x5000 },
+  { 0x8c0e, 0x011f, 0x4000 },
+  { 0x8c0e, 0x011b, 0x3000 },
+  { 0x8c0e, 0x0119, 0x2000 },
+  { 0x0c0e, 0x0118, 0x0000 },
+  { 0x0c0e, 0x011a, 0x0000 },
+  { 0x8c0e, 0x011d, 0x2000 },
+  { 0x0c0e, 0x011c, 0x0000 },
+  { 0x0c0e, 0x011e, 0x0000 },
+  { 0x8c0e, 0x0123, 0x3000 },
+  { 0x8c0e, 0x0121, 0x2000 },
+  { 0x0c0e, 0x0120, 0x0000 },
+  { 0x0c0e, 0x0122, 0x0000 },
+  { 0x8c0e, 0x0125, 0x2000 },
+  { 0x0c0e, 0x0124, 0x0000 },
+  { 0x0c0e, 0x0126, 0x0000 },
+  { 0x8c0e, 0x012f, 0x4000 },
+  { 0x8c0e, 0x012b, 0x3000 },
+  { 0x8c0e, 0x0129, 0x2000 },
+  { 0x0c0e, 0x0128, 0x0000 },
+  { 0x0c0e, 0x012a, 0x0000 },
+  { 0x8c0e, 0x012d, 0x2000 },
+  { 0x0c0e, 0x012c, 0x0000 },
+  { 0x0c0e, 0x012e, 0x0000 },
+  { 0x8c0e, 0x0133, 0x3000 },
+  { 0x8c0e, 0x0131, 0x2000 },
+  { 0x0c0e, 0x0130, 0x0000 },
+  { 0x0c0e, 0x0132, 0x0000 },
+  { 0x8c0e, 0x0135, 0x2000 },
+  { 0x0c0e, 0x0134, 0x0000 },
+  { 0x0c0e, 0x0136, 0x0000 },
+  { 0x8c0e, 0x0157, 0x6000 },
+  { 0x8c0e, 0x0147, 0x5000 },
+  { 0x8c0e, 0x013f, 0x4000 },
+  { 0x8c0e, 0x013b, 0x3000 },
+  { 0x8c0e, 0x0139, 0x2000 },
+  { 0x0c0e, 0x0138, 0x0000 },
+  { 0x0c0e, 0x013a, 0x0000 },
+  { 0x8c0e, 0x013d, 0x2000 },
+  { 0x0c0e, 0x013c, 0x0000 },
+  { 0x0c0e, 0x013e, 0x0000 },
+  { 0x8c0e, 0x0143, 0x3000 },
+  { 0x8c0e, 0x0141, 0x2000 },
+  { 0x0c0e, 0x0140, 0x0000 },
+  { 0x0c0e, 0x0142, 0x0000 },
+  { 0x8c0e, 0x0145, 0x2000 },
+  { 0x0c0e, 0x0144, 0x0000 },
+  { 0x0c0e, 0x0146, 0x0000 },
+  { 0x8c0e, 0x014f, 0x4000 },
+  { 0x8c0e, 0x014b, 0x3000 },
+  { 0x8c0e, 0x0149, 0x2000 },
+  { 0x0c0e, 0x0148, 0x0000 },
+  { 0x0c0e, 0x014a, 0x0000 },
+  { 0x8c0e, 0x014d, 0x2000 },
+  { 0x0c0e, 0x014c, 0x0000 },
+  { 0x0c0e, 0x014e, 0x0000 },
+  { 0x8c0e, 0x0153, 0x3000 },
+  { 0x8c0e, 0x0151, 0x2000 },
+  { 0x0c0e, 0x0150, 0x0000 },
+  { 0x0c0e, 0x0152, 0x0000 },
+  { 0x8c0e, 0x0155, 0x2000 },
+  { 0x0c0e, 0x0154, 0x0000 },
+  { 0x0c0e, 0x0156, 0x0000 },
+  { 0x8c0e, 0x0167, 0x5000 },
+  { 0x8c0e, 0x015f, 0x4000 },
+  { 0x8c0e, 0x015b, 0x3000 },
+  { 0x8c0e, 0x0159, 0x2000 },
+  { 0x0c0e, 0x0158, 0x0000 },
+  { 0x0c0e, 0x015a, 0x0000 },
+  { 0x8c0e, 0x015d, 0x2000 },
+  { 0x0c0e, 0x015c, 0x0000 },
+  { 0x0c0e, 0x015e, 0x0000 },
+  { 0x8c0e, 0x0163, 0x3000 },
+  { 0x8c0e, 0x0161, 0x2000 },
+  { 0x0c0e, 0x0160, 0x0000 },
+  { 0x0c0e, 0x0162, 0x0000 },
+  { 0x8c0e, 0x0165, 0x2000 },
+  { 0x0c0e, 0x0164, 0x0000 },
+  { 0x0c0e, 0x0166, 0x0000 },
+  { 0x8c0e, 0x016f, 0x4000 },
+  { 0x8c0e, 0x016b, 0x3000 },
+  { 0x8c0e, 0x0169, 0x2000 },
+  { 0x0c0e, 0x0168, 0x0000 },
+  { 0x0c0e, 0x016a, 0x0000 },
+  { 0x8c0e, 0x016d, 0x2000 },
+  { 0x0c0e, 0x016c, 0x0000 },
+  { 0x0c0e, 0x016e, 0x0000 },
+  { 0x8c0e, 0x0173, 0x3000 },
+  { 0x8c0e, 0x0171, 0x2000 },
+  { 0x0c0e, 0x0170, 0x0000 },
+  { 0x0c0e, 0x0172, 0x0000 },
+  { 0x8c0e, 0x0175, 0x2000 },
+  { 0x0c0e, 0x0174, 0x0000 },
+  { 0x0c0e, 0x0176, 0x0000 },
+  { 0x8c0e, 0x01b7, 0x7000 },
+  { 0x8c0e, 0x0197, 0x6000 },
+  { 0x8c0e, 0x0187, 0x5000 },
+  { 0x8c0e, 0x017f, 0x4000 },
+  { 0x8c0e, 0x017b, 0x3000 },
+  { 0x8c0e, 0x0179, 0x2000 },
+  { 0x0c0e, 0x0178, 0x0000 },
+  { 0x0c0e, 0x017a, 0x0000 },
+  { 0x8c0e, 0x017d, 0x2000 },
+  { 0x0c0e, 0x017c, 0x0000 },
+  { 0x0c0e, 0x017e, 0x0000 },
+  { 0x8c0e, 0x0183, 0x3000 },
+  { 0x8c0e, 0x0181, 0x2000 },
+  { 0x0c0e, 0x0180, 0x0000 },
+  { 0x0c0e, 0x0182, 0x0000 },
+  { 0x8c0e, 0x0185, 0x2000 },
+  { 0x0c0e, 0x0184, 0x0000 },
+  { 0x0c0e, 0x0186, 0x0000 },
+  { 0x8c0e, 0x018f, 0x4000 },
+  { 0x8c0e, 0x018b, 0x3000 },
+  { 0x8c0e, 0x0189, 0x2000 },
+  { 0x0c0e, 0x0188, 0x0000 },
+  { 0x0c0e, 0x018a, 0x0000 },
+  { 0x8c0e, 0x018d, 0x2000 },
+  { 0x0c0e, 0x018c, 0x0000 },
+  { 0x0c0e, 0x018e, 0x0000 },
+  { 0x8c0e, 0x0193, 0x3000 },
+  { 0x8c0e, 0x0191, 0x2000 },
+  { 0x0c0e, 0x0190, 0x0000 },
+  { 0x0c0e, 0x0192, 0x0000 },
+  { 0x8c0e, 0x0195, 0x2000 },
+  { 0x0c0e, 0x0194, 0x0000 },
+  { 0x0c0e, 0x0196, 0x0000 },
+  { 0x8c0e, 0x01a7, 0x5000 },
+  { 0x8c0e, 0x019f, 0x4000 },
+  { 0x8c0e, 0x019b, 0x3000 },
+  { 0x8c0e, 0x0199, 0x2000 },
+  { 0x0c0e, 0x0198, 0x0000 },
+  { 0x0c0e, 0x019a, 0x0000 },
+  { 0x8c0e, 0x019d, 0x2000 },
+  { 0x0c0e, 0x019c, 0x0000 },
+  { 0x0c0e, 0x019e, 0x0000 },
+  { 0x8c0e, 0x01a3, 0x3000 },
+  { 0x8c0e, 0x01a1, 0x2000 },
+  { 0x0c0e, 0x01a0, 0x0000 },
+  { 0x0c0e, 0x01a2, 0x0000 },
+  { 0x8c0e, 0x01a5, 0x2000 },
+  { 0x0c0e, 0x01a4, 0x0000 },
+  { 0x0c0e, 0x01a6, 0x0000 },
+  { 0x8c0e, 0x01af, 0x4000 },
+  { 0x8c0e, 0x01ab, 0x3000 },
+  { 0x8c0e, 0x01a9, 0x2000 },
+  { 0x0c0e, 0x01a8, 0x0000 },
+  { 0x0c0e, 0x01aa, 0x0000 },
+  { 0x8c0e, 0x01ad, 0x2000 },
+  { 0x0c0e, 0x01ac, 0x0000 },
+  { 0x0c0e, 0x01ae, 0x0000 },
+  { 0x8c0e, 0x01b3, 0x3000 },
+  { 0x8c0e, 0x01b1, 0x2000 },
+  { 0x0c0e, 0x01b0, 0x0000 },
+  { 0x0c0e, 0x01b2, 0x0000 },
+  { 0x8c0e, 0x01b5, 0x2000 },
+  { 0x0c0e, 0x01b4, 0x0000 },
+  { 0x0c0e, 0x01b6, 0x0000 },
+  { 0x8c0e, 0x01d7, 0x6000 },
+  { 0x8c0e, 0x01c7, 0x5000 },
+  { 0x8c0e, 0x01bf, 0x4000 },
+  { 0x8c0e, 0x01bb, 0x3000 },
+  { 0x8c0e, 0x01b9, 0x2000 },
+  { 0x0c0e, 0x01b8, 0x0000 },
+  { 0x0c0e, 0x01ba, 0x0000 },
+  { 0x8c0e, 0x01bd, 0x2000 },
+  { 0x0c0e, 0x01bc, 0x0000 },
+  { 0x0c0e, 0x01be, 0x0000 },
+  { 0x8c0e, 0x01c3, 0x3000 },
+  { 0x8c0e, 0x01c1, 0x2000 },
+  { 0x0c0e, 0x01c0, 0x0000 },
+  { 0x0c0e, 0x01c2, 0x0000 },
+  { 0x8c0e, 0x01c5, 0x2000 },
+  { 0x0c0e, 0x01c4, 0x0000 },
+  { 0x0c0e, 0x01c6, 0x0000 },
+  { 0x8c0e, 0x01cf, 0x4000 },
+  { 0x8c0e, 0x01cb, 0x3000 },
+  { 0x8c0e, 0x01c9, 0x2000 },
+  { 0x0c0e, 0x01c8, 0x0000 },
+  { 0x0c0e, 0x01ca, 0x0000 },
+  { 0x8c0e, 0x01cd, 0x2000 },
+  { 0x0c0e, 0x01cc, 0x0000 },
+  { 0x0c0e, 0x01ce, 0x0000 },
+  { 0x8c0e, 0x01d3, 0x3000 },
+  { 0x8c0e, 0x01d1, 0x2000 },
+  { 0x0c0e, 0x01d0, 0x0000 },
+  { 0x0c0e, 0x01d2, 0x0000 },
+  { 0x8c0e, 0x01d5, 0x2000 },
+  { 0x0c0e, 0x01d4, 0x0000 },
+  { 0x0c0e, 0x01d6, 0x0000 },
+  { 0x8c0e, 0x01e7, 0x5000 },
+  { 0x8c0e, 0x01df, 0x4000 },
+  { 0x8c0e, 0x01db, 0x3000 },
+  { 0x8c0e, 0x01d9, 0x2000 },
+  { 0x0c0e, 0x01d8, 0x0000 },
+  { 0x0c0e, 0x01da, 0x0000 },
+  { 0x8c0e, 0x01dd, 0x2000 },
+  { 0x0c0e, 0x01dc, 0x0000 },
+  { 0x0c0e, 0x01de, 0x0000 },
+  { 0x8c0e, 0x01e3, 0x3000 },
+  { 0x8c0e, 0x01e1, 0x2000 },
+  { 0x0c0e, 0x01e0, 0x0000 },
+  { 0x0c0e, 0x01e2, 0x0000 },
+  { 0x8c0e, 0x01e5, 0x2000 },
+  { 0x0c0e, 0x01e4, 0x0000 },
+  { 0x0c0e, 0x01e6, 0x0000 },
+  { 0x8c0e, 0x01ef, 0x4000 },
+  { 0x8c0e, 0x01eb, 0x3000 },
+  { 0x8c0e, 0x01e9, 0x2000 },
+  { 0x0c0e, 0x01e8, 0x0000 },
+  { 0x0c0e, 0x01ea, 0x0000 },
+  { 0x8c0e, 0x01ed, 0x2000 },
+  { 0x0c0e, 0x01ec, 0x0000 },
+  { 0x0c0e, 0x01ee, 0x0000 },
+  { 0x830f, 0xfffd, 0x2000 },
+  { 0x030f, 0x0000, 0x0000 },
+  { 0x0310, 0x0000, 0x1000 },
+  { 0x0310, 0xfffd, 0x0000 },
+};
diff --git a/connectors/jk/native/iis/pcre/ucptypetable.c b/connectors/jk/native/iis/pcre/ucptypetable.c
new file mode 100644
index 0000000..129529b
--- /dev/null
+++ b/connectors/jk/native/iis/pcre/ucptypetable.c
@@ -0,0 +1,93 @@
+/*************************************************
+*      Perl-Compatible Regular Expressions       *
+*************************************************/
+
+/*
+This is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language. See
+the file Tech.Notes for some information on the internals.
+
+Written by: Philip Hazel <ph10@cam.ac.uk>
+
+           Copyright (c) 1997-2004 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+    * Neither the name of the University of Cambridge nor the names of its
+      contributors may be used to endorse or promote products derived from
+      this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+/* This module contains a table for translating Unicode property names into
+code values for the ucp_findchar function. It is in a separate module so that
+it can be included both in the main pcre library, and into pcretest (for
+printing out internals). */
+
+typedef struct {
+  const char *name;
+  int value;
+} ucp_type_table;
+
+static ucp_type_table utt[] = {
+  { "C",  128 + ucp_C },
+  { "Cc", ucp_Cc },
+  { "Cf", ucp_Cf },
+  { "Cn", ucp_Cn },
+  { "Co", ucp_Co },
+  { "Cs", ucp_Cs },
+  { "L",  128 + ucp_L },
+  { "Ll", ucp_Ll },
+  { "Lm", ucp_Lm },
+  { "Lo", ucp_Lo },
+  { "Lt", ucp_Lt },
+  { "Lu", ucp_Lu },
+  { "M",  128 + ucp_M },
+  { "Mc", ucp_Mc },
+  { "Me", ucp_Me },
+  { "Mn", ucp_Mn },
+  { "N",  128 + ucp_N },
+  { "Nd", ucp_Nd },
+  { "Nl", ucp_Nl },
+  { "No", ucp_No },
+  { "P",  128 + ucp_P },
+  { "Pc", ucp_Pc },
+  { "Pd", ucp_Pd },
+  { "Pe", ucp_Pe },
+  { "Pf", ucp_Pf },
+  { "Pi", ucp_Pi },
+  { "Po", ucp_Po },
+  { "Ps", ucp_Ps },
+  { "S",  128 + ucp_S },
+  { "Sc", ucp_Sc },
+  { "Sk", ucp_Sk },
+  { "Sm", ucp_Sm },
+  { "So", ucp_So },
+  { "Z",  128 + ucp_Z },
+  { "Zl", ucp_Zl },
+  { "Zp", ucp_Zp },
+  { "Zs", ucp_Zs }
+};
+
+/* End of ucptypetable.c */
diff --git a/connectors/jk/native/jni/Makefile.in b/connectors/jk/native/jni/Makefile.in
new file mode 100644
index 0000000..2efcc8b
--- /dev/null
+++ b/connectors/jk/native/jni/Makefile.in
@@ -0,0 +1,30 @@
+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 *.lo *.a *.la *.so *.so.* *.slo
+	rm -rf .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..34085fd
--- /dev/null
+++ b/connectors/jk/native/jni/Makefile.netware
@@ -0,0 +1,248 @@
+#
+# Makefile for jk_nsapi_plugin (NetWare version - gnu make)
+# created by Guenter Knauf <fuankg@apache.org>
+#
+
+# 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
+
+ifndef INSTDIR
+INSTDIR	= s:/sys/novonyx/modules
+endif
+
+# Edit the vars below to change NLM target settings.
+TARGET  = jni_conn
+VERSION	= $(JK_VERSION)
+#COPYR	= Copyright (c) 2000-2007 The Apache Software Foundation. All rights reserved.
+COPYR	= Licensed under the Apache License, Version 2.0
+DESCR	= JNI natives for 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.net/development/prgtools/mkxdc.zip
+MPKXDC	= mkxdc
+AWK	= awk
+
+# Global flags for all compilers
+CFLAGS	= $(OPT) -D$(DB) -DNETWARE -DXP_NETWARE -nostdinc
+
+ifeq ($(CC),mwccnlm)
+LD	= mwldnlm
+LDFLAGS	= -nostdlib $(OBJS) $(PRELUDE) $(LDLIBS) -o $@ -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"
+	LDLIBS = "$(METROWERKS)/Novell Support/libraries/runtime/mwcrtl.lib"
+#	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
+
+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$(NDKBASE)/ws295sdk/include
+	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_jnicb.o \
+	$(OBJDIR)/jk_nwmain.o \
+	$(OBJDIR)/jk_map.o \
+	$(OBJDIR)/jk_pool.o \
+	$(OBJDIR)/jk_util.o \
+	$(OBJDIR)/ap_snprintf.o
+
+vpath %.c . $(JKCOMMON)
+
+
+all: $(OBJDIR) $(OBJDIR)/version.inc $(OBJDIR)/$(TARGET).nlm 
+
+$(OBJDIR)/%.o: %.c
+	@echo Compiling $<
+	@$(CC) $(CFLAGS) -c $< -o $@
+
+$(OBJDIR)/version.inc: $(JKCOMMON)/jk_version.h $(OBJDIR)
+	@echo Creating $@
+	@$(AWK) -f ../../support/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) >> $@
+ifdef LDLIBS
+	@echo $(DL)input $(LDLIBS)$(DL) >> $@
+endif
+	@echo $(DL)output $*.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..e05e89d
--- /dev/null
+++ b/connectors/jk/native/jni/jk_jnicb.c
@@ -0,0 +1,495 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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..0090c84
--- /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 /MD /W3 /GX /Zi /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "JNI_CONNECT_EXPORTS" /FD /c
+# ADD CPP /nologo /MD /W3 /GX /Zi /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" /Fd"Release/jni_connect_src" /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 /base:"0x6A6E0000" /subsystem:windows /dll /incremental:no /debug /machine:I386 /opt:ref
+
+!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 /MDd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "JNI_CONNECT_EXPORTS" /FD /c
+# ADD CPP /nologo /MDd /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" /Fd"Debug/jni_connect_src" /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 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 /base:"0x6A6E0000" /subsystem:windows /dll /incremental:no /debug /machine:I386
+
+!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.linux b/connectors/jk/native/netscape/Makefile.linux
new file mode 100644
index 0000000..473c170
--- /dev/null
+++ b/connectors/jk/native/netscape/Makefile.linux
@@ -0,0 +1,38 @@
+# Defines for example NSAPI programs running under Linux
+
+#gcc
+# If you get relocation errors, try:
+#   1. compiling with Sun's cc
+#   2. statically linking with libgcc
+#   3. Adjusting LD_LIBRARY_PATH to grab libgcc_s
+CC_CMD=gcc -fpic -DNET_SSL -DLinux -DLINUX -D_REENTRANT -DXP_UNIX
+
+LD_SHAREDCMD=gcc -shared
+
+OS_TYPE=linux
+INCLUDEDIR=$(SUITSPOT_HOME)/include
+JAVA_INCLUDE=$(JAVA_HOME)/include
+JK_DIR=../common
+VPATH=.:$(JK_DIR)
+
+JK_SRCS = $(shell \ls $(JK_DIR)/*.c)
+JK_OBJECTS = $(patsubst $(JK_DIR)/%.c,%.o,$(JK_SRCS))
+JK_JNI_OBJECTS = jk_jni_worker.o jk_nwmain.o
+JK_OBJS = $(filter-out $(JK_JNI_OBJECTS),$(JK_OBJECTS))
+
+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: $(PLUGIN_OBJ) $(JK_OBJS)
+	$(LD_SHAREDCMD) $(JK_OBJS) $(PLUGIN_OBJ) -o nsapi_redirector.so $(EXTRA_LDDEFINES)
+
+clean:
+	rm -f *.o nsapi_redirector.so $(JK_OBJS)
+
+%.o : %.c
+	$(CC_CMD) $(INCLUDE_FLAGS) -c $<
diff --git a/connectors/jk/native/netscape/Makefile.netware b/connectors/jk/native/netscape/Makefile.netware
new file mode 100644
index 0000000..c396309
--- /dev/null
+++ b/connectors/jk/native/netscape/Makefile.netware
@@ -0,0 +1,258 @@
+#
+# Makefile for jk_nsapi_plugin (NetWare version - gnu make)
+# created by Guenter Knauf <fuankg@apache.org>
+#
+
+# 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
+
+ifndef INSTDIR
+INSTDIR	= s:/sys/novonyx/modules
+endif
+
+# Edit the vars below to change NLM target settings.
+TARGET  = nsapi_rd
+VERSION	= $(JK_VERSION)
+#COPYR	= Copyright (c) 2000-2007 The Apache Software Foundation. All rights reserved.
+COPYR	= Licensed under the Apache License, Version 2.0
+DESCR	= Netscape plugin for Tomcat $(JK_VERSION_STR)
+MTSAFE	= NO
+STACK	= 65536
+#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.net/development/prgtools/mkxdc.zip
+MPKXDC	= mkxdc
+AWK	= awk
+
+# Global flags for all compilers
+CFLAGS	= $(OPT) -D$(DB) -DNETWARE -DXP_NETWARE -nostdinc
+
+ifeq ($(CC),mwccnlm)
+LD	= mwldnlm
+LDFLAGS	= -nostdlib $(OBJS) $(PRELUDE) $(LDLIBS) -o $@ -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"
+	LDLIBS = "$(METROWERKS)/Novell Support/libraries/runtime/mwcrtl.lib"
+#	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
+
+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$(NDKBASE)/ws295sdk/include
+	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_nsapi_plugin.o \
+	$(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_shm.o \
+	$(OBJDIR)/jk_sockbuf.o \
+	$(OBJDIR)/jk_status.o \
+	$(OBJDIR)/jk_uri_worker_map.o \
+	$(OBJDIR)/jk_util.o \
+	$(OBJDIR)/jk_worker.o \
+	$(OBJDIR)/ap_snprintf.o
+
+vpath %.c . $(JKCOMMON)
+
+
+all: $(OBJDIR) $(OBJDIR)/version.inc $(OBJDIR)/$(TARGET).nlm 
+
+$(OBJDIR)/%.o: %.c
+	@echo Compiling $<
+	@$(CC) $(CFLAGS) -c $< -o $@
+
+$(OBJDIR)/version.inc: $(JKCOMMON)/jk_version.h $(OBJDIR)
+	@echo Creating $@
+	@$(AWK) -f ../../support/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) >> $@
+ifdef LDLIBS
+	@echo $(DL)input $(LDLIBS)$(DL) >> $@
+endif
+	@echo $(DL)output $*.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..e5bbee2
--- /dev/null
+++ b/connectors/jk/native/netscape/Makefile.solaris
@@ -0,0 +1,47 @@
+# Defines for example NSAPI programs running under SOLARIS
+
+#gcc
+# If you get relocation errors, try:
+#   1. compiling with Sun's cc
+#   2. statically linking with libgcc
+#   3. Adjusting LD_LIBRARY_PATH to grab libgcc_s
+CC_CMD=gcc -DNET_SSL -DSOLARIS -D_REENTRANT -DXP_UNIX \
+	-DMCC_HTTPD -DSPAPI20 \
+	-fPIC
+
+#SunStudio cc compiler
+#CC_CMD=cc -DNET_SSL -DSOLARIS -D_REENTRANT -DXP_UNIX \
+#	-DMCC_HTTPD -DSPAPI20 \
+#	-xcode=pic32
+
+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 $(JK_OBJS)
+
+%.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..1d51ae7
--- /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. Be sure to read
+the BUILDING file, one directory up.
+
+
+REQUIREMENT for Windows build
+-----------------------------
+
+MS VC 6.0 (+ update, latest service pack is 6)
+
+BUILDING on Windows
+-------------------
+ 
+The steps that you need to take are:
+
+   1. Change directory to the nsapi redirector plugins source directory.
+   2. Set the SUNONE_HOME system environment value to SunONE installation
+      directory or edit the nsapi.dsp and replace all $(SUNONE_HOME)
+      occurrences with the real path
+   3. 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..4c89f6c
--- /dev/null
+++ b/connectors/jk/native/netscape/jk_nsapi_plugin.c
@@ -0,0 +1,545 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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 "nsapi.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_shm.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 const char ssl_cert_start[] = "-----BEGIN CERTIFICATE-----\r\n";
+static const char ssl_cert_end[] = "\r\n-----END CERTIFICATE-----\r\n";
+
+static jk_logger_t *logger = NULL;
+static jk_worker_env_t worker_env;
+static jk_map_t *init_map = NULL;
+static jk_uri_worker_map_t *uw_map = NULL;
+
+#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)
+{
+    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 (uri_worker_map_alloc(&uw_map, NULL, logger)) {
+        uw_map->fname = "";
+        uw_map->reload = JK_URIMAP_DEF_RELOAD;
+        worker_env.uri_to_worker = uw_map;
+        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");
+        }
+    }
+    else {
+        jk_log(logger, JK_LOG_EMERG,
+               "In init_workers_on_other_threads, failed");
+    }
+
+    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);
+    char *shm_file = pblock_findval(JK_SHM_FILE_TAG, pb);
+
+    int rc = REQ_ABORTED;
+
+    if (!worker_prp_file) {
+        worker_prp_file = JK_WORKER_FILE_DEF;
+    }
+
+    if (!log_level_str) {
+        log_level_str = JK_LOG_DEF_VERB;
+    }
+
+    if (!log_file) {
+        fprintf(stderr,
+                "Missing attribute %s in magnus.conf (jk_init) - aborting!\n", JK_LOG_FILE_TAG);
+        return rc;
+    }
+
+    if (!shm_file) {
+        fprintf(stderr,
+                "Missing attribute %s in magnus.conf (jk_init) - aborting!\n", JK_SHM_FILE_TAG);
+        return rc;
+    }
+
+    fprintf(stderr,
+            "In jk_init.\n   Worker file = %s.\n   Log level = %s.\n   Log File = %s\n   SHM File = %s\n",
+            worker_prp_file, log_level_str, log_file, shm_file);
+
+    if (!jk_open_file_logger(&logger, log_file,
+                             jk_parse_log_level(log_level_str))) {
+        logger = NULL;
+    }
+    
+    jk_shm_open(shm_file, JK_SHM_DEF_SIZE, logger);
+    if (jk_map_alloc(&init_map)) {
+        if (jk_map_read_properties(init_map, worker_prp_file, NULL, 1, logger)) {
+            int sleep_cnt;
+            SYS_THREAD s;
+
+            if (jk_map_resolve_references(init_map, "worker.", 1, 1, logger) == JK_FALSE) {
+                jk_log(logger, JK_LOG_ERROR, "Error in resolving configuration references");
+            }
+
+            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");
+                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;
+            }
+*/
+        }
+    }
+
+#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
+    if (uw_map) {
+        uri_worker_map_free(&uw_map, logger);
+    }
+
+    wc_close(logger);
+    if (logger) {
+        jk_close_file_logger(&logger);
+    }
+
+    if (init_map) {
+    jk_map_free(&init_map);
+    }
+}
+
+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;
+        s.retries = worker->retries;
+
+        wc_maintain(logger);
+        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->route = NULL;
+    s->start_response = start_response;
+    s->read = ws_read;
+    s->write = ws_write;
+    s->flush = NULL;
+    s->flush_packets = JK_FALSE;
+    s->flush_header = JK_FALSE;
+    s->disable_reuse = JK_FALSE;
+
+    /* 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;
+    s->uw_map = uw_map;
+
+#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) {
+        char *ssl_cert = pblock_findval("auth-cert", private_data->rq->vars);
+        if (ssl_cert != NULL) {
+            s->ssl_cert = jk_pool_alloc(s->pool, sizeof(ssl_cert_start)+
+                                                 strlen(ssl_cert)+
+                                                 sizeof(ssl_cert_end));
+            strcpy(s->ssl_cert, ssl_cert_start);
+            strcat(s->ssl_cert, ssl_cert);
+            strcat(s->ssl_cert, ssl_cert_end);
+            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..1b1686e
--- /dev/null
+++ b/connectors/jk/native/netscape/nsapi.dsp
@@ -0,0 +1,275 @@
+# 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 /MD /W3 /Zi /O2 /I "..\common" /I "$(SUNONE_HOME)\plugins\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" /D "MCC_HTTPD" /D "SPAPI20" /Fd"Release/nsapi_redirect_src" /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 ns-httpd40.lib kernel32.lib user32.lib advapi32.lib ws2_32.lib mswsock.lib /nologo /base:"0x6A6B0000" /dll /debug /machine:I386 /out:"nsapi_release/nsapi_redirect.dll" /libpath:"$(SUNONE_HOME)\plugins\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 /MDd /W3 /GX /Zi /Od /I "..\common" /I "$(SUNONE_HOME)\plugins\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" /Fd"Debug/nsapi_redirect_src" /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 ns-httpd40.lib kernel32.lib user32.lib advapi32.lib ws2_32.lib mswsock.lib /nologo /base:"0x6A6B0000" /dll /debug /machine:I386 /out:"nsapi_debug/nsapi_redirect.dll" /pdbtype:sept /libpath:"$(SUNONE_HOME)\plugins\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_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=.\jk_nsapi_plugin.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_pool.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_shm.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_sockbuf.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_status.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_shm.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_status.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..799089f
--- /dev/null
+++ b/connectors/jk/native/nt_service/jk_nt_service.c
@@ -0,0 +1,1231 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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 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 = "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, "Tomcat");
+
+    sprintf(szMsg, "%s error: %d", "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, -1, 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, 
+                                                  msg->buf,
+                                                  msg->len)) {
+                        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(jk_map_alloc(&init_map)) {
+                if(jk_map_read_properties(init_map, prp_file, NULL, 1, NULL)) {
+                    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 = "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)));                
+                        }
+
+                    }                    
+                }
+            }
+            jk_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 = jk_map_get_string(init_map, 
+                                          "wrapper.server_xml", 
+                                          NULL);
+    data->classpath = jk_map_get_string(init_map, 
+                                        "wrapper.class_path", 
+                                        NULL);
+    data->tomcat_home = jk_map_get_string(init_map, 
+                                          "wrapper.tomcat_home", 
+                                          NULL);
+    data->java_bin = jk_map_get_string(init_map, 
+                                       "wrapper.javabin", 
+                                       NULL);
+    data->tomcat_class = jk_map_get_string(init_map,
+                                           "wrapper.startup_class",
+                                           "org.apache.tomcat.startup.Tomcat");
+
+    data->cmd_line = jk_map_get_string(init_map,
+                                       "wrapper.cmd_line",
+                                       NULL);
+
+    data->stop_cmd = jk_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 = jk_map_get_int(init_map,
+                                         "wrapper.shutdown_port",
+                                         8007);
+
+    data->shutdown_secret = jk_map_get_string(init_map,
+                                              "wrapper.shutdown_secret", 
+                                              NULL);
+    
+    data->shutdown_protocol = jk_map_get_string(init_map,
+                                                "wrapper.shutdown_protocol",
+                                                AJP12_TAG);
+
+    data->extra_path = jk_map_get_string(init_map,
+                                         "wrapper.ld_path",
+                                         NULL);
+
+    data->stdout_file = jk_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 = jk_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..02b0260
--- /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" /FD /c
+# ADD CPP /nologo /W3 /GX /Zi /O2 /I "../common" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Fd"Release/jk_nt_service_src" /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 /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 /debug /machine:I386 /out:"Release/jk_nt_service.exe" /opt:ref
+
+!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 /MD /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c
+# ADD CPP /nologo /MD /W3 /Gm /GX /Zi /Od /I "../common" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Fd"Debug/jk_nt_service_src" /FD /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 /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"
+
+!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/instdso.sh b/connectors/jk/native/scripts/build/instdso.sh
new file mode 100755
index 0000000..2b32973
--- /dev/null
+++ b/connectors/jk/native/scripts/build/instdso.sh
@@ -0,0 +1,91 @@
+#!/bin/sh
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+# instdso.sh - install Apache DSO modules
+#
+# we use this instead of libtool --install because:
+# 1) on a few platforms libtool doesn't install DSOs exactly like we'd
+#    want (weird names, doesn't remove DSO first)
+# 2) we never want the .la files copied, so we might as well copy
+#    the .so files ourselves
+
+if test "$#" != "3"; then
+    echo "wrong number of arguments to instdso.sh"
+    echo "Usage: instdso.sh SH_LIBTOOL-value dso-name path-to-modules"
+    exit 1
+fi
+
+SH_LIBTOOL=`echo $1 | sed -e 's/^SH_LIBTOOL=//'`
+DSOARCHIVE=$2
+DSOARCHIVE_BASENAME=`basename $2`
+TARGETDIR=$3
+DSOBASE=`echo $DSOARCHIVE_BASENAME | sed -e 's/\.la$//'`
+TARGET_NAME="$DSOBASE.so"
+
+SYS=`uname -s`
+
+if test "$SYS" = "AIX"
+then
+    # on AIX, shared libraries remain in storage even when
+    # all processes using them have exited; standard practice
+    # prior to installing a shared library is to rm -f first
+    CMD="rm -f $TARGETDIR/$TARGET_NAME"
+    echo $CMD
+    $CMD || exit $?
+fi
+
+CMD="$SH_LIBTOOL --mode=install cp $DSOARCHIVE $TARGETDIR/"
+echo $CMD
+$CMD || exit $?
+
+if test "$SYS" = "OS/2"
+then
+    # on OS/2, aplibtool --install doesn't copy the .la files & we can't
+    # rename DLLs to have a .so extension or they won't load so none of the 
+    # steps below make sense.
+    exit 0
+fi
+
+if test -s "$TARGETDIR/$DSOARCHIVE_BASENAME"
+then
+  DLNAME=`sed -n "/^dlname=/{s/.*='\([^']*\)'/\1/;p;}" $TARGETDIR/$DSOARCHIVE_BASENAME`
+  LIBRARY_NAMES=`sed -n "/^library_names/{s/library_names='\([^']*\)'/\1/;p;}" $TARGETDIR/$DSOARCHIVE_BASENAME`
+  LIBRARY_NAMES=`echo $LIBRARY_NAMES | sed -e "s/ *$DLNAME//g"`
+fi
+
+if test -z "$DLNAME"
+then
+  echo "Warning!  dlname not found in $TARGETDIR/$DSOARCHIVE_BASENAME."
+  echo "Assuming installing a .so rather than a libtool archive."
+  exit 0
+fi
+
+if test -n "$LIBRARY_NAMES"
+then
+    for f in $LIBRARY_NAMES
+    do
+        rm -f $TARGETDIR/$f
+    done
+fi
+
+if test "$DLNAME" != "$TARGET_NAME"
+then
+    mv $TARGETDIR/$DLNAME $TARGETDIR/$TARGET_NAME
+fi
+
+exit 0
diff --git a/connectors/jk/native/scripts/build/rules.mk b/connectors/jk/native/scripts/build/rules.mk
new file mode 100644
index 0000000..84ee664
--- /dev/null
+++ b/connectors/jk/native/scripts/build/rules.mk
@@ -0,0 +1,28 @@
+# That an extract of what is in APR.
+#
+
+# Compile commands
+#VPATH=.:../common
+COMPILE      = $(CC) $(CFLAGS)
+LT_COMPILE   = $(LIBTOOL) --mode=compile $(COMPILE) -c $< -o $@
+
+# 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/support/apache.m4 b/connectors/jk/support/apache.m4
new file mode 100644
index 0000000..87c820f
--- /dev/null
+++ b/connectors/jk/support/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/jk/support/get_ver.awk b/connectors/jk/support/get_ver.awk
new file mode 100644
index 0000000..1e50a1e
--- /dev/null
+++ b/connectors/jk/support/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/jk/support/jk_apache_static.m4 b/connectors/jk/support/jk_apache_static.m4
new file mode 100644
index 0000000..edc06b2
--- /dev/null
+++ b/connectors/jk/support/jk_apache_static.m4
@@ -0,0 +1,133 @@
+dnl
+dnl Licensed to the Apache Software Foundation (ASF) under one or more
+dnl contributor license agreements.  See the NOTICE file distributed with
+dnl this work for additional information regarding copyright ownership.
+dnl The ASF licenses this file to You under the Apache License, Version 2.0
+dnl (the "License"); you may not use this file except in compliance with
+dnl the License.  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(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..3403b34
--- /dev/null
+++ b/connectors/jk/support/jk_apr.m4
@@ -0,0 +1,320 @@
+dnl
+dnl Licensed to the Apache Software Foundation (ASF) under one or more
+dnl contributor license agreements.  See the NOTICE file distributed with
+dnl this work for additional information regarding copyright ownership.
+dnl The ASF licenses this file to You under the Apache License, Version 2.0
+dnl (the "License"); you may not use this file except in compliance with
+dnl the License.  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..32445d6
--- /dev/null
+++ b/connectors/jk/support/jk_apxs.m4
@@ -0,0 +1,150 @@
+dnl
+dnl Licensed to the Apache Software Foundation (ASF) under one or more
+dnl contributor license agreements.  See the NOTICE file distributed with
+dnl this work for additional information regarding copyright ownership.
+dnl The ASF licenses this file to You under the Apache License, Version 2.0
+dnl (the "License"); you may not use this file except in compliance with
+dnl the License.  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.sl \
+                      -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.sl \
+                        -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.sl \
+                        -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..dccc698
--- /dev/null
+++ b/connectors/jk/support/jk_dominohome.m4
@@ -0,0 +1,74 @@
+dnl
+dnl Licensed to the Apache Software Foundation (ASF) under one or more
+dnl contributor license agreements.  See the NOTICE file distributed with
+dnl this work for additional information regarding copyright ownership.
+dnl The ASF licenses this file to You under the Apache License, Version 2.0
+dnl (the "License"); you may not use this file except in compliance with
+dnl the License.  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..d09295d
--- /dev/null
+++ b/connectors/jk/support/jk_exec.m4
@@ -0,0 +1,91 @@
+dnl
+dnl Licensed to the Apache Software Foundation (ASF) under one or more
+dnl contributor license agreements.  See the NOTICE file distributed with
+dnl this work for additional information regarding copyright ownership.
+dnl The ASF licenses this file to You under the Apache License, Version 2.0
+dnl (the "License"); you may not use this file except in compliance with
+dnl the License.  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..c36e91b
--- /dev/null
+++ b/connectors/jk/support/jk_java.m4
@@ -0,0 +1,224 @@
+dnl
+dnl Licensed to the Apache Software Foundation (ASF) under one or more
+dnl contributor license agreements.  See the NOTICE file distributed with
+dnl this work for additional information regarding copyright ownership.
+dnl The ASF licenses this file to You under the Apache License, Version 2.0
+dnl (the "License"); you may not use this file except in compliance with
+dnl the License.  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..8f0aab3
--- /dev/null
+++ b/connectors/jk/support/jk_pcre.m4
@@ -0,0 +1,40 @@
+dnl
+dnl Licensed to the Apache Software Foundation (ASF) under one or more
+dnl contributor license agreements.  See the NOTICE file distributed with
+dnl this work for additional information regarding copyright ownership.
+dnl The ASF licenses this file to You under the Apache License, Version 2.0
+dnl (the "License"); you may not use this file except in compliance with
+dnl the License.  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..0c4673b
--- /dev/null
+++ b/connectors/jk/support/jk_tchome.m4
@@ -0,0 +1,73 @@
+dnl
+dnl Licensed to the Apache Software Foundation (ASF) under one or more
+dnl contributor license agreements.  See the NOTICE file distributed with
+dnl this work for additional information regarding copyright ownership.
+dnl The ASF licenses this file to You under the Apache License, Version 2.0
+dnl (the "License"); you may not use this file except in compliance with
+dnl the License.  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..d9eb556
--- /dev/null
+++ b/connectors/jk/support/jk_ws.m4
@@ -0,0 +1,229 @@
+dnl
+dnl Licensed to the Apache Software Foundation (ASF) under one or more
+dnl contributor license agreements.  See the NOTICE file distributed with
+dnl this work for additional information regarding copyright ownership.
+dnl The ASF licenses this file to You under the Apache License, Version 2.0
+dnl (the "License"); you may not use this file except in compliance with
+dnl the License.  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/support/os_apache.m4 b/connectors/jk/support/os_apache.m4
new file mode 100644
index 0000000..8716772
--- /dev/null
+++ b/connectors/jk/support/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/jk/test/org/apache/ajp/test/TestAjp13.java b/connectors/jk/test/org/apache/ajp/test/TestAjp13.java
new file mode 100644
index 0000000..0ac2c8a
--- /dev/null
+++ b/connectors/jk/test/org/apache/ajp/test/TestAjp13.java
@@ -0,0 +1,281 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 {
+
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( TestAjp13.class );
+
+    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) {
+        if (log.isDebugEnabled())
+            log.debug("[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..972ce1c
--- /dev/null
+++ b/connectors/jk/test/org/apache/ajp/test/TestAll.java
@@ -0,0 +1,41 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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/jkrelease.sh b/connectors/jk/tools/jkrelease.sh
new file mode 100755
index 0000000..2ba7ad9
--- /dev/null
+++ b/connectors/jk/tools/jkrelease.sh
@@ -0,0 +1,259 @@
+#!/bin/ksh
+
+# Make sure to set your path so that we can find
+# the following binaries:
+# cd, mkdir, cp, rm, find
+# svn
+# ant
+# libtoolize, aclocal, autoheader, automake, autoconf
+# tar, zip, gzip
+# gpg
+# And any one of: w3m, elinks, links (links2)
+
+usage() {
+    echo "Usage:: $0 -t VERSION [-b BRANCH | -T | -d DIR]"
+    echo "        -t: version to package"
+    echo "        -b: package from branch BRANCH"
+    echo "        -T: package from trunk"
+    echo "        -d: package from local directory"
+}
+
+conflict=0
+while getopts :t:b:d:T c
+do
+    case $c in
+    t)         tag=$OPTARG;;
+    b)         branch=$OPTARG
+               conflict=$(($conflict+1));;
+    T)         trunk=trunk
+               conflict=$(($conflict+1));;
+    d)         local_dir=$OPTARG
+               conflict=$(($conflict+1));;
+    \:)        usage
+               exit 2;;
+    \?)        usage
+               exit 2;;
+    esac
+done
+shift `expr $OPTIND - 1`
+
+if [ $conflict -gt 1 ]
+then
+    usage
+    echo "Only one of the options '-b', '-T'  and '-d' is allowed."
+    exit 2
+fi
+
+if [ -n "$local_dir" ]
+then
+    echo "Caution: Packaging from directory!"
+    echo "Make sure the directory is committed."
+    answer="x"
+    while [ "$answer" != "y" -a "$answer" != "n" ]
+    do
+        echo "Do you want to procede? [y/n]"
+        read answer
+    done
+    if [ "$answer" != "y" ]
+    then
+        echo "Aborting."
+        exit 4
+    fi
+fi
+
+SVNROOT="http://svn.apache.org/repos/asf"
+SVNPROJ="tomcat/connectors"
+JK_CVST="tomcat-connectors"
+
+JK_OWNER="root"
+JK_GROUP="bin"
+
+COPY_TOP="KEYS LICENSE NOTICE"
+COPY_JK="BUILD.txt native jkstatus support tools xdocs"
+COPY_CONF="uriworkermap.properties workers.properties workers.properties.minimal"
+
+#################### NO CHANGE BELOW THIS LINE ##############
+
+if [ -z "$tag" ]
+then
+    usage
+    exit 2
+fi
+if [ -n "$trunk" ]
+then
+    JK_SVN_URL="${SVNROOT}/${SVNPROJ}/trunk"
+    JK_REV=`svn info ${JK_SVN_URL} | awk '$1 == "Revision:" {print $2}'`
+    if [ -z "$JK_REV" ]
+    then
+       echo "No Revision found at '$JK_SVN_URL'"
+       exit 3
+    fi
+    JK_DIST=${JK_CVST}-${tag}-dev-${JK_REV}-src
+elif [ -n "$branch" ]
+then
+    JK_BRANCH=`echo $branch | sed -e 's#/#__#g'`
+    JK_SVN_URL="${SVNROOT}/${SVNPROJ}/branches/$branch"
+    JK_REV=`svn info ${JK_SVN_URL} | awk '$1 == "Revision:" {print $2}'`
+    if [ -z "$JK_REV" ]
+    then
+       echo "No Revision found at '$JK_SVN_URL'"
+       exit 3
+    fi
+    JK_DIST=${JK_CVST}-${tag}-dev-${JK_BRANCH}-${JK_REV}-src
+elif [ -n "$local_dir" ]
+then
+    JK_SVN_URL="$local_dir"
+    JK_REV=`svn info ${JK_SVN_URL} | awk '$1 == "Revision:" {print $2}'`
+    if [ -z "$JK_REV" ]
+    then
+       echo "No Revision found at '$JK_SVN_URL'"
+       exit 3
+    fi
+    JK_DIST=${JK_CVST}-${tag}-dev-local-${JK_REV}-src
+else
+    JK_VER=$tag
+    JK_TAG=`echo $tag | sed -e 's#^#JK_#' -e 's#\.#_#g'`
+    JK_BRANCH=`echo $tag | sed -e 's#^#jk#' -e 's#\.[0-9][0-9]*$##' -e 's#$#.x#'`
+    JK_SVN_URL="${SVNROOT}/${SVNPROJ}/tags/${JK_BRANCH}/${JK_TAG}"
+    JK_DIST=${JK_CVST}-${JK_VER}-src
+fi
+
+umask 022
+
+rm -rf ${JK_DIST}
+rm -rf ${JK_DIST}.tmp
+
+mkdir -p ${JK_DIST}.tmp
+svn export "${JK_SVN_URL}/jk" ${JK_DIST}.tmp/jk
+for item in ${COPY_TOP}
+do
+    svn export "${JK_SVN_URL}/${item}" ${JK_DIST}.tmp/${item}
+done
+
+# Build documentation.
+cd ${JK_DIST}.tmp/jk/xdocs
+ant
+cd ../../..
+
+# Copying things into source distribution
+srcdir=${JK_DIST}.tmp
+targetdir=${JK_DIST}
+mkdir -p ${targetdir}
+for item in ${COPY_TOP}
+do
+    echo "Copying $item from ${srcdir} ..."
+    cp -pr ${srcdir}/$item ${targetdir}/
+done
+
+srcdir=${JK_DIST}.tmp/jk
+targetdir=${JK_DIST}
+mkdir -p ${targetdir}
+for item in ${COPY_JK}
+do
+    echo "Copying $item from ${srcdir} ..."
+    cp -pr ${srcdir}/$item ${targetdir}/
+done
+
+srcdir=${JK_DIST}.tmp/jk/build
+targetdir=${JK_DIST}
+mkdir -p ${targetdir}
+for item in docs
+do
+    echo "Copying $item from ${srcdir} ..."
+    cp -pr ${srcdir}/$item ${targetdir}/
+done
+
+srcdir=${JK_DIST}.tmp/jk/conf
+targetdir=${JK_DIST}/conf
+mkdir -p ${targetdir}
+for item in ${COPY_CONF}
+do
+    echo "Copying $item from ${srcdir} ..."
+    cp -pr ${srcdir}/$item ${targetdir}/
+done
+
+# Remove extra directories and files
+targetdir=${JK_DIST}
+rm -rf ${targetdir}/xdocs/jk2
+rm -rf ${targetdir}/native/CHANGES.txt
+rm -rf ${targetdir}/native/build.xml
+find ${JK_DIST} -name .cvsignore -exec rm -rf \{\} \; 
+find ${JK_DIST} -name CVS -exec rm -rf \{\} \; 
+find ${JK_DIST} -name .svn -exec rm -rf \{\} \; 
+
+cd ${JK_DIST}/native
+
+# Check for links, elinks or w3m
+W3MOPTS="-dump -cols 80 -t 4 -S -O iso-8859-1 -T text/html"
+ELNKOPTS="-dump -dump-width 80 -dump-charset iso-8859-1 -no-numbering -no-references -no-home"
+LNKOPTS="-dump -width 80 -codepage iso-8859-1 -no-g -html-numbered-links 0"
+failed=true
+for tool in `echo "w3m elinks links"`
+do
+  found=false
+  for dir in `echo ${PATH} | sed 's!^:!.:!;s!:$!:.!;s!::!:.:!g;s!:! !g'`
+  do
+    if [ -x ${dir}/${tool} ]
+    then
+      found=true
+      break
+    fi
+  done
+
+  # Try to run it
+  if ${found}
+  then
+    case ${tool} in
+      w3m)
+        TOOL="w3m $W3MOPTS"
+        ;;
+      links)
+        TOOL="links $LNKOPTS"
+        ;;
+      elinks)
+        TOOL="elinks $ELNKOPTS"
+        ;;
+    esac
+    rm -f CHANGES
+    echo "Creating the CHANGES file using '$TOOL' ..."
+    ${TOOL} ../docs/miscellaneous/printer/changelog.html > CHANGES 2>/dev/null
+    if [ -f CHANGES -a -s CHANGES ]
+    then
+      failed=false
+      break
+    fi
+  fi
+done
+if [ ${failed} = "true" ]
+then
+  echo "Can't convert html to text (CHANGES)"
+  exit 1
+fi
+
+# Export text docs
+echo "Creating the NEWS file using '$TOOL' ..."
+rm -f NEWS
+touch NEWS
+for news in `ls -r ../xdocs/news/[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9].xml`
+do
+  print=`echo $news | sed -e 's#xdocs/news#docs/news/printer#' -e 's#\.xml#.html#'`
+  echo "Adding $print to NEWS file ..."
+  ${TOOL} $print >>NEWS
+done
+if [ ! -s NEWS ]
+then
+  echo "Can't convert html to text (NEWS)"
+  exit 1
+fi
+
+# Generate configure et. al.
+./buildconf.sh
+cd ../../
+
+# Pack and sign
+tar cfz ${JK_DIST}.tar.gz --owner="${JK_OWNER}" --group="${JK_GROUP}" ${JK_DIST}
+perl ${JK_DIST}/tools/lineends.pl --cr ${JK_DIST}
+zip -9 -r ${JK_DIST}.zip ${JK_DIST}
+# Create detatched signature
+gpg -ba ${JK_DIST}.tar.gz
+gpg -ba ${JK_DIST}.zip
diff --git a/connectors/jk/tools/lineends.pl b/connectors/jk/tools/lineends.pl
new file mode 100755
index 0000000..8b9c0ef
--- /dev/null
+++ b/connectors/jk/tools/lineends.pl
@@ -0,0 +1,149 @@
+#!/usr/bin/perl
+#
+#  Heuristically converts line endings to the current OS's preferred format
+#  
+#  All existing line endings must be identical (e.g. lf's only, or even
+#  the accedental cr.cr.lf sequence.)  If some lines end lf, and others as
+#  cr.lf, the file is presumed binary.  If the cr character appears anywhere
+#  except prefixed to an lf, the file is presumed binary.  If there is no 
+#  change in the resulting file size, or the file is binary, the conversion 
+#  is discarded.
+#  
+#  Todo: Handle NULL stdin characters gracefully.
+#
+
+use IO::File;
+use File::Find;
+
+# The ignore list is '-' seperated, with this leading hyphen and
+# trailing hyphens in ever concatinated list below.
+$ignore = "-";
+
+# Image formats
+$ignore .= "gif-jpg-jpeg-png-ico-bmp-";
+
+# Archive formats
+$ignore .= "tar-gz-z-zip-jar-war-bz2-tgz-";
+
+# Many document formats
+$ignore .= "eps-psd-pdf-ai-";
+
+# Some encodings
+$ignore .= "ucs2-ucs4-";
+
+# Some binary objects
+$ignore .= "class-so-dll-exe-obj-a-o-lo-slo-sl-dylib-";
+
+# Some build env files
+$ignore .= "mcp-xdc-ncb-opt-pdb-ilk-sbr-";
+
+$preservedate = 1;
+
+$forceending = 0;
+
+$givenpaths = 0;
+
+$notnative = 0;
+
+while (defined @ARGV[0]) {
+    if (@ARGV[0] eq '--touch') {
+        $preservedate = 0;
+    }
+    elsif (@ARGV[0] eq '--nocr') {
+        $notnative = -1;
+    }
+    elsif (@ARGV[0] eq '--cr') {
+        $notnative = 1;
+    }
+    elsif (@ARGV[0] eq '--force') {
+        $forceending = 1;
+    }
+    elsif (@ARGV[0] eq '--FORCE') {
+        $forceending = 2;
+    }
+    elsif (@ARGV[0] =~ m/^-/) {
+        die "What is " . @ARGV[0] . " supposed to mean?\n\n" 
+	  . "Syntax:\t$0 [option()s] [path(s)]\n\n" . <<'OUTCH'
+Where:	paths specifies the top level directory to convert (default of '.')
+	options are;
+
+	  --cr     keep/add one ^M
+	  --nocr   remove ^M's
+	  --touch  the datestamp (default: keeps date/attribs)
+	  --force  mismatched corrections (unbalanced ^M's)
+	  --FORCE  all files regardless of file name!
+
+OUTCH
+    }
+    else {
+        find(\&totxt, @ARGV[0]);
+	print "scanned " . @ARGV[0] . "\n";
+	$givenpaths = 1;
+    }
+    shift @ARGV;
+}
+
+if (!$givenpaths) {
+    find(\&totxt, '.');
+    print "did .\n";
+}
+
+sub totxt {
+        $oname = $_;
+	$tname = '.#' . $_;
+        if (!-f) {
+            return;
+        }
+	@exts = split /\./;
+	if ($forceending < 2) {
+            while ($#exts && ($ext = pop(@exts))) {
+                if ($ignore =~ m|-$ext-|i) {
+                    return;
+                }
+	    }
+        }
+	@ostat = stat($oname);
+        $srcfl = new IO::File $oname, "r" or die;
+	$dstfl = new IO::File $tname, "w" or die;
+        binmode $srcfl; 
+	if ($notnative) {
+            binmode $dstfl;
+	} 
+	undef $t;
+        while (<$srcfl>) { 
+            if (s/(\r*)\n$/\n/) {
+		$n = length $1;
+		if (!defined $t) { 
+		    $t = $n; 
+		}
+		if (!$forceending && (($n != $t) || m/\r/)) {
+		    print "mismatch in " .$oname. ":" .$n. " expected " .$t. "\n";
+		    undef $t;
+		    last;
+		}
+	        elsif ($notnative > 0) {
+                    s/\n$/\r\n/; 
+                }
+            }
+	    print $dstfl $_; 
+	}
+	if (defined $t && (tell $srcfl == tell $dstfl)) {
+	    undef $t;
+	}
+	undef $srcfl;
+	undef $dstfl;
+	if (defined $t) {
+            unlink $oname or die;
+            rename $tname, $oname or die;
+            @anames = ($oname);
+            if ($preservedate) {
+                utime $ostat[9], $ostat[9], @anames;
+            }
+            chmod $ostat[2] & 07777, @anames;
+            chown $ostat[5], $ostat[6], @anames;
+            print "Converted file " . $oname . " to text in " . $File::Find::dir . "\n"; 
+	}
+	else {
+	    unlink $tname or die;
+	}
+}
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..016979e
--- /dev/null
+++ b/connectors/jk/tools/reports/tomcat_reports.pl
@@ -0,0 +1,431 @@
+#!/usr/local/bin/perl
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# $Id$
+
+# 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 "Graph Plot Failed: " . $graph->error 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 "Graph Plot Failed: " . $graph->error 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 "Graph Plot Failed: " . $graph->error 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 "Graph Plot Failed: " . $graph->error 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 "Graph Plot Failed: " . $graph->error 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..da4640c
--- /dev/null
+++ b/connectors/jk/tools/reports/tomcat_trend.pl
@@ -0,0 +1,408 @@
+#!/usr/local/bin/perl
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# $Id$
+
+# 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];
+   if ($day == 31) {
+      $day=1;
+      $month++;
+      if ($month > 11) {
+         $month=0;
+         $year++;
+      }
+   }
+   $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\)\]{0,1}: / ) {
+            # 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..3b74c5c
--- /dev/null
+++ b/connectors/jk/xdocs/.cvsignore
@@ -0,0 +1,2 @@
+jk
+jk2
diff --git a/connectors/jk/xdocs/ajp/ajpv13a.xml b/connectors/jk/xdocs/ajp/ajpv13a.xml
new file mode 100644
index 0000000..eaec1e3
--- /dev/null
+++ b/connectors/jk/xdocs/ajp/ajpv13a.xml
@@ -0,0 +1,698 @@
+<?xml version="1.0"?> 
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="ajpv13a.html">
+
+  &project;
+ <copyright>
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT 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>
+<body>
+<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>getScheme()</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. Note a size of -1
+  (65535) indicates a null string and no data follow the length in this
+  case.</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>?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>?secret</td><td>0x0C</td><td></td></tr>
+  <tr><td>?stored_method</td><td>0x0D</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>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>
+
+</body>
+</document>
diff --git a/connectors/jk/xdocs/ajp/ajpv13ext.xml b/connectors/jk/xdocs/ajp/ajpv13ext.xml
new file mode 100644
index 0000000..2c4b7df
--- /dev/null
+++ b/connectors/jk/xdocs/ajp/ajpv13ext.xml
@@ -0,0 +1,686 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="ajpv13ext.html">
+
+  &project;
+<copyright>
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT 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$</date>
+</properties>
+<body>
+<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)
+    ?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>
+</body>
+</document>
diff --git a/connectors/jk/xdocs/ajp/project.xml b/connectors/jk/xdocs/ajp/project.xml
new file mode 100644
index 0000000..0e66f0e
--- /dev/null
+++ b/connectors/jk/xdocs/ajp/project.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<project name="Apache Tomcat Connector Documentation - AJP Protocol Reference"
+        href="http://tomcat.apache.org/">
+
+    <title>The Apache Tomcat Connector - AJP Protocol Reference</title>
+
+    <logo href="/images/tomcat.gif">
+      The Apache Tomcat Connector - AJP Protocol Reference
+    </logo>
+<body>
+
+    <menu name="Links">
+        <item name="Docs Home"                  href="../index.html"/>
+    </menu>
+
+    <menu name="Reference Guide">
+        <item name="workers.properties"         href="../reference/workers.html"/>
+        <item name="uriworkermap.properties"    href="../reference/uriworkermap.html"/>
+        <item name="Status Worker"              href="../reference/status.html"/>
+        <item name="Apache"                     href="../reference/apache.html"/>
+        <item name="IIS"                        href="../reference/iis.html"/>
+    </menu>
+
+    <menu name="Generic HowTo">
+        <item name="For the impatient"          href="../generic_howto/quick.html"/>
+        <item name="All about workers"          href="../generic_howto/workers.html"/>
+        <item name="Load Balancing"             href="../generic_howto/loadbalancers.html"/>
+    </menu>
+
+    <menu name="Webserver HowTo">
+        <item name="Apache"                     href="../webserver_howto/apache.html"/>
+        <item name="IIS"                        href="../webserver_howto/iis.html"/>
+        <item name="Netscape/SunOne/Sun"        href="../webserver_howto/nes.html"/>
+    </menu>
+
+    <menu name="AJP Protocol Reference">
+        <item name="AJPv13"                     href="../ajp/ajpv13a.html"/>
+        <item name="AJPv13 Extension Proposal"  href="../ajp/ajpv13ext.html"/>
+    </menu>
+
+    <menu name="Miscellaneous Documentation">
+        <item name="Frequently asked questions" href="../miscellaneous/faq.html"/>
+        <item name="Changelog"                  href="../miscellaneous/changelog.html"/>
+        <item name="Current Native:JK bugs"     href="http://issues.apache.org/bugzilla/buglist.cgi?query_format=advanced&amp;short_desc_type=allwordssubstr&amp;short_desc=&amp;product=Tomcat+5&amp;component=Native%3AJK&amp;long_desc_type=substring&amp;long_desc=&amp;bug_file_loc_type=allwordssubstr&amp;bug_file_loc=&amp;keywords_type=allwords&amp;keywords=&amp;bug_status=NEW&amp;bug_status=ASSIGNED&amp;bug_status=REOPENED&amp;emailassigned_to1=1&amp;emailtype1=substring&amp;email1=&amp;emailassigned_to2=1&amp;emailreporter2=1&amp;emailcc2=1&amp;emailtype2=substring&amp;email2=&amp;bugidtype=include&amp;bug_id=&amp;votes=&amp;chfieldfrom=&amp;chfieldto=Now&amp;chfieldvalue=&amp;cmdtype=doit&amp;order=Reuse+same+sort+as+last+time&amp;field0-0-0=noop&amp;type0-0-0=noop&amp;value0-0-0="/>
+        <item name="Contribute documentation"   href="../miscellaneous/doccontrib.html"/>
+        <item name="JK Status Ant Tasks"        href="../miscellaneous/jkstatustasks.html"/>
+        <item name="Reporting Tools"            href="../miscellaneous/reporttools.html"/>
+        <item name="Old JK/JK2 documentation"   href="http://tomcat.apache.org/connectors-doc-archive/jk2/index.html"/>
+    </menu>
+
+    <menu name="News">
+        <item name="2007"                       href="../news/20070301.html"/>
+        <item name="2006"                       href="../news/20060101.html"/>
+        <item name="2005"                       href="../news/20050101.html"/>
+        <item name="2004"                       href="../news/20041100.html"/>
+    </menu>
+    
+</body>
+</project>
diff --git a/connectors/jk/xdocs/build.xml b/connectors/jk/xdocs/build.xml
new file mode 100644
index 0000000..cddf054
--- /dev/null
+++ b/connectors/jk/xdocs/build.xml
@@ -0,0 +1,242 @@
+<project name="tomcat-docs" default="build-main" 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="build.properties"/>
+  <property file="../build.properties"/>
+  <property file="../../build.properties"/>
+  <property file="${user.home}/build.properties"/>
+
+  <property name="build.compiler"  value="modern"/>
+  <property name="build.dir"   value="../build"/>
+  <property name="dist.dir"    value="../dist"/>
+  <property name="dist.name"     value="docs"/>
+
+
+  <!-- =================== BUILD: Create Directories ====================== -->
+  <target name="build-prepare">
+    <mkdir dir="${build.dir}"/>
+    <mkdir dir="${build.dir}/${dist.name}"/>
+  </target>
+
+
+  <!-- ================ BUILD: Copy Static Files ========================== -->
+  <target name="build-static" depends="build-prepare">
+
+    <!-- Top Level Static Files -->
+    <copy    todir="${build.dir}/${dist.name}">
+      <fileset dir=".">
+        <include name="BUILDING.txt"/>
+        <include name="README.txt"/>
+        <include name="RUNNING.txt"/>
+        <include name="style.css"/>
+      </fileset>
+    </copy>
+    <copy    todir="${build.dir}/${dist.name}">
+      <fileset dir="." includes="**/*.html"/>
+    </copy>
+
+    <!-- Images Subdirectory -->
+    <mkdir     dir="${build.dir}/${dist.name}/images"/>
+    <copy    todir="${build.dir}/${dist.name}/images">
+      <fileset dir="images"/>
+    </copy>
+
+    <mkdir     dir="${build.dir}/${dist.name}/printer"/>
+    <!-- Top Level Static Files -->
+    <copy    todir="${build.dir}/${dist.name}/printer">
+      <fileset dir=".">
+        <include name="BUILDING.txt"/>
+        <include name="README.txt"/>
+        <include name="RUNNING.txt"/>
+      </fileset>
+    </copy>
+    <style basedir="."
+           destdir="${build.dir}/${dist.name}/printer"
+         extension=".html"
+             style="style.xsl"
+          excludes="build.xml project.xml empty.xml"
+          includes="*.xml">
+      <param name="relative-path" expression="./.."/>
+      <param name="project-menu" expression="nomenu"/>
+    </style>
+
+    <!-- Reference Guide -->
+    <style basedir="reference"
+           destdir="${build.dir}/${dist.name}/reference"
+         extension=".html"
+             style="style.xsl"
+          excludes="project.xml"
+          includes="*.xml">
+      <param name="relative-path" expression=".."/>
+    </style>
+    <mkdir     dir="${build.dir}/${dist.name}/reference/printer"/>
+    <style basedir="reference"
+           destdir="${build.dir}/${dist.name}/reference/printer"
+         extension=".html"
+             style="style.xsl"
+          excludes="project.xml"
+          includes="*.xml">
+      <param name="relative-path" expression="../.."/>
+      <param name="project-menu" expression="nomenu"/>
+    </style>
+
+    <!-- Generic Howto -->
+    <style basedir="generic_howto"
+           destdir="${build.dir}/${dist.name}/generic_howto"
+         extension=".html"
+             style="style.xsl"
+          excludes="project.xml"
+          includes="*.xml">
+      <param name="relative-path" expression=".."/>
+    </style>
+    <mkdir     dir="${build.dir}/${dist.name}/generic_howto/printer"/>
+    <style basedir="generic_howto"
+           destdir="${build.dir}/${dist.name}/generic_howto/printer"
+         extension=".html"
+             style="style.xsl"
+          excludes="project.xml"
+          includes="*.xml">
+      <param name="relative-path" expression="../.."/>
+      <param name="project-menu" expression="nomenu"/>
+    </style>
+
+        <!-- Webserver Howto -->
+        <style basedir="webserver_howto"
+               destdir="${build.dir}/${dist.name}/webserver_howto"
+             extension=".html"
+                 style="style.xsl"
+              excludes="project.xml"
+              includes="*.xml">
+          <param name="relative-path" expression=".."/>
+        </style>
+        <mkdir     dir="${build.dir}/${dist.name}/webserver_howto/printer"/>
+        <style basedir="webserver_howto"
+               destdir="${build.dir}/${dist.name}/webserver_howto/printer"
+             extension=".html"
+                 style="style.xsl"
+              excludes="project.xml"
+              includes="*.xml">
+          <param name="relative-path" expression="../.."/>
+          <param name="project-menu" expression="nomenu"/>
+        </style>
+
+        <!-- AJP Protocol Reference -->
+        <style basedir="ajp"
+               destdir="${build.dir}/${dist.name}/ajp"
+             extension=".html"
+                 style="style.xsl"
+              excludes="project.xml"
+              includes="*.xml">
+          <param name="relative-path" expression=".."/>
+        </style>
+        <mkdir     dir="${build.dir}/${dist.name}/ajp/printer"/>
+        <style basedir="ajp"
+               destdir="${build.dir}/${dist.name}/ajp/printer"
+             extension=".html"
+                 style="style.xsl"
+              excludes="project.xml"
+              includes="*.xml">
+          <param name="relative-path" expression="../.."/>
+          <param name="project-menu" expression="nomenu"/>
+        </style>
+
+        <!-- Miscellaneous Documentation -->
+        <style basedir="miscellaneous"
+               destdir="${build.dir}/${dist.name}/miscellaneous"
+             extension=".html"
+                 style="style.xsl"
+              excludes="project.xml"
+              includes="*.xml">
+          <param name="relative-path" expression=".."/>
+        </style>
+        <mkdir     dir="${build.dir}/${dist.name}/miscellaneous/printer"/>
+        <style basedir="miscellaneous"
+               destdir="${build.dir}/${dist.name}/miscellaneous/printer"
+             extension=".html"
+                 style="style.xsl"
+              excludes="project.xml"
+              includes="*.xml">
+          <param name="relative-path" expression="../.."/>
+          <param name="project-menu" expression="nomenu"/>
+        </style>
+
+        <!-- News -->
+        <style basedir="news"
+               destdir="${build.dir}/${dist.name}/news"
+             extension=".html"
+                 style="style.xsl"
+              excludes="project.xml"
+              includes="*.xml">
+          <param name="relative-path" expression=".."/>
+        </style>
+        <mkdir     dir="${build.dir}/${dist.name}/news/printer"/>
+        <style basedir="news"
+               destdir="${build.dir}/${dist.name}/news/printer"
+             extension=".html"
+                 style="style.xsl"
+              excludes="project.xml"
+              includes="*.xml">
+          <param name="relative-path" expression="../.."/>
+          <param name="project-menu" expression="nomenu"/>
+        </style>
+ 
+  </target>
+
+
+  <!-- ================= BUILD: XML-HTML Generation ======================= -->
+  <target name="build-main" depends="build-static">
+
+    <!-- Top Level Directory -->
+    <style basedir="."
+           destdir="${build.dir}/${dist.name}"
+         extension=".html"
+             style="style.xsl"
+          excludes="build.xml project.xml empty.xml"
+          includes="*.xml">
+      <param name="relative-path" expression="."/>
+    </style>
+
+  </target>
+
+
+  <!-- ==================== BUILD: Rebuild Everything ===================== -->
+  <target name="all" depends="build-clean,build-main"
+   description="Clean and build documentation"/>
+
+
+  <!-- ======================= BUILD: Clean Directory ===================== -->
+  <target name="build-clean">
+    <delete dir="${build.dir}/${dist.name}"/>
+  </target>
+
+
+  <!-- ======================= DIST: Create Directories =================== -->
+  <target name="dist-prepare">
+    <mkdir dir="${dist.dir}"/>
+  </target>
+
+
+  <!-- ======================= DIST: Create Distribution Files ============ -->
+  <target name="dist" depends="build-main,dist-prepare"
+   description="Create documentation binary distribution">
+      <jar   jarfile="${dist.dir}/${dist.name}.war"
+             basedir="${build.dir}/${dist.name}" includes="**"/>
+  </target>
+
+
+  <!-- ======================= DIST: Clean Directory ====================== -->
+  <target name="dist-clean">
+    <delete dir="${dist.dir}/${dist.name}"/>
+  </target>
+
+
+  <!-- ====================== Convenient Synonyms ========================= -->
+  <target name="clean" depends="build-clean,dist-clean"
+   description="Clean build and dist directories"/>
+
+
+</project>
diff --git a/connectors/jk/xdocs/empty.xml b/connectors/jk/xdocs/empty.xml
new file mode 100644
index 0000000..e241ce0
--- /dev/null
+++ b/connectors/jk/xdocs/empty.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="empty.html">
+  &project;
+<copyright>
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT 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>Empty template</title>
+<author email="developer@apache.org">Developer Name</author>
+
+<date>$Date$</date>
+</properties>
+<body>
+
+<section name="Generic">
+<p>
+This is a generic template for JK documentation. Please fill in the <b>url</b> and <b>title</b>
+tags, as well as <b>author</b> tags.
+</p>
+</section>
+
+</body>
+</document>
diff --git a/connectors/jk/xdocs/generic_howto/loadbalancers.xml b/connectors/jk/xdocs/generic_howto/loadbalancers.xml
new file mode 100644
index 0000000..2e2dc1f
--- /dev/null
+++ b/connectors/jk/xdocs/generic_howto/loadbalancers.xml
@@ -0,0 +1,168 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="loadbalancers.html">
+
+  &project;
+<copyright>
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT 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>LoadBalancer HowTo</title>
+<author email="mturk@apache.org">Mladen Tur</author>
+<date>$Date$</date>
+</properties>
+<body>
+<section name="Introduction"> 
+<br/>
+<p>A Load balancer is a virtual worker that does not really communicate with Tomcat workers.
+Instead it is responsible for the management of several "real" workers.
+The worker is supposed to be a load balancer if its worker type is <b>lb</b>.
+See workers <b>type</b> directive. For a complete reference of all load balancer configuration
+items, please consult the worker <a href="../reference/workers.html">reference</a>.
+The comprehensive status management features of the load balancer together with the status worker,
+makes its use an interesting option, even if only combined with a single "real" worker.
+<warn>The workers that are member of load balancer do not need to appear in the
+<b>worker.list</b> directive.</warn>
+</p>
+
+<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 falling-back 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 some properties that the lb worker can accept:
+<ul>
+<li><b>balance_workers</b> is a comma separated list of workers that the load balancer need to manage. 
+These workers do not need to appear in the worker.list property. This directive can be used multiple times for the same load balancer.</li>
+<li><b>sticky_session</b> specifies whether requests with SESSION ID's should be routed back to the same
+Tomcat worker. You can set sticky_session to 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 True.</li>
+</ul>
+</p>
+
+<source>
+  # The worker balance1 while use "real" workers worker1 and worker2
+  worker.balance1.balance_workers=worker1, worker2
+</source>
+
+</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>redirect</b> and <b>activation</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>
+
+<source>
+  # The advanced router LB worker
+  worker.list=router
+
+  # Define a worker using ajp13
+  worker.worker1.port=8009
+  worker.worker1.host=node1.domain.org
+  worker.worker1.type=ajp13
+  worker.worker1.lbfactor=1
+  # Define prefered failover node for worker1
+  worker.worker1.redirect=worker2
+
+  # Define another worker using ajp13
+  worker.worker2.port=8009
+  worker.worker2.host=node2.domain.org
+  worker.worker2.type=ajp13
+  worker.worker2.lbfactor=1
+  # Disable worker2 for all requests except failover
+  worker.worker2.activation=disabled
+  
+  # Define the LB worker
+  worker.router.type=lb
+  worker.router.balance_workers=worker1,worker2
+</source>
+
+<p>
+The <b>redirect</b> flag on worker1 tells the <b>lb_worker</b> to redirect the requests
+to worker2 only if worker1 is in error state. In other cases worker2 will not receive
+any requests, thus acting like a hot standby.
+</p>
+
+
+</subsection>
+
+<subsection name="Status Worker properties">
+<p>
+The status worker does not communicate with Tomcat.
+Instead it is responsible for the load balancer management. 
+</p>
+<source>
+  # Add the status worker to the worker list
+  worker.list=jkstatus
+  # Define a 'jkstatus' worker using status
+  worker.jkstatus.type=status
+</source>
+<p>Next thing is to mount the requests to the jkstatus worker. For Apache
+web servers use the:</p>
+<source>
+  # Add the jkstatus mount point
+  JkMount /jkmanager/* jkstatus 
+</source>
+<p>To obtain a higher level of security use the:</p>
+<source>
+  # Enable the JK manager access from localhost only
+ &lt;Location /jkmanager/&gt;
+    JkMount jkstatus
+    Order deny,allow
+    Deny from all
+    Allow from 127.0.0.1
+ &lt;/Location&gt;
+</source>
+
+</subsection>
+
+</section>
+
+</body>
+</document>
diff --git a/connectors/jk/xdocs/generic_howto/project.xml b/connectors/jk/xdocs/generic_howto/project.xml
new file mode 100644
index 0000000..28c8a48
--- /dev/null
+++ b/connectors/jk/xdocs/generic_howto/project.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<project name="Apache Tomcat Connector Documentation - Generic HowTo"
+        href="http://tomcat.apache.org/">
+
+    <title>The Apache Tomcat Connector - Generic HowTo</title>
+
+    <logo href="/images/tomcat.gif">
+      The Apache Tomcat Connector - Generic HowTo
+    </logo>
+<body>
+
+    <menu name="Links">
+        <item name="Docs Home"                  href="../index.html"/>
+    </menu>
+
+    <menu name="Reference Guide">
+        <item name="workers.properties"         href="../reference/workers.html"/>
+        <item name="uriworkermap.properties"    href="../reference/uriworkermap.html"/>
+        <item name="Status Worker"              href="../reference/status.html"/>
+        <item name="Apache"                     href="../reference/apache.html"/>
+        <item name="IIS"                        href="../reference/iis.html"/>
+    </menu>
+
+    <menu name="Generic HowTo">
+        <item name="For the impatient"          href="../generic_howto/quick.html"/>
+        <item name="All about workers"          href="../generic_howto/workers.html"/>
+        <item name="Load Balancing"             href="../generic_howto/loadbalancers.html"/>
+    </menu>
+
+    <menu name="Webserver HowTo">
+        <item name="Apache"                     href="../webserver_howto/apache.html"/>
+        <item name="IIS"                        href="../webserver_howto/iis.html"/>
+        <item name="Netscape/SunOne/Sun"        href="../webserver_howto/nes.html"/>
+    </menu>
+
+    <menu name="AJP Protocol Reference">
+        <item name="AJPv13"                     href="../ajp/ajpv13a.html"/>
+        <item name="AJPv13 Extension Proposal"  href="../ajp/ajpv13ext.html"/>
+    </menu>
+
+    <menu name="Miscellaneous Documentation">
+        <item name="Frequently asked questions" href="../miscellaneous/faq.html"/>
+        <item name="Changelog"                  href="../miscellaneous/changelog.html"/>
+        <item name="Current Native:JK bugs"     href="http://issues.apache.org/bugzilla/buglist.cgi?query_format=advanced&amp;short_desc_type=allwordssubstr&amp;short_desc=&amp;product=Tomcat+5&amp;component=Native%3AJK&amp;long_desc_type=substring&amp;long_desc=&amp;bug_file_loc_type=allwordssubstr&amp;bug_file_loc=&amp;keywords_type=allwords&amp;keywords=&amp;bug_status=NEW&amp;bug_status=ASSIGNED&amp;bug_status=REOPENED&amp;emailassigned_to1=1&amp;emailtype1=substring&amp;email1=&amp;emailassigned_to2=1&amp;emailreporter2=1&amp;emailcc2=1&amp;emailtype2=substring&amp;email2=&amp;bugidtype=include&amp;bug_id=&amp;votes=&amp;chfieldfrom=&amp;chfieldto=Now&amp;chfieldvalue=&amp;cmdtype=doit&amp;order=Reuse+same+sort+as+last+time&amp;field0-0-0=noop&amp;type0-0-0=noop&amp;value0-0-0="/>
+        <item name="Contribute documentation"   href="../miscellaneous/doccontrib.html"/>
+        <item name="JK Status Ant Tasks"        href="../miscellaneous/jkstatustasks.html"/>
+        <item name="Reporting Tools"            href="../miscellaneous/reporttools.html"/>
+        <item name="Old JK/JK2 documentation"   href="http://tomcat.apache.org/connectors-doc-archive/jk2/index.html"/>
+    </menu>
+
+    <menu name="News">
+        <item name="2007"                       href="../news/20070301.html"/>
+        <item name="2006"                       href="../news/20060101.html"/>
+        <item name="2005"                       href="../news/20050101.html"/>
+        <item name="2004"                       href="../news/20041100.html"/>
+    </menu>
+
+</body>
+</project>
diff --git a/connectors/jk/xdocs/generic_howto/quick.xml b/connectors/jk/xdocs/generic_howto/quick.xml
new file mode 100644
index 0000000..63d0e20
--- /dev/null
+++ b/connectors/jk/xdocs/generic_howto/quick.xml
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="quick.html">
+
+  &project;
+<copyright>
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT 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>
+<body>
+<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>web server</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="workers.html">Workers HowTo</a>. 
+</p>
+<p>
+<source>
+
+  # Define 1 real worker using ajp13
+  worker.list=worker1
+  # Set properties for worker1 (ajp13)
+  worker.worker1.type=ajp13
+  worker.worker1.host=localhost
+  worker.worker1.port=8009
+
+</source>
+</p>
+</section>
+
+<section name="Minimum Apache web server configuration">
+<p>
+   Here is a minimun informations about Apache configuration, a 
+   complete documentation is available in <a href="apache.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 a link to prebuilt binaries
+        <a href="http://tomcat.apache.org/download-connectors.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>
+<source>
+
+  # Load mod_jk module
+  # Update this path to match your modules location
+  LoadModule    jk_module  libexec/mod_jk.so
+  # Declare the module for &lt;IfModule directive&gt; (remove this line on Apache 2.x)
+  AddModule     mod_jk.c
+  # Where to find workers.properties
+  # Update this path to match your conf directory location (put workers.properties next to httpd.conf)
+  JkWorkersFile /etc/httpd/conf/workers.properties
+  # Where to put jk shared memory
+  # Update this path to match your local state directory or logs directory
+  JkShmFile     /var/log/httpd/mod_jk.shm
+  # Where to put jk logs
+  # Update this path to match your logs directory location (put mod_jk.log next to access_log)
+  JkLogFile     /var/log/httpd/mod_jk.log
+  # Set the jk log level [debug/error/info]
+  JkLogLevel    info
+  # Select the timestamp log format
+  JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "
+  # Send everything for context /examples to worker named worker1 (ajp13)
+  JkMount  /examples/* worker1
+
+</source>
+</p>
+</section>
+
+<section name="Minimum IIS web server configuration">
+<p>
+	A complete documentation is available in <a href="iis.html">IIS HowTo</a>.
+</p>
+<todo>
+More informations to be added!
+</todo>
+</section>
+
+<section name="Minimum NES/iPlanet web server configuration">
+<p>
+	A complete documentation is available in <a href="nes.html">Netscape/iPlanet HowTo</a>.
+<todo>
+More informations to be added?
+</todo>
+</p>
+</section>
+
+
+<section name="Test your configuration">
+<p>
+	(Re)start the web server and browse to the <a href="http://localhost/examples/">http://localhost/examples/</a>
+</p>
+
+</section>
+</body>
+</document>
diff --git a/connectors/jk/xdocs/generic_howto/workers.xml b/connectors/jk/xdocs/generic_howto/workers.xml
new file mode 100644
index 0000000..953b627
--- /dev/null
+++ b/connectors/jk/xdocs/generic_howto/workers.xml
@@ -0,0 +1,463 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="workers.html">
+
+  &project;
+<copyright>
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT 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>
+<author email="mturk@apache.org">Mladen Tur</author>
+<date>$Date$</date>
+</properties>
+<body>
+<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>
+
+<source>
+  # the list of workers
+  worker.list= worker1, worker2
+</source>
+
+<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. The directive can be used multiple times.
+</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>
+  <tr><td>status</td><td>This is a status worker for managing load balancers.</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 only contain any space the characters [a-zA-Z0-9\-_]).
+</p>
+
+<source>
+  # Defines a worker named "local" that uses the ajpv12 protocol to forward requests to a Tomcat process.
+  worker.local.type=ajp12
+  # Defines a worker named "remote" that uses the ajpv13 protocol to forward requests to a Tomcat process.
+  worker.remote.type=ajp13
+  # Defines a worker named "fast" that uses JNI to forward requests to a Tomcat process.
+  worker.fast.type=jni
+  # Defines a worker named "loadbalancer" that loadbalances several Tomcat processes transparently.
+  worker.loadbalancer.type=lb
+</source>
+
+</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><warn>
+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 and 5.5 and Tomcat 6.
+</warn></p> 
+<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 sets the host where the Tomcat worker is listening for ajp12 requests.
+</p>
+
+<p>
+<b>port</b> property sets 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 <a href="../generic_howto/loadbalancers.html">lb worker</a> section.
+</p>
+
+<source>
+  # worker "worker1" will talk to Tomcat listening on machine www.x.com at port 8007 using 2 lb factor
+  worker.worker1.host=www.x.com
+  worker.worker1.port=8007
+  worker.worker1.lbfactor=2
+</source>
+
+<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 tries to compress some of the request data by coding 
+frequently used strings as small integers.
+</li>
+<li>
+ajpv13 reuses open sockets and leaves them open for future requests (remember when you've got a Firewall between your 
+web server 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, 5.0.x, 5.5.x and 6.
+</p>
+
+
+<source>
+  # worker "worker2" will talk to Tomcat listening on machine www2.x.com at port 8009 using 3 lb factor
+  worker.worker2.host=www2.x.com
+  worker.worker2.port=8009
+  worker.worker2.lbfactor=3
+  # worker "worker2" uses connections, which will stay no more than 10mn in the connection pool
+  worker.worker2.connection_pool_timeout=600
+  # worker "worker2" ask operating system to send KEEP-ALIVE signal on the connection
+  worker.worker2.socket_keepalive=1
+  # mount can be used as an alternative to the JkMount directive
+  worker.worker2.mount=/contexta /contexta/* /contextb /contextb/*
+</source>
+
+<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 falling-back 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 some properties that the lb worker can accept:
+<ul>
+<li><b>balance_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. This directive can be used multiple times for the same load balancer.</li>
+<li><b>sticky_session</b> specifies whether requests with SESSION ID's should be routed back to the same
+Tomcat worker. Set sticky_session to 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 True.</li>
+</ul>
+</p>
+
+<source>
+  # The worker balance1 while use "real" workers worker1 and worker2
+  worker.balance1.balance_workers=worker1, worker2
+</source>
+
+</subsection>
+
+<subsection name="Status Worker properties">
+<p>
+The status worker does not communicate with Tomcat.
+Instead it is responsible for the load balancer management. 
+</p>
+<source>
+  # Add the status worker to the worker list
+  worker.list=jkstatus
+  # Define a 'jkstatus' worker using status
+  worker.jkstatus.type=status
+</source>
+<p>Next thing is to mount the requests to the jkstatus worker. For Apache
+web servers use the:</p>
+<source>
+  # Add the jkstatus mount point
+  JkMount /jkmanager/* jkstatus 
+</source>
+<p>To obtain a higher level of security use the:</p>
+<source>
+  # Enable the JK manager access from localhost only
+ &lt;Location /jkmanager/&gt;
+    JkMount jkstatus
+    Order deny,allow
+    Deny from all
+    Allow from 127.0.0.1
+ &lt;/Location&gt;
+</source>
+
+</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>
+
+<source>
+  # property example, don't hardcode path separator
+  ps=\
+  workers.tomcat_home=d:\tomcat
+  workers.java_home=d:\sdk\jdk1.2.2
+  # Using macros we'll have : worker.inprocess.class_path=d:\tomcat\classes
+  worker.inprocess.class_path=$(workers.tomcat_home)$(ps)classes
+  # Using macros we'll have : worker.inprocess.class_path=d:\sdk\jdk1.2.2\lib\tools.jar
+  worker.inprocess.class_path=$(workers.java_home)$(ps)lib$(ps)tools.jar
+</source>
+
+</subsection>
+
+<subsection name="Hierarchical property configuration">
+<p>
+Workers can reference configurations of other workers.
+If worker "x" references worker "y", then it inherits all
+configuration parameters from "y", except for the ones
+that have explicitely been set for "x".
+</p>
+
+<source>
+  # worker toe defines some default settings
+  worker.toe.type=ajp13
+  worker.toe.socket_keepalive=true
+  worker.toe.connect_timeout=10000
+  worker.toe.recovery_options=7
+  # workers tic and tac inherit those values
+  worker.tic.reference=worker.toe
+  worker.tac.reference=worker.toe
+</source>
+
+<p>
+Please note, that the reference contains
+the full prefix to the referenced configuration attributes,
+not only the name of the referenced worker.
+</p>
+
+<p>
+References can be nested. Be careful to avoid loops!
+</p>
+
+<p>
+References are especially useful, when configuring load balancers.
+Try to understand the following two stage references:
+</p>
+
+<source>
+  # We only use one load balancer
+  worker.list=lb
+  # Let's define some defaults
+  worker.basic.port=8009
+  worker.basic.type=ajp13
+  worker.basic.socket_keepalive=true
+  worker.basic.connect_timeout=10000
+  worker.basic.recovery_options=7
+  # And we use them in two groups
+  worker.lb1.domain=dom1
+  worker.lb1.distance=0
+  worker.lb1.reference=worker.basic
+  worker.lb2.domain=dom2
+  worker.lb2.distance=1
+  worker.lb2.reference=worker.basic
+  # Now we configure the load balancer
+  worker.lb.type=lb
+  worker.lb.method=B
+  worker.lb.balanced_workers=w11,w12,w21,w22
+  worker.w11.host=myhost11
+  worker.w11.reference=worker.lb1
+  worker.w12.host=myhost12
+  worker.w12.reference=worker.lb1
+  worker.w21.host=myhost21
+  worker.w21.reference=worker.lb2
+  worker.w22.host=myhost22
+  worker.w22.reference=worker.lb2
+</source>
+
+</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>
+
+<source>
+  # Define some properties
+  workers.apache_log=/var/log/httpd/
+  workers.tomcat_home=/var/tomcat3
+  workers.java_home=/opt/IBMJava2-131/
+  ps=/
+  # Define 4 workers, 3 real workers using ajp12, ajp13, jni, the last one being a loadbalancing worker 
+  worker.list=worker1, worker2, worker3, worker4
+  # Set properties for worker1 (ajp12)
+  worker.worker1.type=ajp12
+  worker.worker1.host=locahost
+  worker.worker1.port=8007
+  worker.worker1.lbfactor=1
+  # Set properties for worker2 (ajp13)
+  worker.worker2.type=ajp13
+  worker.worker2.host=locahost
+  worker.worker2.port=8009
+  worker.worker2.lbfactor=1
+  worker.worker2.connection_pool_timeout=600
+  worker.worker2.socket_keepalive=1
+  worker.worker2.socket_timeout=60
+  # Set properties for worker3 (jni)
+  worker.worker3.type=jni
+  # Set worker3 bridge type, here Tomcat 3.3
+  worker.worker3.bridge=tomcat33
+  # Set worker3 classpath
+  worker.worker3.class_path=$(workers.tomcat_home)$(ps)classes
+  worker.worker3.class_path=$(workers.tomcat_home)$(ps)lib$(ps)tomcat.jar
+  # Set worker3 tomcat command line
+  worker.worker3.cmd_line=-home
+  worker.worker3.cmd_line=$(workers.tomcat_home)
+  # Set worker3 Tomcat/JVM settings
+  worker.worker3.jvm_lib=$(workers.java_home)$(ps)jre$(ps)bin$(ps)classic$(ps)libjvm.so
+  worker.worker3.stdout=$(workers.apache_log)$(ps)inprocess.stdout
+  worker.worker3.stderr=$(workers.apache_log)$(ps)inprocess.stderr
+  worker.worker3.sysprops=tomcat.home=$(workers.tomcat_home)
+  # Set properties for worker4 (lb) which use worker1 and worker2
+  worker.worker4.balance_workers=worker1,worker2
+</source>
+
+</section>
+</body>
+</document>
diff --git a/connectors/jk/xdocs/images/add.gif b/connectors/jk/xdocs/images/add.gif
new file mode 100644
index 0000000..0774d07
--- /dev/null
+++ b/connectors/jk/xdocs/images/add.gif
Binary files differ
diff --git a/connectors/jk/xdocs/images/code.gif b/connectors/jk/xdocs/images/code.gif
new file mode 100644
index 0000000..d27307b
--- /dev/null
+++ b/connectors/jk/xdocs/images/code.gif
Binary files differ
diff --git a/connectors/jk/xdocs/images/design.gif b/connectors/jk/xdocs/images/design.gif
new file mode 100644
index 0000000..f5db0a9
--- /dev/null
+++ b/connectors/jk/xdocs/images/design.gif
Binary files differ
diff --git a/connectors/jk/xdocs/images/docs.gif b/connectors/jk/xdocs/images/docs.gif
new file mode 100644
index 0000000..d64a4a1
--- /dev/null
+++ b/connectors/jk/xdocs/images/docs.gif
Binary files differ
diff --git a/connectors/jk/xdocs/images/fix.gif b/connectors/jk/xdocs/images/fix.gif
new file mode 100644
index 0000000..d59ad64
--- /dev/null
+++ b/connectors/jk/xdocs/images/fix.gif
Binary files differ
diff --git a/connectors/jk/xdocs/images/jakarta-logo.gif b/connectors/jk/xdocs/images/jakarta-logo.gif
new file mode 100644
index 0000000..049cf82
--- /dev/null
+++ b/connectors/jk/xdocs/images/jakarta-logo.gif
Binary files differ
diff --git a/connectors/jk/xdocs/images/printer.gif b/connectors/jk/xdocs/images/printer.gif
new file mode 100644
index 0000000..5021187
--- /dev/null
+++ b/connectors/jk/xdocs/images/printer.gif
Binary files differ
diff --git a/connectors/jk/xdocs/images/tomcat.gif b/connectors/jk/xdocs/images/tomcat.gif
new file mode 100644
index 0000000..6175673
--- /dev/null
+++ b/connectors/jk/xdocs/images/tomcat.gif
Binary files differ
diff --git a/connectors/jk/xdocs/images/update.gif b/connectors/jk/xdocs/images/update.gif
new file mode 100644
index 0000000..31e22ab
--- /dev/null
+++ b/connectors/jk/xdocs/images/update.gif
Binary files differ
diff --git a/connectors/jk/xdocs/images/void.gif b/connectors/jk/xdocs/images/void.gif
new file mode 100644
index 0000000..e565824
--- /dev/null
+++ b/connectors/jk/xdocs/images/void.gif
Binary files differ
diff --git a/connectors/jk/xdocs/index.xml b/connectors/jk/xdocs/index.xml
new file mode 100644
index 0000000..6344fc3
--- /dev/null
+++ b/connectors/jk/xdocs/index.xml
@@ -0,0 +1,275 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="index.html">
+
+  &project;
+
+  <properties>
+    <author email="mturk@apache.org">Mladen Turk</author>
+    <author email="rjung@apache.org">Rainer Jung</author>
+    <title>Documentation Index</title>
+  </properties>
+
+<body>
+
+<section name="Introduction">
+
+<p>This is the top-level entry point of the documentation bundle for the
+<strong>Apache Tomcat Connectors</strong> 
+
+</p>
+<p>Select one of the links from the navigation menu (to the left) to drill
+down to the more detailed documentation that is available. Each available
+manual is described in more detail below.</p>
+
+</section>
+
+<section name="Headlines">
+<br />
+<ul>
+<li><a href="news/20070301.html#20070518.1">18 May 2007 - <b>JK-1.2.23 released</b></a>
+<p>The Apache Tomcat team is proud to announce the immediate availability
+of Tomcat Connectors 1.2.23 Stable.
+</p>
+<p>This version addresses the security flaw:
+<br />
+<a href="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2007-1860"><b>CVE-2007-1860</b></a>
+A double encoded ".." in a URL can be used to access URLs on the AJP backend,
+for which no mod_jk forwarding rule exists (patch for CVE-2007-0450 was insufficient).
+</p><p>
+This version fixes the problem by using ForwardURICompatUnparsed
+as the default for the forwarding JkOption.
+You can similarly fix the problem for all previous versions of mod_jk by setting
+"JkOption ForwardURICompatUnparsed".
+If you upgrade to version 1.2.23 please ensure, that you do not have
+a different forwarding option in your existing configuration.
+We highly recommend, that you are consulting the
+<a href="reference/apache.html#Forwarding">forwarding documentation</a>,
+especially concerning the implications for interaction with mod_rewrite.
+</p><p>
+Please note that this issue only affects configurations,
+which use a prefix forwarding rule like "/myapp/*" or "/myapp/*.jsp"
+to restrict access to the context "/myapp". The issue will allow 
+malicious URLs to reach "/otherapp" or "/otherapp/*.jsp" as well.
+</p><p>
+The Tomcat Project thanks Kazu Nambo for his responsible reporting of this 
+vulnerability.
+</p>
+<p>Download the <a href="http://www.apache.org/dist/tomcat/tomcat-connectors/jk/source/jk-1.2.23/tomcat-connectors-1.2.23-src.tar.gz">JK 1.2.23 release sources</a>
+ | <a href="http://www.apache.org/dist/tomcat/tomcat-connectors/jk/source/jk-1.2.23/tomcat-connectors-1.2.23-src.tar.gz.asc">PGP signature</a>
+</p>
+<p>Download the <a href="http://www.apache.org/dist/tomcat/tomcat-connectors/jk/binaries/">binaries</a> for selected platforms.
+</p>
+</li>
+<li><a href="news/20070301.html#20070417.1">17 April 2007 - <b>JK-1.2.22 released</b></a>
+<p>The Apache Tomcat team is proud to announce the immediate availability
+of Tomcat Connectors 1.2.22 Stable.
+</p>
+<p>Download the <a href="http://www.apache.org/dist/tomcat/tomcat-connectors/jk/source/jk-1.2.22/tomcat-connectors-1.2.22-src.tar.gz">JK 1.2.22 release sources</a>
+ | <a href="http://www.apache.org/dist/tomcat/tomcat-connectors/jk/source/jk-1.2.22/tomcat-connectors-1.2.22-src.tar.gz.asc">PGP signature</a>
+</p>
+<p>Download the <a href="http://www.apache.org/dist/tomcat/tomcat-connectors/jk/binaries/">binaries</a> for selected platforms.
+</p>
+</li>
+<li><a href="news/20070301.html#20070301.1">1 March 2007 - <b>JK-1.2.21 released</b></a>
+<p>The Apache Tomcat team is proud to announce the immediate availability
+of Tomcat Connectors 1.2.21 Stable.
+</p>
+<p>This version addresses the security flaw:
+<br />
+<a href="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2007-0774"><b>CVE-2007-0774</b></a>
+A Long URL Stack Overflow Vulnerability exists in the URI handler for the mod_jk library.
+When parsing a long URL request, the URI worker map routine performs an
+unsafe memory copy. This results in a stack overflow condition which can
+be leveraged execute arbitrary code.
+</p><p>
+Please note this issue only affected versions 1.2.19 and 1.2.20 of the
+JK Apache Tomcat Connector and not previous versions.
+Tomcat 5.5.20 and Tomcat 4.1.34
+included a vulnerable version in their source packages.
+<strong>No </strong>other source code releases <strong> and no binary packages</strong>
+of Tomcat were affected.
+</p><p>
+The Apache Tomcat project recommends that all users who have built mod_jk from source apply the patch or upgrade to the latest level and rebuild. Providers of mod_jk-based modules in pre-compiled form will be able to determine if this vulnerability applies to their builds. That determination has no bearing on any other builds of mod_jk, and mod_jk users are urged to exercise caution and apply patches or upgrade unless they have specific instructions from the provider of their module.
+</p><p>
+The Tomcat Project thanks an anonymous researcher working with 
+TippingPoint (www.tippingpoint.com) and the Zero Day Initiative 
+(www.zerodayintiative.com) for their responsible reporting of this 
+vulnerability.
+</p>
+<p>Download the <a href="http://www.apache.org/dist/tomcat/tomcat-connectors/jk/source/jk-1.2.21/tomcat-connectors-1.2.21-src.tar.gz">JK 1.2.21 release sources</a>
+ | <a href="http://www.apache.org/dist/tomcat/tomcat-connectors/jk/source/jk-1.2.21/tomcat-connectors-1.2.21-src.tar.gz.asc">PGP signature</a>
+</p>
+<p>Download the <a href="http://www.apache.org/dist/tomcat/tomcat-connectors/jk/binaries/">binaries</a> for selected platforms.
+</p>
+</li>
+<li><a href="news/20060101.html#20061210.1">10 December 2006 - <b>JK-1.2.20 released</b></a>
+<p>The Apache Tomcat team is proud to announce the immediate availability
+of Tomcat Connectors 1.2.20 Stable.
+</p>
+<p>Download the <a href="http://www.apache.org/dist/tomcat/tomcat-connectors/jk/source/jk-1.2.20/tomcat-connectors-1.2.20-src.tar.gz">JK 1.2.20 release sources</a>
+ | <a href="http://www.apache.org/dist/tomcat/tomcat-connectors/jk/source/jk-1.2.20/tomcat-connectors-1.2.20-src.tar.gz.asc">PGP signature</a>
+</p>
+<p>Download the <a href="http://www.apache.org/dist/tomcat/tomcat-connectors/jk/binaries/">binaries</a> for selected platforms.
+</p>
+</li>
+<li><a href="news/20060101.html#20060917.1">17 September 2006 - <b>JK-1.2.19 released</b></a>
+<p>The Apache Tomcat team is proud to announce the immediate availability
+of Tomcat Connectors 1.2.19 Stable.
+</p>
+<p>Download the <a href="http://www.apache.org/dist/tomcat/tomcat-connectors/jk/source/jk-1.2.19/tomcat-connectors-1.2.19-src.tar.gz">JK 1.2.19 release sources</a>
+ | <a href="http://www.apache.org/dist/tomcat/tomcat-connectors/jk/source/jk-1.2.19/tomcat-connectors-1.2.19-src.tar.gz.asc">PGP signature</a>
+</p>
+<p>Download the <a href="http://www.apache.org/dist/tomcat/tomcat-connectors/jk/binaries/">binaries</a> for selected platforms.
+</p>
+</li>
+</ul>
+</section>
+
+<section name="Reference Guide">
+<br />
+<ul>
+<li><a href="reference/workers.html"><b>workers.properties</b></a>
+<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>This page contains detailed description of all workers.properties
+directives.
+</p>
+</li>
+
+<li><a href="reference/uriworkermap.html"><b>uriworkermap.properties</b></a>
+<p>
+The forwarding of requests from the web server to tomcat gets configured by defining mapping rules.
+The so-called <b>uriworkermap</b> file is a mechanism of defining those rules.
+</p>
+</li>
+
+<li><a href="reference/apache.html"><b>Apache</b></a>
+<p>This page contains detailed description of all directives related to
+Apache web server. 
+</p>
+</li>
+
+<li><a href="reference/iis.html"><b>IIS</b></a>
+<p>This page contains detailed description of all IIS directives.
+</p>
+</li>
+
+</ul>
+</section>
+
+<section name="Generic HowTo">
+<br />
+<ul>
+
+<li><a href="generic_howto/quick.html"><b>Quick Start</b></a>
+<p>This page describes the configuration files used by JK on the
+Web Server side for the 'impatients'.
+</p>
+</li>
+<li><a href="generic_howto/workers.html"><b>All about workers</b></a>
+<p>This page contains an overview about the various aspects of defining
+and using workers.
+</p>
+</li>
+<li><a href="generic_howto/loadbalancers.html"><b>Load Balancing</b></a>
+<p>This page contains an introduction on load balancing with JK.
+</p>
+</li>
+
+</ul>
+</section>
+
+<section name="Webserver HowTo">
+<br />
+<p>These pages contain detailed descriptions of how to build and
+install JK for the various web servers.
+</p>
+<ul>
+
+<li><a href="webserver_howto/apache.html"><b>Apache</b></a>
+</li>
+<li><a href="webserver_howto/iis.html"><b>IIS</b></a>
+</li>
+<li><a href="webserver_howto/nes.html"><b>Netscape/SunOne/Sun</b></a>
+</li>
+
+</ul>
+</section>
+
+<section name="AJP Protocol Reference">
+<br />
+<ul>
+<li><a href="ajp/ajpv13a.html"><b>AJPv13</b></a>
+<p>This page describes the Apache JServ Protocol version 1.3 (hereafter
+<b>ajp13</b>). 
+</p>
+</li>
+<li><a href="ajp/ajpv13ext.html"><b>AJPv13 Extension Proposal</b></a>
+<p>This page describes an extension proposal for ajp13.
+</p>
+</li>
+</ul>
+
+</section>
+
+<section name="Miscellaneous documentation">
+<br />
+<ul>
+<li><a href="miscellaneous/faq.html"><b>Frequently asked questions</b></a>
+<p>
+</p>
+</li>
+<li><a href="miscellaneous/changelog.html"><b>Changelog</b></a>
+<p>
+The FAQ detail the changes made in each version of JK.
+</p>
+</li>
+<li><a href="http://issues.apache.org/bugzilla/buglist.cgi?query_format=advanced&amp;short_desc_type=allwordssubstr&amp;short_desc=&amp;product=Tomcat+5&amp;component=Native%3AJK&amp;long_desc_type=substring&amp;long_desc=&amp;bug_file_loc_type=allwordssubstr&amp;bug_file_loc=&amp;keywords_type=allwords&amp;keywords=&amp;bug_status=NEW&amp;bug_status=ASSIGNED&amp;bug_status=REOPENED&amp;emailassigned_to1=1&amp;emailtype1=substring&amp;email1=&amp;emailassigned_to2=1&amp;emailreporter2=1&amp;emailcc2=1&amp;emailtype2=substring&amp;email2=&amp;bugidtype=include&amp;bug_id=&amp;votes=&amp;chfieldfrom=&amp;chfieldto=Now&amp;chfieldvalue=&amp;cmdtype=doit&amp;order=Reuse+same+sort+as+last+time&amp;field0-0-0=noop&amp;type0-0-0=noop&amp;value0-0-0=">
+<b>Current Native:JK bugs</b></a>
+<p>This is the Bugzilla Bug List related to Native:JK.
+</p>
+</li>
+<li><a href="miscellaneous/doccontrib.html"><b>Contribute documentation</b></a>
+<p>
+This page describes, how to contribute to the JK documentation.
+</p>
+</li>
+<li><a href="miscellaneous/tools.html"><b>Tools</b></a>
+<p>
+This page contains information, on some tool scripts contained in the Jk distribution.
+</p>
+</li>
+<li><a href="http://tomcat.apache.org/connectors-doc-archive/jk2/index.html">
+<b>Old JK/JK2 documentation archive.</b></a>
+<p>Here you can find old JK and JK2 documentation.
+</p>
+</li>
+</ul>
+
+</section>
+
+<section name="News">
+<br />
+<p>Release news from various years.
+</p>
+
+<ul>
+<li><a href="news/20070301.html"><b>2007</b></a>
+</li>
+<li><a href="news/20060101.html"><b>2006</b></a>
+</li>
+<li><a href="news/20050101.html"><b>2005</b></a>
+</li>
+<li><a href="news/20041100.html"><b>2004</b></a>
+</li>
+
+</ul>
+</section>
+
+</body>
+</document>
diff --git a/connectors/jk/xdocs/jk2/confighowto.xml b/connectors/jk/xdocs/jk2/confighowto.xml
new file mode 100644
index 0000000..0d2fc8a
--- /dev/null
+++ b/connectors/jk/xdocs/jk2/confighowto.xml
@@ -0,0 +1,133 @@
+<?xml version="1.0"?>
+<document>
+<copyright>
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT 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..1b4f131
--- /dev/null
+++ b/connectors/jk/xdocs/jk2/configtc.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0"?>
+<document>
+<copyright>
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT 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..774116a
--- /dev/null
+++ b/connectors/jk/xdocs/jk2/configtccom.xml
@@ -0,0 +1,315 @@
+<?xml version="1.0"?>
+<document>
+<copyright>
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT 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..5c035f6
--- /dev/null
+++ b/connectors/jk/xdocs/jk2/configtcex.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0"?>
+<document>
+<copyright>
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT 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..644a2f4
--- /dev/null
+++ b/connectors/jk/xdocs/jk2/configweb.xml
@@ -0,0 +1,187 @@
+<?xml version="1.0"?>
+<document>
+<copyright>
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT 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..b7fd6b9
--- /dev/null
+++ b/connectors/jk/xdocs/jk2/configwebcom.xml
@@ -0,0 +1,766 @@
+<?xml version="1.0"?>
+<document>
+<copyright>
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT 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..e3fd198
--- /dev/null
+++ b/connectors/jk/xdocs/jk2/configwebex.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0"?>
+<document>
+<copyright>
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT 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..dd3a9ed
--- /dev/null
+++ b/connectors/jk/xdocs/jk2/installhowto.xml
@@ -0,0 +1,263 @@
+<?xml version="1.0"?>
+<document>
+<copyright>
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT 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..a4f382b
--- /dev/null
+++ b/connectors/jk/xdocs/jk2/vhosthowto.xml
@@ -0,0 +1,611 @@
+<?xml version="1.0"?>
+<document>
+<copyright>
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT 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/miscellaneous/changelog.xml b/connectors/jk/xdocs/miscellaneous/changelog.xml
new file mode 100644
index 0000000..c87074f
--- /dev/null
+++ b/connectors/jk/xdocs/miscellaneous/changelog.xml
@@ -0,0 +1,1149 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="changelog.html">
+
+  &project;
+
+  <properties>
+    <author email="mturk@apache.org">Mladen Turk</author>
+    <author email="rjung@apache.org">Rainer Jung</author>
+    <author email="hgomez@apache.org">Henri Gomez</author>
+    <title>Changelog</title>
+  </properties>
+
+<body>
+
+<section name="Preface">
+  <p>
+  This is the Changelog for Tomcat Connectors. This changelog
+  does not contain all updates and fixes to the Tomcat connectors (yet).
+  It should contain fixes made only after November 10th 2004, when the
+  new documentation project for JK was started.
+  </p>
+</section>
+<section name="Changes between 1.2.23 and 1.2.24">
+  <br />
+  <subsection name="Native">
+    <changelog>
+      <fix>
+      Use initial zero timeout for jk_is_socket_connected. The resulting
+      detection is the same but offers a huge performance increase
+      with mod_jk. In most cases the Operating System does not favor
+      the 1 microsecond timeout, but it rather rounds that up to much
+      higher value (frequency of interrupt timer which on most systems
+      defaults to 100Hz).
+      Patch provided by David McLaughlin. (mturk)
+      </fix>
+      <fix>
+      Always build with thread support, unless flag --enable-prefork
+      is set during for configure. (rjung)
+      </fix>
+      <update>
+      i5/OS (AS/400) V5R4 port where Apache 2.0 modules should now use UTF8. (hgomez)
+      </update>
+      <update>
+      Docs: Add comments on i5/OS build for V5R4 and previous releases. (hgomez)
+      </update>
+    </changelog>
+  </subsection>
+</section>
+<section name="Changes between 1.2.22 and 1.2.23">
+  <br />
+  <subsection name="Native">
+    <changelog>
+      <update>
+      Change the default value of JkOptions to ForwardURICompatUnparsed.
+      The old default value was ForwardURICompat.
+      This should make URL interpretation between Apache httpd and
+      Tomcat consistent (prevent double decoding problems). (rjung)
+      </update>
+    </changelog>
+  </subsection>
+</section>
+<section name="Changes between 1.2.21 and 1.2.22">
+  <br />
+  <subsection name="Native">
+    <changelog>
+      <fix>
+      Refactor line endings logging to make it correct for all
+      platforms and webservers. (mturk)
+      </fix>
+      <update>
+      Added command line windows make files. (mturk)
+      </update>
+      <update>
+      Allow fail_on_status directive to be multi line. (mturk)
+      </update>
+      <fix>
+        <bug>42076</bug>: Fix name of new option from ForwardCertChain to
+        ForwardSSLCertChain as documented. (rjung)
+      </fix>
+      <fix>
+      Docs: Fix a couple of typos, change format of a few tables,
+      fix links to news pages. (rjung)
+      </fix>
+      <fix>
+      Fix correct URL for TC 6 examples in new IIS rewrite.properties
+      configuration example file. (rjung)
+      </fix>
+      <fix>
+      Add svn properties to several files. (rjung)
+      </fix>
+      <update>
+      Add TC 6 examples to uriworkermap.properties in config examples. (rjung)
+      </update>
+      <update>
+      Allow multiple status codes for fail_on_status directive.
+      The status codes can be delimited by space or comma characters. (mturk)
+      </update>
+      <update>
+        IIS. Added pcre like regular expressions for url rewrite rules. (mturk)
+      </update>
+      <fix>
+        <bug>41922</bug>: Apache 1.3. Enable JkEnvVar. (mturk)
+      </fix>
+      <update>
+        Apache. Add --enable-flock configure parameter for explicit
+        compilation of faster flock() system calls for OS supporting
+        those calls. By default the fcntl system call for locking will
+        be used that is a little bit slower but it can work on NFS
+        mounted volumes as well. (mturk)
+      </update>
+      <fix>
+        <bug>41562</bug>: Add Debug logging for read from client in ISAPI Redirector.
+         Contributed by Tim Whittington. (mturk)
+      </fix>
+      <update>
+        Apache. Add ForwardSSLCertChain JkOption.
+        Contributed by Patrik Schnellmann. (mturk)
+      </update>
+      <fix>
+        IIS. Do not forbid access to web-inf or meta-inf if there is
+        no mapped worker. This allows to have resource with those names
+        that are outside mapped contexts. (mturk)
+      </fix>
+      <update>
+        Apache. Use process id for creating shared memory name and delete shared
+        memory and shared memory lock files on exit. (mturk)
+      </update>
+      <fix>
+        IIS. Fix Keep-Alive regression introduced in 1.2.21. (mturk)
+      </fix>
+      <update>
+      Delete unused check for empty init_map during startup. (rjung)
+      </update>
+      <fix>
+        <bug>41770</bug>: Fix startup error if no JkWorkersFile is used. (rjung)
+      </fix>
+      <update>
+      Use JK_TRUE/JK_FALSE instead of OK/!OK as return values in init_jk(). (rjung)
+      </update>
+      <update>
+      Minor adjustments to apache startup log messages (when to use STDERR, remove
+      deprecated NOERRNO flag, shm warning and warnings for usage of default files). (rjung)
+      </update>
+      <update>
+      Replace APR precompiler directive by httpd mpm_query to detect MPM threading.
+      Add a debug log message about auto-detected pool size. (rjung)
+      </update>
+      <fix>
+        Make MMN check easier to understand and a little more precise
+        (for new ap_get_server_banner()/ap_get_server_description()).
+        We use the new API only for Apache httpd 2.3. This way our binaries are not
+        tightly coupled to a minor 2.0 version, and we don't use ap_get_server_banner()
+        any way. (rjung)
+      </fix>
+      <fix>
+        Use the full description string ap_get_server_description() instead of
+        the truncated info from ap_get_server_banner(), because this info gets used internally
+        (status worker display and ajp14 backend communication) and is not send back to the
+        normal user. (rjung)
+      </fix>
+      <fix>
+        <bug>41757</bug>: Document the "--enable-prefork" flag of configure. (rjung)
+      </fix>
+      <update>
+      Enhance log messages for failures when parsing attribute maps. (rjung)
+      </update>
+      <fix>
+        Correct log message during worker initialization, in case remote host could not be
+        resolved. We logged the default host name "localhost" instead of the configured one. (rjung)
+      </fix>
+      <fix>
+        <bug>41770</bug>: Fix the second part of the bug: local_worker and local_worker_only
+        is missing from the list of deprecated attributes (and not supported either), so prevents
+        the web server from startup. (rjung)
+      </fix>
+    </changelog>
+  </subsection>
+</section>
+<section name="Changes between 1.2.20 and 1.2.21">
+  <br />
+  <subsection name="Native">
+    <changelog>
+      <fix>
+        <a href="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2007-0774"><b>CVE-2007-0774</b></a>
+        : A denial of service and critical remote code execution vulnerability.
+        Caused by buffer overflow in map_uri_to_worker() when URL were longer that 4095 bytes.
+        Reported by ZDI (www.zerodayintiative.com).
+        Please note this issue only affected versions 1.2.19 and 1.2.20 of the
+        Apache Tomcat JK Web Server Connector and not previous versions.
+        Tomcat 5.5.20 and Tomcat 4.1.34
+        included a vulnerable version in their source packages.
+        Other versions of Tomcat were not affected.
+      </fix>
+      <add>
+      Check the worker. parameters and don't start if the parameter is not a valid one. (jfclere)
+      </add>
+      <add>
+        <bug>41439</bug>: Allow session IDs to get stripped off URLs of static
+        content in Apache by adding JkStripSession
+        directive (configurable per vhost). (mturk)
+      </add>
+      <add>
+      Change semantics of empty defaults for JkEnvVar variables.
+      Until 1.2.19: not allowed. In 1.2.20: send variables as empty strings, if
+      neither set to non empty in config, nor during runtime.
+      Starting with 1.2.21: If config has no second argument only send
+      variable if set (even when set to empty string) during runtime.
+      Allows good combination with condition attribute in tomcat access log. (rjung)
+      </add>
+      <fix>
+        <bug>41610</bug>: Fix incorrect detection of missing Content-Length
+        header leading to duplicate headers. Contributed by Boris Maras. (rjung)
+      </fix>
+      <fix>
+      Better build support for SunONE (Netscape/iPlanet) webservers. (jim)
+      </fix>
+      <add>
+      Add warning if duplicate map keys are read and are not allowed,
+      e.g. when parsing uriworkermap.properties. (rjung)
+      </add>
+      <fix>
+      Don't concat worker names, if uriworkermap.properties has a duplicate
+      pattern, instead overwrite the worker. (rjung)
+      </fix>
+      <fix>
+      Log deprecation message even in duplication case. (rjung)
+      </fix>
+      <fix>
+      uriworkermap.properties: Fix off-by-one problem when deleting
+      URL mapping during reloading of uriworkermap.properties. (rjung)
+      </fix>
+      <add>
+        <bug>41439</bug>: Allow session IDs to get stripped off URLs of static
+        content in IIS (configurable). (rjung)
+      </add>
+      <add>
+        <bug>41333</bug>: Refactoring isapi_plugin configuration reading. (rjung)
+      </add>
+      <add>
+        <bug>41332</bug>: Add some more errno logging and unify the format. (rjung)
+      </add>
+      <add>
+      JkStatus: Improved logging by adding status worker name to messages.
+      Added messages to the recover worker action. (rjung)
+      </add>
+      <add>
+      JkStatus: Refactoring searching for workers and sub workers. (rjung)
+      </add>
+      <add>
+        <bug>41318</bug>: Add configuration to make status worker user
+        name checks case insensitive. (rjung)
+      </add>
+      <add>
+      JkStatus: Add estimated time until next global maintenance to other
+      mime types and adopt jkstatus ant task. (rjung)
+      </add>
+      <add>
+      JkStatus: Show estimated time until next global maintenance.
+      Change displayed time until next recovery to a min/max pair. (rjung)
+      </add>
+      <add>
+      JkStatus: Allow a user of a read/write status worker to switch it
+      to and from read_only mode temporarily. (rjung)
+      </add>
+      <fix>
+      JkStatus: Do not show read/write commands in a read_only status worker. (rjung)
+      </fix>
+      <add>
+      JkStatus: Allow lb sub workers in error state to be marked for recovery
+      administratively from the status worker. (rjung)
+      </add>
+      <add>
+      Load Balancer: Do not try to recover multiple times in parallel.
+      Use additional runtime states "PROBE" and "FORCED". (rjung)
+      </add>
+      <fix>
+      JkStatus: Improve data synchronization between different processes. (rjung)
+      </fix>
+      <fix>
+        <bug>41381</bug>: Fix segfault in feature fail_on_status
+        (wrong order of log arguments). Patch by Juri Haberland. (rjung)
+      </fix>
+      <fix>
+      Use correct windows line endings for log file on WIN32 platform. (rjung)
+      </fix>
+    </changelog>
+  </subsection>
+</section>
+<section name="Changes between 1.2.19 and 1.2.20">
+  <br />
+  <subsection name="Native">
+    <changelog>
+      <add>
+      JkStatus Ant Task documentation page. (pero/rjung)
+      </add>
+      <add>
+      JkStatus Ant Tasks: Add new tasks for update and reset. (pero)
+      </add>
+      <update>
+      JkStatus Ant Tasks: Update for new xml status format. (pero)
+      </update>
+      <update>
+      Allow integer and string values when setting enumeration/boolean
+      attributes via status worker update action. (rjung)
+      </update>
+      <add>
+      Docs: New reference guide page for status worker. (rjung)
+      </add>
+      <update>
+      Docs: Renaming the config dir to reference and using the title
+      Reference Guide in the docs. (rjung)
+      </update>
+      <update>
+      Added retry_on_status for workers directive. (mturk)
+      </update>
+      <update>
+      Status Worker: Add directive to make property prefix
+      and good/bad rule configurable. (rjung)
+      </update>
+      <update>
+      Status Worker: Omit lb members when att=nosw. (rjung)
+      </update>
+      <update>
+      Status Worker: New command cmd=version for a short version output. (rjung)
+      </update>
+      <update>
+      Status Worker: New output stype mime=prop produces property lists. (rjung)
+      </update>
+      <fix>
+      Apache: Fix incorrect handling of JkEnvVar when Vars are set multiple times. (rjung)
+      </fix>
+      <update>
+      Renamed jvm_route to route. Deprecated jvm_route, but still use it as fallback
+      when parsing the worker configuration. (rjung)
+      </update>
+      <update>
+      IIS: Make uriworkermap file reload check interval configurable. (mturk)
+      </update>
+      <update>
+      Apache: Make uriworkermap file reload check interval configurable. (rjung)
+      </update>
+      <update>
+      Status Worker: Add directives for customizing the XML
+      output (ns, xmlns, doctype). (mturk)
+      </update>
+      <add>
+      Docs: New page with description of uriworkermap. (rjung)
+      </add>
+      <update>
+      Docs: Added short description of max_packet_size to worker
+      reference. (rjung)
+      </update>
+      <update>
+      Status Worker: All functions accessible also for xml and txt
+      mime types (list, show, update, reset). (rjung)
+      </update>
+      <update>
+      Status Worker: New global health indicators for load balancers
+      named bad (error, recovering or stopped), degraded (busy or disabled)
+      and good (the rest, active and OK or N/A). (rjung)
+      </update>
+      <update>
+      Status Worker: New edit page, to change one attribute for all
+      members of a load balancer. (rjung)
+      </update>
+      <update>
+      Status Worker: Standard logging for status worker. (rjung)
+      </update>
+      <update>
+      Status Worker: code refactoring. (rjung)
+      </update>
+      <update>
+      Status Worker: New attribute user (list) denies access, if
+      the request user in the sense of remote_user is not in this list.
+      Empty list = no deny (rjung)
+      </update>
+      <update>
+      Status Worker: New attribute read_only disables the parts
+      of the status worker, that change states and configurations. (rjung)
+      </update>
+      <fix>
+        <bug>36121</bug>: Don't change main uri when mod_jk serves
+        included uri. (markt)
+      </fix>
+      <update>
+      Apache VHosts: Merge JkOptions +base - -base + +vhost - -vhost. (rjung)
+      </update>
+      <update>
+      Apache Docs: Adding requirements, context information, default values and
+      inheritance rules to the Apache config documentation. (rjung)
+      </update>
+      <update>
+      Status Worker: Add source type to status worker, remove the redundant "context"
+      column in the map listing (context=uri). (rjung)
+      </update>
+      <update>
+      uriworkermap: On reload of the file, all old entries from the previous file
+      version get deleted, before the new ones are being read. (rjung)
+      </update>
+      <fix>
+      Keep normal maps and exclusion maps internally separate. Don't treat them
+      as the same when adding a rule. (rjung)
+      </fix>
+      <update>
+      Status Worker: Display mapping rules also for non-lb workers and in global view. (rjung)
+      </update>
+      <update>
+      Apache VHosts: Use the vhost log files instead of the main log. (rjung)
+      </update>
+      <update>
+      Apache VHosts: Allow individual timestamp formats by refactoring the formatting
+      method. (rjung)
+      </update>
+      <update>
+      Apache VHosts: Adding all missing config items to the virtual host level.
+      Don't overwrite the settings from the global server, but inherit them
+      in case they are not set in the virtual host. (rjung)
+      </update>
+      <update>
+      Apache: remove unnecessary function names from log messages. (rjung)
+      </update>
+      <update>
+      Apache: add a default log file location and a message, if the default gets used. (rjung)
+      </update>
+      <update>
+      Apache: add missing JK_IS_DEBUG_LEVEL() (rjung)
+      </update>
+      <update>
+      Apache VHosts: Allow JkWorkersFile, JKWorkerProperty, JkShmFile and JkShmFileSize
+      only in global virtual server. (rjung)
+      </update>
+      <update>
+      Add some more jk_close_socket() and reduce log level for some info messages. (rjung)
+      </update>
+      <update>
+      Load Balancer: Added the Sessions strategy. Contributed by Takayuki Kaneko. (rjung)
+      </update>
+      <update>
+      Docs: Minor enhancements and syncing with more recent versions. (rjung)
+      </update>
+      <fix>
+      <bug>40997</bug>: Separate uri mappings from their '!'
+      counterpart when checking for duplicates in uriworkermap
+      reloading. (rjung)
+      </fix>
+      <fix>
+      <bug>40877</bug>: Make sure the shared memory is reset on
+      attach for multiple web server child processes. (mturk)
+      </fix>
+      <update>
+      IIS: Added shm_size property to be able to deal with over 64
+      workers configurations. (mturk)
+      </update>
+      <update>
+      IIS: Increase default thread count to 250, so its the same as Apache Httpd
+      default configuration. (mturk)
+      </update>
+      <fix>
+      <bug>40966</bug>: Fix socket descriptor checks on windows. (mturk)
+      </fix>
+      <fix>
+      <bug>40965</bug>: Initialize missing service parameters. (mturk)
+      </fix>
+      <fix>
+      <bug>40938</bug>: Fix releasing of rewrite map.
+      Thanks to Chris Adams for spotting that. (mturk)
+      </fix>
+      <update>
+      Apache: Added +FlushHeader JkOptions. (mturk)
+      </update>
+      <update>
+      Added explicit flush when AJP body packet size is zero. (mturk)
+      </update>
+      <fix>
+      <bug>40856</bug>: Fixing case sensitivity bug in URL mapping. (rjung)
+      </fix>
+      <fix>
+      <bug>40793</bug>: Documentation: Improvements to Apache HowTo provided by
+      Paul Charles Leddy. (markt)
+      </fix>
+      <fix>
+      <bug>40774</bug>: Fixing wrong recursion termination. This one restricted the
+      "reference" feature unintentionally to 20 workers. (rjung)
+      </fix>
+      <fix>
+      <bug>40716</bug>: Adding "reference" feature to IIS and Netscape. (rjung)
+      </fix>
+      <fix>
+      Documentation: Corrected SetEnvIf syntax in JK_WORKER_NAME example. (rjung)
+      </fix>
+      <fix>
+      Documentation: Added forgotten STATE and ACTIVATION notes for load balancer logging in Apache. (rjung)
+      </fix>
+      <update>
+      Apache: Use instdso.sh instead libtool: libtool does not work on HP-UX for example. (jfclere)
+      </update>
+    </changelog>
+  </subsection>
+</section>
+<section name="Changes between 1.2.18 and 1.2.19">
+  <br />
+  <subsection name="Native">
+    <changelog>
+      <update>
+      Docs: Add SetHandler and new env var to Apache config docs. (rjung)
+      </update>
+      <update>
+      Apache 1.3: Backport "no-jk" feature. (rjung)
+      </update>
+      <update>
+      Apache: Add an environment variable to make SetHandler "jakarta-servlet" more
+      useful. The variable is JK_WORKER_NAME, but can be changed by the
+      new directive JkWorkerIndicator. (rjung)
+      </update>
+      <fix>
+      LB: Don't use single worker shortcut, if the single worker is being diabled. (rjung)
+      </fix>
+      <fix>
+      Status worker: Add short explanation of activation and error states to legend. (rjung)
+      </fix>
+      <fix>
+      Docs: Add meaning of zero timeout values for various timeouts
+      in workers.properties. (rjung)
+      </fix>
+      <fix>
+      LB: Cleanup of Mladens forced recovery. (rjung)
+      </fix>
+      <fix>
+      LB: Do not change lb_value for recovering workers to max, if
+      we are using BUSYNESS method. (rjung)
+      </fix>
+      <fix>
+      Apache: Since 1.2.14 mod_jk failed to detect client abort. (rjung)
+      </fix>
+      <fix>
+      Docs: Corrected description of JkEnvVar. (rjung)
+      </fix>
+      <fix>
+      Solaris: Detect filio.h in configure to make the new connection detection
+      build on solaris (r432825). (rjung)
+      </fix>
+      <update>
+      Add feature to force the recovery of workers that are
+      member of loadbalancer if all the members are in error
+      state. This fixes the time gap where 503 was returned
+      caused by recovery_timeout although the backend was
+      ready to handle the requests. (mturk)
+      </update>
+      <update>
+      Docs: Seperate deprecated directives in their own table. (rjung)
+      </update>
+      <update>
+      Docs: Allow "-" and "_" in worker names. (rjung)
+      </update>
+      <update>
+      Allow multiple lines with attributes "balance_workers" and "mount". (rjung)
+      </update>
+      <fix>
+      Make jk_is_some_property match more precisely. (rjung)
+      </fix>
+      <update>
+      JkStatus: Make refresh interval changeable. (rjung)
+      </update>
+      <fix>
+      JkStatus: Adjust display of recover time wrt. global maintenance. (rjung)
+      </fix>
+      <update>
+      LB: Resetting worker state from OK to NA, if worker has been idle
+      too long. (rjung)
+      </update>
+      <fix>
+      Avoid compiler warnings concerning the use of lb_*_type arrays.
+      Use functions instead. (rjung)
+      </fix>
+      <update>
+      Added %R JkRequestLogFormat option for Apache 1 and Apache 2. (mturk)
+      </update>
+      <update>
+      Allow changing jvm Route from status manager. (mturk)
+      </update>
+      <fix>
+      Do not retun 400 if Tomcat fails in the midle of the post
+      request. Return 500 insted. (mturk)
+      </fix>
+      <update>
+      LB: Combine ok/error/recovering/busy runtime states into a single scalar. (rjung)
+      </update>
+      <update>
+      LB: Combine active/disabled/stopped configuration states into a single scalar. (rjung)
+      </update>
+      <update>
+      LB: Add several Apache notes to enable standard logging for load balancer results. (rjung)
+      </update>
+      <update>
+      LB: Reorganisation of the main load balancer service loop. (rjung)
+      </update>
+      <update>
+      Implement hierarchical worker configuration via attribute "reference". (rjung)
+      </update>
+      <update>
+      Log deprecated properties. (rjung)
+      </update>
+      <fix>
+      IIS: Fix simple_rewrite for the cases where the
+      rewritten url is larger then the original one. (mturk)
+      </fix>
+      <update>
+      New JkOption "DisableReuse" to disable connection persistence. (jim)
+      </update>
+      <update>
+      LB: Move sessionid retrieval out of get_most_suitable_worker into service. (rjung)
+      </update>
+      <update>
+      Code cleanup for all service methods (use TRACE, JK_LOG_NULL_PARAMS, null pointer checks). (rjung)
+      </update>
+      <update>
+      JKSTATUS: add refresh link. No refresh for updates. Redirect to list view after update. (rjung)
+      </update>
+      <update>
+      Add new hook add_log_items into servers. (rjung)
+      </update>
+      <update>
+      APACHE httpd: Rename apache logging notes. (rjung)
+      </update>
+      <update>
+      LB: Rename lock and method constants. Add constants for defaults. (rjung)
+      </update>
+      <fix>
+      Default log level should be INFO and not DEBUG.
+      Default log level should be the same for all server types. (rjung)
+      </fix>
+      <fix>
+      Make rewrite_rule_map and log_level as non mandatory
+      directives for isapi_redirect. (mturk)
+      </fix>
+      <fix>
+      <bug>40107</bug>: Rewrite is_socket_connected function.
+      Non blocking socket is not used any more. (mturk)
+      </fix>
+      <update>
+      Allow building with VS2005 without too many warnings. (mturk)
+      </update>
+      <fix>
+      Decide by MMN, which piped log API we should use.
+      mod_jk 1.2.18 broke compilation with Apache 1.3 pre 1.3.28. (rjung)
+      </fix>
+    </changelog>
+  </subsection>
+</section>
+<section name="Changes between 1.2.17 and 1.2.18">
+  <br />
+  <subsection name="Native">
+    <changelog>
+      <fix>
+      Using socklen_t in getsockopt. Also introducing jk_sock_t. (mturk)
+      </fix>
+      <update>
+      Allow recovery wait time below 60 seconds (new minimum is 1 second). (mturk)
+      </update>
+    </changelog>
+  </subsection>
+</section>
+<section name="Changes between 1.2.16 and JK 1.2.17">
+  <br />
+  <subsection name="Native">
+    <changelog>
+      <fix>
+      Fix hanging jk status worker when certain attributes are being updated
+      due to double locking. (rjung)
+      </fix>
+      <update>
+      Allow JkMount to behave like uriworkermap.properties
+      by parsing pipe symbol as two directive marker. (mturk)
+      </update>
+    </changelog>
+  </subsection>
+</section>
+<section name="Changes between 1.2.15 and JK 1.2.16">
+  <br />
+  <subsection name="Native">
+    <changelog>
+      <update>
+      Added simple rewrite capability for IIS. Although simple it
+      will fulfill most needs. (mturk)
+      </update>
+      <update>
+      Added RECOVER_ABORT_IF_CLIENTERROR recovery_option that closes
+      the connection if client connection is broken during the request. (mturk)
+      </update>
+      <update>
+      Renamed cache_timeout directive to connection_pool_timeout. (mturk)
+      </update>
+      <update>
+      Added connection_pool_minsize directive. (mturk)
+      </update>
+      <update>
+      Deprecate recycle_timeout directive. (mturk)
+      </update>
+      <update>
+      Corrected some HTML syntax bugs in output of status worker. (rjung)
+      </update>
+      <update>
+      Added the refresh=n parameter to the status worker. It will update the display every n seconds. (rjung)
+      </update>
+      <update>
+      Balancer: Add attribute distance to balanced workers to express preferences between workers. (rjung)
+      </update>
+      <update>
+      Balancer: Add attribute jvm_route to balanced workers to be able to use the same target in different balancers. (rjung)
+      </update>
+      <update>
+      Status: Add lb_mult to status. (rjung)
+      </update>
+      <update>
+      Balancer: Make different balancing strategies work in a similar way (use lb_value, use decay during global maintenance, use integer factors for weights. (rjung)
+      </update>
+      <update>
+      Balancer: Improve locking. (rjung)
+      </update>
+      <update>
+      Balancer: Workers start slower after recovering. (rjung)
+      </update>
+      <update>
+      Balancer: Make different balancing strategies work in a similar way (use lb_value, use decay during global maintenance, use integer factors lb_mult for weights). (rjung)
+      </update>
+      <update>
+      Balancer: Move recovery check to global maintenance. (rjung)
+      </update>
+      <update>
+      Balancer: Add global maintenance method, that is called in only one process. (rjung)
+      </update>
+      <update>
+      Extend our use of autoconf to find a 32Bit and a 64Bit unsigned type and their printf formats. (rjung)
+      </update>
+      <update>
+      Logging: piped loggers for JkLogFile and Apache 1.3. (rjung)
+      </update>
+      <update>
+      Logging: Add PID to log lines for each log level apart from REQUEST. (rjung)
+      </update>
+      <update>
+      Logging: flush buffered logs to keep lines in correct order. Output final newline together with log message. (rjung)
+      </update>
+      <update>
+      Reducing shm size. (rjung)
+      </update>
+      <update>
+      Only log removing of old worker, when we actually do it. (rjung)
+      </update>
+      <fix>
+      <bug>37469</bug>: Fix shared memory close for forked childs.
+      The shared memory will be closed by the parent process. (mturk)
+      </fix>
+      <fix>
+      <bug>37332</bug>: Fix potential misuse of buffer length with
+      snprintf functions. (mturk)
+      </fix>
+      <fix>
+      <bug>38859</bug>: Protect mod_jk against buggy or malicious
+      AJP servers in the backend. Patch provided by Ruediger Pluem. (mturk)
+      </fix>
+      <fix>
+      <bug>38889</bug>: Use worker map sorting depending on the path
+      elements, to comply with Servlet spec. Patch provided by
+      Steve Revilak. (mturk)
+      </fix>
+      <update>
+      <bug>36138</bug>: Added Busyness lb method. Patch provided
+      by  Chris Lamprecht. (mturk)
+      </update>
+      <fix>
+      Fix pessimistic locking mode. The patch correctly handles the
+      burst load, by syncing the access to the shared memory data. (mturk)
+      </fix>
+      <fix>
+      <bug>38806</bug>: Reclycle worker even if it is disabled.
+      This fixes hot-standby workers in error state. (mturk)
+      </fix>
+      <fix>
+      <bug>37167</bug>: Allow building with BSD-ish like make. (mturk)
+      </fix>
+      <fix>
+      ISAPI plugin (isapi_redirect.dll) did not provide correct request data
+      for IIS
+ to include in the IIS log. (markt)
+      </fix>
+    </changelog>
+  </subsection>
+</section>
+<section name="Changes between 1.2.14 and 1.2.15">
+  <br />
+  <subsection name="Native">
+    <changelog>
+      <fix>
+      Fix AJP13 Cookie2 parsing. Cookie2 was always send as Cookie.
+      Patch provided by Andre Gebers. (mturk)
+      </fix>
+      <fix>
+      <bug>35862</bug>: NSAPI plugin attempts to read freed memory and attempts to
+      dereference a null pointer. Patch provided by Brian Kavanagh. (markt)
+      </fix>
+    </changelog>
+  </subsection>
+</section>
+<section name="Changes between 1.2.13 and 1.2.14">
+  <br />
+  <subsection name="Native">
+    <changelog>
+      <fix>
+        Fix lb for worker mpm's with cachesize set to lower number then
+        ThreadsPerChild is. If retries is set to value larger then 3 sleep for
+        100 ms on each attempt. This enables to tune the connection cache,
+        and serialize incoming connections instead returning busy if connection
+        count is larger then cachesize. (mturk)
+      </fix>
+      <fix>
+      <bug>36525</bug>: Solaris core dump. (mturk)
+      </fix>
+      <fix>
+      <bug>36102</bug>: Worker actions do not persist. (mturk)
+      </fix>
+      <fix>
+      <bug>35864</bug>: Status worker doesn't list workers.
+      Patch provided by Martin Goldhahn. (mturk)
+      </fix>
+      <fix>
+      <bug>35809</bug>: JkMountCopy don't work for Apache 2.0 Patch provided by
+       Christophe Dubach. (mturk)
+      </fix>
+      <fix>
+      <bug>35298</bug>: Multiple JK/ISAPI redirectors on a single IIS site are not supported
+       Patch provided by Tim Whittington. (mturk)
+      </fix>
+    </changelog>
+  </subsection>
+</section>
+<section name="Changes between 1.2.12 and 1.2.13">
+  <br />
+  <subsection name="Native">
+    <changelog>
+      <fix>
+      <bug>34397</bug>: Emergency was handled as Error. (jfclere)
+      </fix>
+      <fix>
+      <bug>34474</bug>: // in URL were not handled correctly with Apache-1.3. (jfclere)
+      </fix>
+      <fix>
+      Use 64 bits int for transferred/read bytes.
+      </fix>
+      <update>
+      Added JkOptions +FlushPackets used to optimize memory
+      usage when sending large data. (mturk)
+      </update>
+      <update>
+      Added lock directive for load balancer that allows more acurate
+      load balancing in case of burst load. (mturk)
+      </update>
+      <update>
+      Added worker.maintain directive to allow customizing default 10
+      second timeout. On busy servers this value needs to be set on
+      higher value. (mturk)
+      </update>
+      <fix>
+      Fix for NetWare compiler to deal with different types between AP13
+      and AP2 SDKs. (fuankg)
+      </fix>
+      <update>
+      Emit much more legible user.dmp crash analysis output for WIN32. (wrowe)
+      </update>
+     <fix>
+     <bug>34558</bug>: Fix first failover request. (mturk)
+     </fix>
+    </changelog>
+  </subsection>
+</section>
+
+<section name="Changes between 1.2.11 and 1.2.12">
+  <br />
+  <subsection name="Native">
+    <changelog>
+      <update>
+      Added ForwardLocallAddres JkOptions flag for passing local instead remote
+      address. Useful for remote addr valve. (mturk)
+      </update>
+     <fix>Fix that worker not used, when stopped flag is true. (pero)
+     </fix>
+      <update>
+      Add loadbalance default worker secret attribute to the documentation (pero)
+      </update>
+    </changelog>
+  </subsection>
+</section>
+
+<section name="Changes between 1.2.10 and 1.2.11">
+  <br />
+  <subsection name="Native">
+    <changelog>
+      <fix>Backport SC_M_JK_STORED from JK2 for passing arbitrary
+      methods instead failing the request. (mturk)
+      </fix>
+      <fix>Added missing SEARCH and ACL http methods. (mturk)
+      </fix>
+      <update>
+      Add worker secret attribute to the documentation (pero)
+      </update>
+      <update>
+      Add a stopped flag to worker configuration. Set flag True and
+      complete traffic to worker is stopped.
+      Also update the Ant JkStatusUpdateTask at Tomcat 5.5.10 release.
+      Only usefull in a replicated session cluster.(pero)
+      </update>
+      <update>Added worker maintain function that will maintain all
+      the workers instead just the current one. This enables to recycle
+      the connections on all workers. (mturk)
+      </update>
+      <update>Use shutdown when recycling connections instead hard
+      breaking the socket. (mturk)
+      </update>
+      <update>Add unique directives checking. The directives if
+      unique are now overwritten instead concatenated. (mturk)
+      </update>
+      <update>Allow multiple worker.list directives. (mturk)
+      </update>
+      <fix>
+      <bug>34577</bug>: For IIS log original request instead loging
+      the request for ISAPI extension. (mturk)
+      </fix>
+      <fix>
+      <bug>34558</bug>: Make sure the returned status codes are the same
+      for ajp and lb workers. (mturk)
+      </fix>
+      <fix>
+      <bug>34423</bug>: Use APR_USE_FLOCK_SERIALIZE for setting log lock
+      on platforms like FreeBSD. Patch provided by Allan Saddi. (mturk)
+      </fix>
+      <fix>
+      <bug>33843</bug>: Fix obtaining LDFLAGS that were used for building
+      Apache HTTPD. Patch provided by Beat Kneubuehl. (mturk)
+      </fix>
+      <fix>
+      <bug>34358</bug>: Enable load balancer method configuration. (glenn)
+      </fix>
+      <fix>
+      <bug>34357</bug>: In some situations Apache 2 mod_jk could segfault
+      when the JkAutoAlias directive is used. (glenn)
+      </fix>
+      <update>
+        Add --enable-prefork to the documentation (pero)
+      </update>
+    </changelog>
+  </subsection>
+</section>
+<section name="Changes between 1.2.9 and 1.2.10">
+  <br />
+  <subsection name="Native">
+    <changelog>
+      <update>Set default shared memory to 64K instead 1M. (mturk)
+      </update>
+      <fix>Do not mark the worker in error state if headers are
+      larger then AJP13 limit. (mturk)
+      </fix>
+      <update>
+      On Series you should use the latest PTF for Apache 2.0
+      (which is now 2.0.52) and ad minima SI17402/SI17061 or cumulative
+      including them. (hgomez)
+      </update>
+      <update>
+      Change the xml status format to xml attribute syntax (pero)
+      </update>
+      <fix>
+      <bug>33248</bug>: Fix builds where apxs defines multiple
+      directories for APR includes. (mturk)
+      </fix>
+      <fix>
+      <bug>32696</bug>: Return 404 instead 403 when WEB-INF is requested
+      to comply with Servlet spec. (mturk)
+      </fix>
+      <update>Added ANT task for managing jkstatus. (pero)
+      </update>
+      <update>
+      If socket_timeout is set, check if socket is alive before
+      sending any request to Tomcat. (mturk)
+      </update>
+      <update>
+      Added JkMountFile for Apache web servers. This file can contain
+      uri mappings in the form (/url=worker), and is checked for
+      updates at regular 60 second interval. (mturk)
+      </update>
+      <update>
+      Added status worker for managing worker runtime data using
+      web page. (mturk)
+      </update>
+      <update>
+      Added load balancer method directive that is used for setting
+      the algorithm used for balancing workers. Method can be either
+      Request (default) or Traffic. (mturk)
+      </update>
+      <update>
+      Added shared memory to allow dynamic configuration. Shared memory
+      is needed only for unix platform and web servers having multiple
+      child processes. For Apache web server two new directives has been
+      added (JkShmFile and JkShmSize). (mturk)
+      </update>
+      <update>
+      Added textupdate mode to status worker to handle remote updates
+      from ant tasks.(pero)
+      </update>
+      <fix>
+      <bug>33562</bug>: Fix Reply_timeout when recovery_options
+      is larger than 1. Patch provided by Takashi Satou. (mturk)
+      </fix>
+      <fix>
+      <bug>33308</bug>: Fix segfaults when ForwardDirectories is enabled
+        with Apache 1.3
+      </fix>
+    </changelog>
+  </subsection>
+</section>
+<section name="Changes between 1.2.7 and 1.2.8">
+  <br />
+  <subsection name="Native">
+    <changelog>
+      <update>
+      Allow anyone to debug and diagnose stack dumps using windbg or any
+      other debugging tool, and (if they add the .pdb files to their
+      installation) to make sense of dr watson logs.
+      Patch provided by William A. Rowe (wrowe)
+      </update>
+      <fix>
+      Fix in_addr_t usage by using the real struct ignoring typedef.
+      Patch provided by William A. Rowe (wrowe)
+      </fix>
+      <fix>
+      Fix url rewriting by restoring the in place uri from which the
+      jsessionid was removed. (mturk)
+      </fix>
+      <update>
+      Make load balancer algorithm thread safe by introducing mutex
+      to the load balancer worker. (mturk)
+      </update>
+      <fix>
+      Fix sending error pages for IIS to client by adding Content-Type header
+      using correct api function call. (mturk)
+      </fix>
+      <fix>
+      <bug>32696</bug>: Prevent IIS from crushing when web-inf url was requested. (mturk)
+      </fix>
+      <update>
+      Use default cachesize for servers that support discovering the number of
+      threads per child process. (mturk).
+      </update>
+      <fix>
+      Fix Apache content-length header parsing using case insensitive compare. (billbarker)
+      </fix>
+      <fix>
+      Fix parsing AJP headers using case insensitive compare. (mturk)
+      </fix>
+      <fix>
+      Use infinite socket timeout if socket_timeout is set to zero or less then zero. (mturk)
+      </fix>
+      <update>
+      Change <b>balanced_workers</b> to <b>balance_workers</b> but keep
+      backward compatibility preserving the old directive. (mturk).
+      </update>
+      <fix>
+      Fix ajp initialization for workers with cache_size set to zero. (mturk)
+      </fix>
+      <update>
+      <bug>32317</bug>: Making mod_jk replication aware (Clustering Support).
+      Patch provided by Rainer Jung. (mturk).
+      </update>
+      <fix>
+      <bug>31132</bug>: Core dump when JkLogFile is missing from conf. (mturk)
+      </fix>
+    </changelog>
+  </subsection>
+</section>
+<section name="Changes between 1.2.6 and 1.2.7">
+  <br />
+  <subsection name="Native">
+    <changelog>
+      <update>
+      Added new property named recover_time that can be used to change the
+      default 60 second recover time. (mturk)
+      </update>
+      <update>
+      Added custom retries for worker, so we don't depend on default setting.
+      If set to a number grater then 3, it will sleep for 100ms on retry greater
+      then 3 and then try again. (mturk)
+      </update>
+      <update>
+      Added JkWorkerProperty directive that enables omiting workers.properties file.
+      For example: JkWorkerProperty worker.ajp13a.port=8009. (mturk)
+      </update>
+      <fix>
+      Check all JSESSIONID cookies for a valid jvmRoute. If you have multiple Tomcats
+      with overlapping domains, then you can get multiple cookies without a defined order.
+      This will route correctly as long as the different domains don't have any
+      Tomcats in common. (billbarker)
+      </fix>
+      <update>
+      Added JkUnMount directive for negative mappings that works as opposite to JkMount directives.
+      It is used for blocking of particular URL or content type. (mturk)
+      </update>
+      <update>
+      Added wildchar match uri mappings. One can now use JkMount to
+      map /app/*/servlet/* or /app?/*/*.jsp. (mturk)
+      </update>
+      <update>
+      Rewrite the logging by adding Trace options. (mturk)
+      </update>
+      <update>
+      Added socket_timeout property that sets the timeout
+      for the socket itself. (mturk)
+      </update>
+      <fix>
+      Changed socket_timeout property to recycle_timeout. This better
+      explains what the directive actually does. (mturk)
+      </fix>
+      <fix>
+        Changed the load balancer algorithm.
+        The idea behind this new scheduler is the following:
+        lbfactor is <i>how much we expect this worker to work</i>,
+        or <i>the worker's work quota</i>.
+        lbstatus is <i>how urgent this worker has to work to fulfill its quota
+        of work</i>. We distribute each worker's work quota to the worker, and then look
+        which of them needs to work most urgently (biggest lbstatus).  This
+        worker is then selected for work, and its lbstatus reduced by the
+        total work quota we distributed to all workers.  Thus the sum of all
+        lbstatus does not change.(*)
+        If some workers are disabled, the others will
+        still be scheduled correctly. (mturk)
+      </fix>
+    </changelog>
+  </subsection>
+</section>
+<section name="JK 2">
+<p>JK2 has been put in maintainer mode and no further development will take place.
+The reason for shutting down JK2 development was the lack of developers interest.
+Other reason was lack of users interest in adopting JK2, caused by configuration
+complexity when compared to JK.
+</p>
+</section>
+
+</body>
+</document>
diff --git a/connectors/jk/xdocs/miscellaneous/doccontrib.xml b/connectors/jk/xdocs/miscellaneous/doccontrib.xml
new file mode 100644
index 0000000..a34be91
--- /dev/null
+++ b/connectors/jk/xdocs/miscellaneous/doccontrib.xml
@@ -0,0 +1,339 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="doccontrib.html">
+
+  &project;
+<copyright>
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT 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>
+<body>
+<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 syntax 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 subversion</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://ant.apache.org/">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>SVN_HOME\tomcat\connectors\trunk\jk\xdocs</b> directory.  In the 
+<b>build.xml</b> file there is a target named <b>all</b> 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://svn.apache.org/repos/asf/tomcat/connectors/">tomcat-connectors</a>
+from the subversion repository.  If you'll 
+be editing from a windows platform you will need a windows subversion client.  There 
+are several available.  I like <a href="http://tortoisesvn.tigris.org/">turtoiseSVN</a>.  
+Unix users should install the subversion 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.  For simplicity we will call this 
+your <b>SVN_HOME</b>.  Mine is located in C:\build.
+</p>
+<p>
+    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\>svn checkout http://svn.apache.org/repos/asf/tomcat/connectors/
+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 is over.  From now on, to update your 
+sources all you have to do is cd into any directory in your repository and run 
+the <b>svn update</b> command.
+    <screen>
+<note>    To update your xdocs directory simply cd into the xdocs directory 
+and:</note>
+<read>C:\build\tomcat-connectors\jk\>cd xdocs</read>
+<read>C:\build\tomcat-connectors\jk\xdocs\>svn update</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 and then into the xdocs directory so
+that <b>Ant</b> can see the 
+<b>build.xml</b> file. Then from a command prompt, run the following:
+<screen>
+<read> </read>
+<read>C:\build\tomcat-connectors>cd jk</read>
+<read>C:\build\tomcat-connectors\jk>cd xdocs</read>
+<read>C:\build\tomcat-connectors\jk\xdocs>ant all</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\tomcat-connectors\jk\build\docs\news\printer></read>
+<read>[style] Processing C:\build\tomcat-connectors\jk\xdocs\news\20041100.xml 
+to</read>
+<read>C:\build\tomcat-connectors\jk\build\docs\news/20041100.html</read>
+<read>[style] Loading stylesheet C:\build\tomcat-connectors\jk\xdocs\style.xsl</read>
+<read>[style] Processing C:\build\tomcat-connectors\jk\xdocs\news\20050101.xml 
+to</read>
+<read>C:\build\tomcat-connectors\jk\build\docs\news/20050101.html</read>
+<read>[style] Processing C:\build\tomcat-connectors\jk\xdocs\news\20060101.xml 
+to</read>
+<read>C:\build\tomcat-connectors\jk\build\docs\news/20060101.html</read>
+<read>[style] Transforming into C:\build\tomcat-connectors\jk\build\docs></read>
+<read>[style] Processing C:\build\tomcat-connectors\jk\xdocs\index.xml 
+to</read>
+<read>C:\build\tomcat-connectors\jk\build\docs\index.html</read>
+<read>[style] Loading stylesheet C:\build\tomcat-connectors\jk\xdocs\style.xsl</read>
+<read> </read>
+<read>BUILD SUCCESSFUL</read>
+<read>Total time: 10 seconds</read>
+<read>C:\build\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>SVN_HOME\tomcat-connectors\jk\build\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>SVN_HOME\tomcat-connectors\jk\xdocs</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 all</read>
+<read> </read>
+</screen>
+</p>
+<p>
+    My second window I call my <b>edit</b> window and I keep that one in the 
+<b>SVN_HOME\tomcat-connectors\jk\xdocs</b> directory where I'm doing my
+edits, diffs and svn 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\tomcat-connectors\jk>cd xdocs</read>
+<read>C:\build\tomcat-connectors\jk\xdocs></read>
+<read>C:\build\tomcat-connectors\jk\xdocs>svn update</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 something 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>SVN_HOME\tomcat-connectors\jk\xdocs</b> directory run:
+<screen>
+<read> </read>
+<read>C:\build\tomcat-connectors\jk\xdocs> 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\tomcat-connectors\jk\xdocs>ant all</read>
+<read> </read>
+</screen>
+</p>
+<p>
+    Use your browser to view the edits you just made, they will be in the 
+<b>SVN_HOME\tomcat-connectors\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>svn update</b> command.  For example, 
+to produce a 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\tomcat-connectors\jk\xdocs\>svn diff 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://issues.apache.org/bugzilla/">http://issues.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://issues.apache.org/bugzilla/bugwritinghelp.html">http://issues.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="Subversion Basics">
+<p>
+    After you have checked out the sources the first time it is much easier to 
+use subversion.  You can cd into any directory of the repository and run <b>svn 
+update</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>svn diff</b> to generate patches for submission.  
+Again cd into the directory containing the file you are editing and run <b>svn 
+diff name_of_the_file_you_edited > patch.txt</b> to generate a patch for 
+submission.
+</p>
+<p>
+    Pay attention to the terminal window during the update.
+</p>
+<p>
+    Lines begining with a <b>A</b> indicate files that have been added.
+</p>
+<p>
+    Lines begining with a <b>D</b> indicate files that have been deleted.
+</p>
+<p>
+    Lines begining with a <b>U</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>G</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="Updating Web site">
+<p>
+    Only Committers are able to update the web site (http://tomcat.apache.org/connectors-doc/).
+    To do it:
+    <ul>
+    <li>Connect to people.apache.org.</li>
+    <li>umask 002</li>
+    <li>Copy the changed files to /www/tomcat.apache.org/connectors-doc/.</li>
+    <li>or use ant from a checkout tomcat/connectors/jk/xdocs repository:<br />
+        ant -Dbuild.dir=/www/tomcat.apache.org -Ddist.name=connectors-doc 
+    </li>
+    <li>The changes need around 4 hours to be synced to tomcat.apache.org.</li>
+    </ul>
+</p>
+</section>
+<section name="Guides and Resources">
+<p>
+    A little help to get you started 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://issues.apache.org/bugzilla/">Bugzilla</a>
+</li>
+<li>
+<a href="http://issues.apache.org/bugzilla/bugwritinghelp.html">Bugzilla Bug 
+Writing Guide</a>
+</li>
+<li>
+<a href="http://ant.apache.org/">Ant</a>
+</li>
+<li>
+<a href="http://subversion.tigris.org/">Subversion Home</a>
+</li>
+<li>
+<a href="http://svn.apache.org/repos/asf/tomcat/connectors/jk/xdocs/">JK Docs repository</a>
+</li>
+</ul>
+</section>
+</body>
+</document>
diff --git a/connectors/jk/xdocs/miscellaneous/faq.xml b/connectors/jk/xdocs/miscellaneous/faq.xml
new file mode 100644
index 0000000..fd9708b
--- /dev/null
+++ b/connectors/jk/xdocs/miscellaneous/faq.xml
@@ -0,0 +1,322 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="faq.html">
+
+  &project;
+<copyright>
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT 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>
+<body>
+<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 Tomcat web site devoted to the
+<a href="http://tomcat.apache.org/connectors-doc/">
+Apache Tomcat Connectors Project</a>
+For additional help, the best resource is the Tomcat Users Discussion list.  
+You should start by searching
+<a href="http://mail-archives.apache.org/mod_mbox/tomcat-users/">
+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://tomcat.apache.org/lists.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>tomcat-connectors</b> repository, 
+the source and the binaries for JK can be downloaded from a mirror at the
+<a href="http://tomcat.apache.org/download-connectors.cgi">
+Tomcat Connectors (mod_jk, mod_jk2) Downloads</a> page.
+</p>
+</subsection>
+
+<subsection name="What's the difference between JK and mod_jk ?">
+<br />
+<p>
+<b>JK</b> is a project covering web-servers to Tomcat connectors,
+whereas <b>mod_jk</b> is the <a href="../webserver_howto/apache.html">Apache module</a> developed in JK.
+</p>
+
+<p>
+<a href="../webserver_howto/iis.html">IIS webserver</a>support is implemented on JK, using a redirector
+called <b>isapi redirector</b>.
+</p>
+
+<p>
+<a href="../webserver_howto/nes.html">Netscape/SunONE/Sun 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="../generic_howto/quick.html">For the impatient</a>
+</li>
+
+<li>
+<a href="../webserver_howto/apache.html">Apache and JK</a>
+</li>
+
+<li>
+<a href="../webserver_howto/iis.html">IIS and JK</a>
+</li>
+
+<li>
+<a href="../webserver_howto/nes.html">Netscape/SunONE/Sun and JK</a>
+</li>
+
+<li>
+<a href="../generic_howto/workers.html">Workers configuration</a>
+</li>
+</ul>
+
+<p> 
+For more detailed information, have a look at the Reference Guide.
+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="../ajp/ajpv13a.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 <strong>ajp12 is deprecated</strong>.
+</p>
+<p> 
+Also ajp13 is supported by all Apache Tomcat including 3.2.x , 3.3.x, 4.0.x, 4.1.x, 5.0.x, 5.5.x
+and the new tomcat 6. 
+</p>
+
+<p>
+Others Servlet engines like <b>jetty</b> have support for Ajp13.
+</p>
+</subsection>
+
+<subsection name="I've got a firewall between my web server and Tomcat which drops ajp13 connections after some time">
+<p>
+Ajp13 uses persistant connections where the traffic could be null if there is no request to be sent to Tomcat. 
+Firewalls use to drop inactive connections and will make your web server 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="../generic_howto/workers.html">Workers HowTo</a> and
+<a href="../reference/workers.html">workers.properties reference</a>.
+If nothing else helps, you can try <b>JkOptions +DisableReuse</b>, but this will have strong performance implications.
+</p>
+</subsection>
+
+<subsection name="Under heavy load, I've got many threads in Tomcat even if my Apache Web Server handles much of the load">
+<p>
+Under heavy load, Apache Web Server creates many children to handle the load,
+which will in turn create many connections 
+to Tomcat to forward the requests they should handle. 
+Apache Web Server will normally kill the children/threads when the load decreases.
+But if the load is still there and even if only Apache handles the requests,
+ie static contents, the children are kept and with them all the ajp13 connections, 
+even if they are no more used. 
+</p>
+<p>
+To close connections after some time of inactivity you can use <b>connection_pool_timeout</b>,
+for more informations refer to <a href="../reference/workers.html">workers.properties reference</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 handles the network failure. 
+But with very ancient releases of mod_jk, you may have to restart Apache as well.
+</p>
+</subsection>
+
+<subsection name="Why do there exist two files mod_jk.so (-eapi ad -noeapi) in download directories for Apache 1.3?">
+<p>
+Many versions of Apache use a modified API, known at Extended API, developed for use with the
+<a href="http://www.modssl.org">mod_ssl module</a>. Starting with Apache 2.0 there is no more difference.
+</p>
+
+<p>
+For example, Apache 1.3 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="Does it work for Apache 2.2?">
+<p>
+mod_jk works well with Apache 2.2. You need a binary module compiled for version 2.2 of the Apache web server.
+A binary compiled for version 2.0 will not work.
+</p>
+<p>
+Important parts of the functionality of mod_jk have been reimplemented as Apache httpd modules mod_proxy_ajp
+and mod_proxy_balancer. These are part of the standard distributoin of Apache 2.2. The new modules do not contain
+all features of mod_jk, but you get them automatically with every Apache 2.2.
+</p>
+</subsection>
+
+<subsection name="JNI doesn't work with Apache 1.3">
+<p>
+JNI support requires 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>
+
+<source>
+  # Add pthread to Apache in httpd.conf
+  LoadModule "/usr/lib/libpthreads.so"
+</source>
+
+<p>
+Also keep in mind that JNI is suited for multi-threaded servers and you should consider upgrading 
+to Apache 2.x 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 already 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 case the make processing doesn't work as expected, you should use a GNU make <b>gmake</b>.
+</p>
+</subsection>
+
+</section>
+</body>
+</document>
diff --git a/connectors/jk/xdocs/miscellaneous/jkstatustasks.xml b/connectors/jk/xdocs/miscellaneous/jkstatustasks.xml
new file mode 100644
index 0000000..ab0acb3
--- /dev/null
+++ b/connectors/jk/xdocs/miscellaneous/jkstatustasks.xml
@@ -0,0 +1,216 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="jkstatustasks.html">
+ 
+  &project;
+
+<copyright>
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT 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>Status Worker Ant Tasks</title>
+<author>Peter Rossbach</author>
+<date>$Date$</date>
+</properties>
+
+<body>
+
+<section name="Introduction">
+<p>Since version 1.2.19 the JK release contains additional ant tasks.
+They can be used to manage the JK web server plugins via the special status worker.
+</p>
+</section>
+
+<section name="Manage JK with remote Ant Tasks">
+
+<subsection name="Simple antlib integration">
+<p>
+<source>
+&lt;?xml version="1.0" encoding="UTF-8"?&gt;
+
+&lt;project name="modjk-status" 
+         xmlns:jk="urn:org-apache-jk-status"
+         default="status" basedir="."&gt;
+
+	&lt;property name="profile" value=""/&gt;
+	&lt;property file="jkstatus${profile}.properties"/&gt;
+	&lt;property file="jkstatus.properties.default"/&gt;
+
+    &lt;path id="jkstatus.classpath"&gt;
+      &lt;pathelement location="${catalina.home}/bin/commons-logging-api.jar"/&gt;
+      &lt;pathelement location="${catalina.home}/server/lib/catalina-ant.jar"/&gt;
+      &lt;pathelement location="../dist/tomcat-jkstatus-ant.jar"/&gt;
+      &lt;pathelement location="${catalina.home}/server/lib/tomcat-util.jar"/&gt;
+    &lt;/path&gt;
+
+    &lt;typedef resource="org/apache/jk/status/antlib.xml"       
+           uri="urn:org-apache-jk-status" classpathref="jkstatus.classpath"/&gt; 
+           
+    &lt;target name="status" &gt;       
+ 	    &lt;jk:status url="${jkstatus.url}" 
+	                username="${jkstatus.username}"
+	                password="${jkstatus.password}"
+	                resultproperty="worker"
+	      	        echo="off"
+	                failOnError="off"/&gt;
+	    &lt;echoproperties prefix="worker" /&gt;
+    &lt;/target&gt;
+&lt;/project&gt;    
+</source>
+</p>
+</subsection>
+
+<subsection name="Test Result">
+<p>
+<source>
+[echoproperties] #Ant properties
+[echoproperties] #Sun Dec 10 20:40:21 CET 2006
+[echoproperties] worker.node01.lbmult=1
+[echoproperties] worker.loadbalancer.lock=Optimistic
+[echoproperties] worker.node02.transferred=0
+[echoproperties] worker.loadbalancer.sticky_session=false
+[echoproperties] worker.node01.distance=0
+[echoproperties] worker.node01.client_errors=0
+[echoproperties] worker.node02.lbmult=1
+[echoproperties] worker.node01.port=7309
+[echoproperties] worker.node01.elected=0
+[echoproperties] worker.loadbalancer.good=2
+[echoproperties] worker.loadbalancer.method=Sessions
+[echoproperties] worker.server.port=2090
+[echoproperties] worker.loadbalancer.map.2.type=Wildchar
+[echoproperties] worker.node02.route=node02
+[echoproperties] worker.node01.route=node01
+[echoproperties] worker.node01.lbvalue=0
+[echoproperties] worker.node01.lbfactor=1
+[echoproperties] worker.node01.max_busy=0
+[echoproperties] worker.node01.busy=0
+[echoproperties] worker.node01.redirect=
+[echoproperties] worker.node02.distance=0
+[echoproperties] worker.loadbalancer.name=loadbalancer
+[echoproperties] worker.loadbalancer.sticky_session_force=false
+[echoproperties] worker.node02.state=N/A
+[echoproperties] worker.node01.state=N/A
+[echoproperties] worker.node01.transferred=0
+[echoproperties] worker.loadbalancer.map.length=2
+[echoproperties] worker.node01.type=ajp13
+[echoproperties] worker.node01.address=127.0.0.1\:7309
+[echoproperties] worker.result.type=OK
+[echoproperties] worker.loadbalancer.member_count=2
+[echoproperties] worker.loadbalancer.map_count=2
+[echoproperties] worker.loadbalancer.mtime_to_maintenance_min=12
+[echoproperties] worker.loadbalancer.mtime_to_maintenance_max=75
+[echoproperties] worker.node02.lbfactor=1
+[echoproperties] worker.node02.max_busy=0
+[echoproperties] worker.jk_version=mod_jk/1.2.21-dev
+[echoproperties] worker.loadbalancer.bad=0
+[echoproperties] worker.node02.redirect=
+[echoproperties] worker.node01.host=localhost
+[echoproperties] worker.node02.activation=ACT
+[echoproperties] worker.loadbalancer.map.1.source=JkMount
+[echoproperties] worker.loadbalancer.retries=2
+[echoproperties] worker.node02.elected=0
+[echoproperties] worker.loadbalancer.map.2.source=JkMount
+[echoproperties] worker.node02.port=7409
+[echoproperties] worker.loadbalancer.length=2
+[echoproperties] worker.node02.lbvalue=0
+[echoproperties] worker.loadbalancer.degraded=0
+[echoproperties] worker.loadbalancer.map.1.type=Wildchar
+[echoproperties] worker.loadbalancer.map.2.uri=/myapps*
+[echoproperties] worker.node02.client_errors=0
+[echoproperties] worker.length=1
+[echoproperties] worker.node01.domain=d20
+[echoproperties] worker.loadbalancer.recover_time=60
+[echoproperties] worker.server.name=localhost
+[echoproperties] worker.node02.domain=
+[echoproperties] worker.result.message=Action finished
+[echoproperties] worker.node02.busy=0
+[echoproperties] worker.node01.readed=0
+[echoproperties] worker.node01.errors=0
+[echoproperties] worker.node02.address=127.0.0.1\:7409
+[echoproperties] worker.node02.readed=0
+[echoproperties] worker.loadbalancer.busy=0
+[echoproperties] worker.web_server=Apache/2.0.59 (Unix) mod_jk/1.2.21-dev
+[echoproperties] worker.node02.errors=0
+[echoproperties] worker.node02.type=ajp13
+[echoproperties] worker.loadbalancer.map.1.uri=/ClusterTest*
+[echoproperties] worker.node01.activation=ACT
+[echoproperties] worker.loadbalancer.max_busy=0
+[echoproperties] worker.loadbalancer.type=lb
+[echoproperties] worker.node02.host=localhost
+</source>
+</p>
+</subsection>
+
+<subsection name="Update Load Balancer">
+<p>
+<source>
+     &lt;target name="updatelb" &gt;       
+ 	    &lt;jk:updateloadbalancer url="${jkstatus.url}" 
+	                username="${jkstatus.username}"
+	                password="${jkstatus.password}"
+	                loadbalancer="loadbalancer"
+	                method="Busyness"
+	                retries="2"
+	                recoverWaitTime="60"
+	                lock="Optimistic"
+	                forceStickySession="false"
+	                stickySession="false"/&gt;
+     &lt;/target&gt;
+</source>
+</p>
+</subsection>
+
+<subsection name="Update Worker">
+<p>
+<source>
+     &lt;target name="updatew" &gt;       
+ 	    &lt;jk:updateworker url="${jkstatus.url}" 
+	                username="${jkstatus.username}"
+	                password="${jkstatus.password}"
+	                loadbalancer="loadbalancer"
+	      	        worker="node01"
+	                lbfactor="2"
+	                activation="Active"
+	                redirect=""
+	                domain=""
+	                route="node01"
+	                distance="0"/&gt;
+     &lt;/target&gt;
+</source>
+</p>
+</subsection>
+
+<subsection name="Reset Worker">
+<p>
+<source>
+     &lt;target name="reset" &gt;       
+ 	    &lt;jk:reset url="${jkstatus.url}" 
+	                username="${jkstatus.username}"
+	                password="${jkstatus.password}"
+	                loadbalancer="loadbalancer"
+	      	        worker="node01"
+	                /&gt;
+     &lt;/target&gt;
+</source>
+</p>
+</subsection>
+
+</section>
+</body>
+</document>
diff --git a/connectors/jk/xdocs/miscellaneous/project.xml b/connectors/jk/xdocs/miscellaneous/project.xml
new file mode 100644
index 0000000..2d4d173
--- /dev/null
+++ b/connectors/jk/xdocs/miscellaneous/project.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<project name="Apache Tomcat Connector Documentation - Miscellaneous Documentation"
+        href="http://tomcat.apache.org/">
+
+    <title>The Apache Tomcat Connector - Miscellaneous Documentation</title>
+
+    <logo href="/images/tomcat.gif">
+      The Apache Tomcat Connector - Miscellaneous Documentation
+    </logo>
+<body>
+
+    <menu name="Links">
+        <item name="Docs Home"                  href="../index.html"/>
+    </menu>
+
+    <menu name="Reference Guide">
+        <item name="workers.properties"         href="../reference/workers.html"/>
+        <item name="uriworkermap.properties"    href="../reference/uriworkermap.html"/>
+        <item name="Status Worker"              href="../reference/status.html"/>
+        <item name="Apache"                     href="../reference/apache.html"/>
+        <item name="IIS"                        href="../reference/iis.html"/>
+    </menu>
+
+    <menu name="Generic HowTo">
+        <item name="For the impatient"          href="../generic_howto/quick.html"/>
+        <item name="All about workers"          href="../generic_howto/workers.html"/>
+        <item name="Load Balancing"             href="../generic_howto/loadbalancers.html"/>
+    </menu>
+
+    <menu name="Webserver HowTo">
+        <item name="Apache"                     href="../webserver_howto/apache.html"/>
+        <item name="IIS"                        href="../webserver_howto/iis.html"/>
+        <item name="Netscape/SunOne/Sun"        href="../webserver_howto/nes.html"/>
+    </menu>
+
+    <menu name="AJP Protocol Reference">
+        <item name="AJPv13"                     href="../ajp/ajpv13a.html"/>
+        <item name="AJPv13 Extension Proposal"  href="../ajp/ajpv13ext.html"/>
+    </menu>
+
+    <menu name="Miscellaneous Documentation">
+        <item name="Frequently asked questions" href="../miscellaneous/faq.html"/>
+        <item name="Changelog"                  href="../miscellaneous/changelog.html"/>
+        <item name="Current Native:JK bugs"     href="http://issues.apache.org/bugzilla/buglist.cgi?query_format=advanced&amp;short_desc_type=allwordssubstr&amp;short_desc=&amp;product=Tomcat+5&amp;component=Native%3AJK&amp;long_desc_type=substring&amp;long_desc=&amp;bug_file_loc_type=allwordssubstr&amp;bug_file_loc=&amp;keywords_type=allwords&amp;keywords=&amp;bug_status=NEW&amp;bug_status=ASSIGNED&amp;bug_status=REOPENED&amp;emailassigned_to1=1&amp;emailtype1=substring&amp;email1=&amp;emailassigned_to2=1&amp;emailreporter2=1&amp;emailcc2=1&amp;emailtype2=substring&amp;email2=&amp;bugidtype=include&amp;bug_id=&amp;votes=&amp;chfieldfrom=&amp;chfieldto=Now&amp;chfieldvalue=&amp;cmdtype=doit&amp;order=Reuse+same+sort+as+last+time&amp;field0-0-0=noop&amp;type0-0-0=noop&amp;value0-0-0="/>
+        <item name="Contribute documentation"   href="../miscellaneous/doccontrib.html"/>
+        <item name="JK Status Ant Tasks"        href="../miscellaneous/jkstatustasks.html"/>
+        <item name="Reporting Tools"            href="../miscellaneous/reporttools.html"/>
+        <item name="Old JK/JK2 documentation"   href="http://tomcat.apache.org/connectors-doc-archive/jk2/index.html"/>
+    </menu>
+
+    <menu name="News">
+        <item name="2007"                       href="../news/20070301.html"/>
+        <item name="2006"                       href="../news/20060101.html"/>
+        <item name="2005"                       href="../news/20050101.html"/>
+        <item name="2004"                       href="../news/20041100.html"/>
+    </menu>
+
+</body>
+</project>
diff --git a/connectors/jk/xdocs/miscellaneous/reporttools.xml b/connectors/jk/xdocs/miscellaneous/reporttools.xml
new file mode 100644
index 0000000..d33f35c
--- /dev/null
+++ b/connectors/jk/xdocs/miscellaneous/reporttools.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="reporttools.html">
+ 
+  &project;
+<copyright>
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT 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>Reporting Tools</title>
+<author>Glenn Nielsen</author>
+<date>$Date$</date>
+</properties>
+<body>
+<section name="Reporting Tools">
+<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>
+
+</body>
+</document>
diff --git a/connectors/jk/xdocs/news/20041100.xml b/connectors/jk/xdocs/news/20041100.xml
new file mode 100644
index 0000000..4961cdf
--- /dev/null
+++ b/connectors/jk/xdocs/news/20041100.xml
@@ -0,0 +1,145 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="20041100.html">
+ 
+  &project;
+ 
+  <properties>
+    <author email="general.AT.tomcat.DOT.apache.DOT.org">Apache Jakarta Project</author>
+    <title>2004 News and Status</title>
+  </properties>
+
+<body>
+
+<section name="2004 News &amp; Status">
+<br />
+<!--
+<a name="2004****.1">
+<h3></h3>
+</a>
+<hr size="1" noshade="noshade" />
+-->
+<a name="20041224.1"> 
+<h3>17 December - JK-1.2.8 released</h3>
+</a> 
+<p>The Apache Jakarta Tomcat team is proud to announce the immediate availability
+of Jakarta Tomcat Connectors 1.2.8.
+</p>
+<p>
+ Please see the <a href="../miscellaneous/changelog.html">ChangeLog</a> for a full list of changes.
+</p>
+<p>If you find any bugs during testing this release, please fill in the
+<a href="http://issues.apache.org/bugzilla/enter_bug.cgi?product=Tomcat%205">Bugzilla</a>
+Bug Report. When entering bug select <b>Native:JK</b> Component.
+</p>
+<hr size="1" noshade="noshade" />
+<a name="20041218.1"> 
+<h3>17 December - JK-1.2.8-rc-1 released</h3>
+</a> 
+<p>The Apache Jakarta Tomcat team is proud to announce the immediate availability
+of Jakarta Tomcat Connectors 1.2.8-rc-1 (Relase Canditate 1).
+</p>
+<p>
+We expect it to be ratified as a Stable release when the vote takes place
+in the next week.
+</p>
+<p>
+ Please see the <a href="../miscellaneous/changelog.html">ChangeLog</a> for a full list of changes.
+</p>
+<p>If you find any bugs during testing this release, please fill in the
+<a href="http://issues.apache.org/bugzilla/enter_bug.cgi?product=Tomcat%205">Bugzilla</a>
+Bug Report. When entering bug select <b>Native:JK</b> Component.
+</p>
+<hr size="1" noshade="noshade" />
+<a name="20041213.1"> 
+<h3>13 December - JK-1.2.7-beta-3 released</h3>
+</a> 
+<p>The Apache Jakarta Tomcat team is proud to announce the immediate availability
+of Jakarta Tomcat Connectors 1.2.7-beta-3. The release contains a fix to few configuration
+problems detected with JK-1.2.7-beta-2 version.
+</p>
+<p>
+We expect it to be ratified as a Stable release when the vote takes place
+in the next week.
+</p>
+<p>
+ Please see the <a href="../miscellaneous/changelog.html">ChangeLog</a> for a full list of changes.
+</p>
+<p>If you find any bugs during testing this release, please fill in the
+<a href="http://issues.apache.org/bugzilla/enter_bug.cgi?product=Tomcat%205">Bugzilla</a>
+Bug Report. When entering bug select <b>Native:JK</b> Component.
+</p>
+<hr size="1" noshade="noshade" />
+<a name="20041207.1"> 
+<h3>7 December - JK-1.2.7-beta-2 released</h3>
+</a> 
+<p>The Apache Jakarta Tomcat team is proud to announce the immediate availability
+of Jakarta Tomcat Connectors 1.2.7-beta-2. The release contains a fix to few compilation
+problems detected with JK-1.2.7-beta version. This release also introduces a new
+<b>domain</b> concept clustering support. See <bug>32317</bug> for details.
+</p>
+<p>
+We expect it to be ratified as a Stable release when the vote takes place
+in the next two weeks.
+</p>
+<p>
+ Please see the <a href="../miscellaneous/changelog.html">ChangeLog</a> for a full list of changes.
+</p>
+<p>If you find any bugs during testing this release, please fill in the
+<a href="http://issues.apache.org/bugzilla/enter_bug.cgi?product=Tomcat%205">Bugzilla</a>
+Bug Report. When entering bug select <b>Native:JK</b> Component.
+</p>
+<hr size="1" noshade="noshade" />
+
+<a name="20041130.1"> 
+<h3>30 November - JK-1.2.7-beta released</h3>
+</a> 
+<p>The Apache Jakarta Tomcat team is proud to announce the immediate availability
+of Jakarta Tomcat Connectors 1.2.7-beta. The release contains a significant number
+of bug fixes and new features. 
+</p>
+<p>
+We expect it to be ratified as a Stable release when the vote takes place
+in the next two weeks.
+</p>
+<p>
+ Please see the <a href="../miscellaneous/changelog.html">ChangeLog</a> for a full list of changes.
+</p>
+<warn>
+Since release 1.2.7 the <b>socket_timeout</b> property has been renamed to
+<b>recycle_timeout</b>.
+The socket_timeout now sets the real timeout for socket operations.
+</warn>
+<p>If you find any bugs during testing this release, please fill in the
+<a href="http://issues.apache.org/bugzilla/enter_bug.cgi?product=Tomcat%205">Bugzilla</a>
+Bug Report. When entering bug select <b>Native:JK</b> Component.
+</p>
+
+<hr size="1" noshade="noshade" />
+
+<a name="20041115.1"> 
+<h3>15 November - JK2 is officially unsupported</h3>
+</a> 
+<p>JK2 has been put in maintainer mode and no further development will take place.
+The reason for shutting down JK2 development was the lack of developers interest.
+Other reason was lack of users interest in adopting JK2, caused by configuration
+complexity when compared to JK.
+</p>
+<p>The latest official JK2 release is 2.0.4.
+</p>
+<p>JK2 will have it's successor within core Apache2.1/2.2 distribution.
+We have developed new <b>proxy_ajp</b> that is an addition to
+the mod_proxy and uses Tomcat's AJP protocol stack. It is developped in httpd-2.1
+and integrated in it. We have also developed a new <b>proxy_balancer</b> module
+for load balancing http and ajp protocol stacks.
+</p>
+<p>JK will be fully supported for all other web servers. The next JK release is
+planned for the end of November. Lots of code from JK2 has been ported to JK
+</p>
+<hr size="1" noshade="noshade" />
+
+</section>
+</body>
+</document>  
diff --git a/connectors/jk/xdocs/news/20050101.xml b/connectors/jk/xdocs/news/20050101.xml
new file mode 100644
index 0000000..71c7cef
--- /dev/null
+++ b/connectors/jk/xdocs/news/20050101.xml
@@ -0,0 +1,158 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="20050101.html">
+ 
+  &project;
+ 
+  <properties>
+    <author email="general.AT.tomcat.DOT.apache.DOT.org">Apache Jakarta Project</author>
+    <title>2005 News and Status</title>
+  </properties>
+
+<body>
+
+<section name="2005 News &amp; Status">
+<br />
+<!--
+<a name="2004****.1">
+<h3></h3>
+</a>
+<hr size="1" noshade="noshade" />
+-->
+<a name="20051108.1"> 
+<h3>8 November - JK-1.2.15 released</h3>
+</a>
+<p>The Apache Tomcat team is proud to announce the immediate availability
+of Jakarta Tomcat Connectors 1.2.15. This is Stable release and it contains
+few bug fixes found in 1.2.14 version.
+</p>
+<p>
+ Please see the <a href="../miscellaneous/changelog.html">ChangeLog</a> for a full list of changes.
+</p>
+<p>If you find any bugs while using this release, please fill in the
+<a href="http://issues.apache.org/bugzilla/enter_bug.cgi?product=Tomcat%205">Bugzilla</a>
+Bug Report. When entering bug select <b>Native:JK</b> Component.
+</p>
+<hr size="1" noshade="noshade" />
+
+<a name="20050713.1"> 
+<h3>13 July - JK-1.2.14 released</h3>
+</a>
+<p>The Apache Tomcat team is proud to announce the immediate availability
+of Jakarta Tomcat Connectors 1.2.14. This is Stable release and it contains
+few bug fixes found in 1.2.13 version.
+</p>
+<p>
+ Please see the <a href="../miscellaneous/changelog.html">ChangeLog</a> for a full list of changes.
+</p>
+<p>If you find any bugs while using this release, please fill in the
+<a href="http://issues.apache.org/bugzilla/enter_bug.cgi?product=Tomcat%205">Bugzilla</a>
+Bug Report. When entering bug select <b>Native:JK</b> Component.
+</p>
+<hr size="1" noshade="noshade" />
+<a name="20050516.1"> 
+<h3>7 May - JK-1.2.13 released</h3>
+</a>
+<p>The Apache Jakarta Tomcat team is proud to announce the immediate availability
+of Jakarta Tomcat Connectors 1.2.13. This is development release and contains
+few bug fixes found in 1.2.12 version.
+</p>
+<p>
+ Please see the <a href="../miscellaneous/changelog.html">ChangeLog</a> for a full list of changes.
+</p>
+<p>If you find any bugs while using this release, please fill in the
+<a href="http://issues.apache.org/bugzilla/enter_bug.cgi?product=Tomcat%205">Bugzilla</a>
+Bug Report. When entering bug select <b>Native:JK</b> Component.
+</p>
+<hr size="1" noshade="noshade" />
+<a name="20050507.1"> 
+<h3>7 May - JK-1.2.12 released</h3>
+</a> 
+<p>The Apache Jakarta Tomcat team is proud to announce the immediate availability
+of Jakarta Tomcat Connectors 1.2.12 The release contains a significant number
+of bug fixes and new features. 
+</p>
+<p>
+We expect it to be ratified as a Stable release when the vote takes place
+in the next week.
+</p>
+<p>
+ Please see the <a href="../miscellaneous/changelog.html">ChangeLog</a> for a full list of changes.
+</p>
+<p>If you find any bugs while using this release, please fill in the
+<a href="http://issues.apache.org/bugzilla/enter_bug.cgi?product=Tomcat%205">Bugzilla</a>
+Bug Report. When entering bug select <b>Native:JK</b> Component.
+</p>
+<hr size="1" noshade="noshade" />
+<a name="20050429.1"> 
+<h3>29 April - JK-1.2.11 released</h3>
+</a> 
+<p>The Apache Jakarta Tomcat team is proud to announce the immediate availability
+of Jakarta Tomcat Connectors 1.2.11 The release contains a significant number
+of bug fixes and new features. 
+</p>
+<p>
+This version has not been released.
+</p>
+<p>
+ Please see the <a href="../miscellaneous/changelog.html">ChangeLog</a> for a full list of changes.
+</p>
+<p>If you find any bugs while using this release, please fill in the
+<a href="http://issues.apache.org/bugzilla/enter_bug.cgi?product=Tomcat%205">Bugzilla</a>
+Bug Report. When entering bug select <b>Native:JK</b> Component.
+</p>
+<hr size="1" noshade="noshade" />
+<a name="20050330.1"> 
+<h3>30 March - JK-1.2.10 released</h3>
+</a> 
+<p>The Apache Jakarta Tomcat team is proud to announce the immediate availability
+of Jakarta Tomcat Connectors 1.2.10 The release contains a significant number
+of bug fixes and new features. 
+</p>
+<p>
+We expect it to be ratified as a Stable release when the vote takes place
+in the next two weeks.
+</p>
+<p>
+ Please see the <a href="../miscellaneous/changelog.html">ChangeLog</a> for a full list of changes.
+</p>
+<warn>
+Since release 1.2.10 the <b>JkShmFile</b> property has been added for
+Apache 1.3.x and Apache 2.x web servers on UNIX and LINUX platforms.
+Load balancer will not work properly if this directive is not present.
+</warn>
+<p>If you find any bugs while using this release, please fill in the
+<a href="http://issues.apache.org/bugzilla/enter_bug.cgi?product=Tomcat%205">Bugzilla</a>
+Bug Report. When entering bug select <b>Native:JK</b> Component.
+</p>
+<hr size="1" noshade="noshade" />
+<a name="20050318.1"> 
+<h3>18 March - JK-1.2.9-beta released</h3>
+</a> 
+<p>The Apache Jakarta Tomcat team is proud to announce the immediate availability
+of Jakarta Tomcat Connectors 1.2.9-beta. The release contains a significant number
+of bug fixes and new features. 
+</p>
+<p>
+We expect it to be ratified as a Stable release when the vote takes place
+in the next two weeks.
+</p>
+<p>
+ Please see the <a href="../miscellaneous/changelog.html">ChangeLog</a> for a full list of changes.
+</p>
+<warn>
+Since release 1.2.9 the <b>JkShmFile</b> property has been added for
+Apache 1.3.x and Apache 2.x web servers on UNIX and LINUX platforms.
+Load balancer will not work properly if this directive is not present.
+</warn>
+<p>If you find any bugs during testing this release, please fill in the
+<a href="http://issues.apache.org/bugzilla/enter_bug.cgi?product=Tomcat%205">Bugzilla</a>
+Bug Report. When entering bug select <b>Native:JK</b> Component.
+</p>
+<hr size="1" noshade="noshade" />
+
+</section>
+</body>
+</document>  
diff --git a/connectors/jk/xdocs/news/20060101.xml b/connectors/jk/xdocs/news/20060101.xml
new file mode 100644
index 0000000..edb2efd
--- /dev/null
+++ b/connectors/jk/xdocs/news/20060101.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="20060101.html">
+ 
+  &project;
+ 
+  <properties>
+    <author email="general.AT.tomcat.DOT.apache.DOT.org">Apache Tomcat Connectors Project</author>
+    <title>2006 News and Status</title>
+  </properties>
+
+<body>
+
+<section name="2006 News &amp; Status">
+<br />
+<a name="20061210.1"> 
+<h3>10 December - JK-1.2.20 released</h3>
+</a>
+<p>The Apache Tomcat team is proud to announce the immediate availability
+of Tomcat Connectors 1.2.20. This is a stable release adding new features
+and a few bug fixes to version 1.2.19.
+Furthermore the documentation has been reorganised.
+</p>
+<p>
+ Please see the <a href="../miscellaneous/changelog.html">ChangeLog</a> for a full list of changes.
+</p>
+<p>If you find any bugs while using this release, please fill in the
+<a href="http://issues.apache.org/bugzilla/enter_bug.cgi?product=Tomcat%205">Bugzilla</a>
+Bug Report. When entering bug select <b>Native:JK</b> Component.
+</p>
+<hr size="1" noshade="noshade" />
+
+<a name="20060917.1"> 
+<h3>17 September - JK-1.2.19 released</h3>
+</a>
+<p>The Apache Tomcat team is proud to announce the immediate availability
+of Tomcat Connectors 1.2.19. This is a stable release adding some features
+and a few bug fixes to version 1.2.18.
+Furthermore the non-functional code trees for isapi and domino have been removed.
+</p>
+<p>
+ Please see the <a href="../miscellaneous/changelog.html">ChangeLog</a> for a full list of changes.
+</p>
+<p>If you find any bugs while using this release, please fill in the
+<a href="http://issues.apache.org/bugzilla/enter_bug.cgi?product=Tomcat%205">Bugzilla</a>
+Bug Report. When entering bug select <b>Native:JK</b> Component.
+</p>
+<hr size="1" noshade="noshade" />
+
+<a name="20060720.1"> 
+<h3>13 July - JK-1.2.18 released</h3>
+</a>
+<p>The Apache Tomcat team is proud to announce the immediate availability
+of Tomcat Connectors 1.2.18. This is a stable release adding
+a few bug fixes to the not released 1.2.17 version.
+</p>
+<p>
+ Please see the <a href="../miscellaneous/changelog.html">ChangeLog</a> for a full list of changes.
+</p>
+<p>If you find any bugs while using this release, please fill in the
+<a href="http://issues.apache.org/bugzilla/enter_bug.cgi?product=Tomcat%205">Bugzilla</a>
+Bug Report. When entering bug select <b>Native:JK</b> Component.
+</p>
+<hr size="1" noshade="noshade" />
+
+<a name="20060708.1"> 
+<h3>JK-1.2.17 not released</h3>
+</a>
+<p>Version 1.2.17 of Tomcat Connectors 1.2.17 has not been released 
+due to a bug in the types chosen for socket arguments.
+</p>
+<p>
+ Please see the <a href="../miscellaneous/changelog.html">ChangeLog</a> for a full list of changes.
+</p>
+<hr size="1" noshade="noshade" />
+
+<a name="20060606.1"> 
+<h3>JK-1.2.16 not released</h3>
+</a>
+<p>Version 1.2.16 of Tomcat Connectors 1.2.16 has not been released 
+due to a bug in the jk status worker. This version adds some features
+and a few bug fixes to the 1.2.15 version. Furthermore some worker attributes
+have been <a href="../reference/workers.html">deprecated</a>.
+</p>
+<p>
+ Please see the <a href="../miscellaneous/changelog.html">ChangeLog</a> for a full list of changes.
+</p>
+<hr size="1" noshade="noshade" />
+
+</section>
+</body>
+</document>  
diff --git a/connectors/jk/xdocs/news/20070301.xml b/connectors/jk/xdocs/news/20070301.xml
new file mode 100644
index 0000000..ad6bd54
--- /dev/null
+++ b/connectors/jk/xdocs/news/20070301.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="20070301.html">
+ 
+  &project;
+ 
+  <properties>
+    <author email="general.AT.tomcat.DOT.apache.DOT.org">Apache Tomcat Connectors Project</author>
+    <title>2007 News and Status</title>
+  </properties>
+
+<body>
+
+<section name="2007 News &amp; Status">
+<br />
+<a name="20070518.1"> 
+<h3>18 May - JK-1.2.23 released</h3>
+</a>
+<p>The Apache Tomcat team is proud to announce the immediate availability
+of Tomcat Connectors 1.2.23. This is a stable release adding new features
+and a few bug fixes to version 1.2.23.
+</p><p>
+It fixes an <a href="http://tomcat.apache.org/security-jk.html">Important vulnerability</a>.
+</p><p>
+ Please see the <a href="../miscellaneous/changelog.html">ChangeLog</a> for a full list of changes.
+</p>
+<p>If you find any bugs while using this release, please fill in the
+<a href="http://issues.apache.org/bugzilla/enter_bug.cgi?product=Tomcat%205">Bugzilla</a>
+Bug Report. When entering bug select <b>Native:JK</b> Component.
+</p>
+<hr size="1" noshade="noshade" />
+<a name="20070417.1"> 
+<h3>17 April - JK-1.2.22 released</h3>
+</a>
+<p>The Apache Tomcat team is proud to announce the immediate availability
+of Tomcat Connectors 1.2.22. This is a stable release adding new features
+and a few bug fixes to version 1.2.22.
+</p><p>
+ Please see the <a href="../miscellaneous/changelog.html">ChangeLog</a> for a full list of changes.
+</p>
+<p>If you find any bugs while using this release, please fill in the
+<a href="http://issues.apache.org/bugzilla/enter_bug.cgi?product=Tomcat%205">Bugzilla</a>
+Bug Report. When entering bug select <b>Native:JK</b> Component.
+</p>
+<hr size="1" noshade="noshade" />
+<a name="20070301.1"> 
+<h3>1 March - JK-1.2.21 released</h3>
+</a>
+<p>The Apache Tomcat team is proud to announce the immediate availability
+of Tomcat Connectors 1.2.21. This is a stable release adding new features
+and a few bug fixes to version 1.2.20.
+</p><p>
+It fixes a <a href="http://tomcat.apache.org/security-jk.html">Critical vulnerability</a> introduced in version 1.2.19
+</p><p>
+ Please see the <a href="../miscellaneous/changelog.html">ChangeLog</a> for a full list of changes.
+</p>
+<p>If you find any bugs while using this release, please fill in the
+<a href="http://issues.apache.org/bugzilla/enter_bug.cgi?product=Tomcat%205">Bugzilla</a>
+Bug Report. When entering bug select <b>Native:JK</b> Component.
+</p>
+<hr size="1" noshade="noshade" />
+
+</section>
+</body>
+</document>  
diff --git a/connectors/jk/xdocs/news/project.xml b/connectors/jk/xdocs/news/project.xml
new file mode 100644
index 0000000..798ec44
--- /dev/null
+++ b/connectors/jk/xdocs/news/project.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<project name="Apache Tomcat Connector Documentation - News"
+        href="http://tomcat.apache.org/">
+
+    <title>The Apache Tomcat Connector - News</title>
+
+    <logo href="/images/tomcat.gif">
+      The Apache Tomcat Connector - News
+    </logo>
+<body>
+
+    <menu name="Links">
+        <item name="Docs Home"                  href="../index.html"/>
+    </menu>
+
+    <menu name="Reference Guide">
+        <item name="workers.properties"         href="../reference/workers.html"/>
+        <item name="uriworkermap.properties"    href="../reference/uriworkermap.html"/>
+        <item name="Status Worker"              href="../reference/status.html"/>
+        <item name="Apache"                     href="../reference/apache.html"/>
+        <item name="IIS"                        href="../reference/iis.html"/>
+    </menu>
+
+    <menu name="Generic HowTo">
+        <item name="For the impatient"          href="../generic_howto/quick.html"/>
+        <item name="All about workers"          href="../generic_howto/workers.html"/>
+        <item name="Load Balancing"             href="../generic_howto/loadbalancers.html"/>
+    </menu>
+
+    <menu name="Webserver HowTo">
+        <item name="Apache"                     href="../webserver_howto/apache.html"/>
+        <item name="IIS"                        href="../webserver_howto/iis.html"/>
+        <item name="Netscape/SunOne/Sun"        href="../webserver_howto/nes.html"/>
+    </menu>
+
+    <menu name="AJP Protocol Reference">
+        <item name="AJPv13"                     href="../ajp/ajpv13a.html"/>
+        <item name="AJPv13 Extension Proposal"  href="../ajp/ajpv13ext.html"/>
+    </menu>
+
+    <menu name="Miscellaneous Documentation">
+        <item name="Frequently asked questions" href="../miscellaneous/faq.html"/>
+        <item name="Changelog"                  href="../miscellaneous/changelog.html"/>
+        <item name="Current Native:JK bugs"     href="http://issues.apache.org/bugzilla/buglist.cgi?query_format=advanced&amp;short_desc_type=allwordssubstr&amp;short_desc=&amp;product=Tomcat+5&amp;component=Native%3AJK&amp;long_desc_type=substring&amp;long_desc=&amp;bug_file_loc_type=allwordssubstr&amp;bug_file_loc=&amp;keywords_type=allwords&amp;keywords=&amp;bug_status=NEW&amp;bug_status=ASSIGNED&amp;bug_status=REOPENED&amp;emailassigned_to1=1&amp;emailtype1=substring&amp;email1=&amp;emailassigned_to2=1&amp;emailreporter2=1&amp;emailcc2=1&amp;emailtype2=substring&amp;email2=&amp;bugidtype=include&amp;bug_id=&amp;votes=&amp;chfieldfrom=&amp;chfieldto=Now&amp;chfieldvalue=&amp;cmdtype=doit&amp;order=Reuse+same+sort+as+last+time&amp;field0-0-0=noop&amp;type0-0-0=noop&amp;value0-0-0="/>
+        <item name="Contribute documentation"   href="../miscellaneous/doccontrib.html"/>
+        <item name="JK Status Ant Tasks"        href="../miscellaneous/jkstatustasks.html"/>
+        <item name="Reporting Tools"            href="../miscellaneous/reporttools.html"/>
+        <item name="Old JK/JK2 documentation"   href="http://tomcat.apache.org/connectors-doc-archive/jk2/index.html"/>
+    </menu>
+
+    <menu name="News">
+        <item name="2007"                       href="../news/20070301.html"/>
+        <item name="2006"                       href="../news/20060101.html"/>
+        <item name="2005"                       href="../news/20050101.html"/>
+        <item name="2004"                       href="../news/20041100.html"/>
+    </menu>
+    
+</body>
+</project>
diff --git a/connectors/jk/xdocs/project.xml b/connectors/jk/xdocs/project.xml
new file mode 100644
index 0000000..ae21819
--- /dev/null
+++ b/connectors/jk/xdocs/project.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<project name="Apache Tomcat Connector Documentation"
+        href="http://tomcat.apache.org/">
+
+    <title>The Apache Tomcat Connector</title>
+
+    <logo href="/images/tomcat.gif">
+      The Apache Tomcat Connector
+    </logo>
+<body>
+
+    <menu name="Links">
+        <item name="Docs Home"                  href="index.html"/>
+    </menu>
+
+    <menu name="Reference Guide">
+        <item name="workers.properties"         href="reference/workers.html"/>
+        <item name="uriworkermap.properties"    href="reference/uriworkermap.html"/>
+        <item name="Status Worker"              href="reference/status.html"/>
+        <item name="Apache"                     href="reference/apache.html"/>
+        <item name="IIS"                        href="reference/iis.html"/>
+    </menu>
+
+    <menu name="Generic HowTo">
+        <item name="For the impatient"          href="generic_howto/quick.html"/>
+        <item name="All about workers"          href="generic_howto/workers.html"/>
+        <item name="Load Balancing"             href="generic_howto/loadbalancers.html"/>
+    </menu>
+
+    <menu name="Webserver HowTo">
+        <item name="Apache"                     href="webserver_howto/apache.html"/>
+        <item name="IIS"                        href="webserver_howto/iis.html"/>
+        <item name="Netscape/SunOne/Sun"        href="webserver_howto/nes.html"/>
+    </menu>
+
+    <menu name="AJP Protocol Reference">
+        <item name="AJPv13"                     href="ajp/ajpv13a.html"/>
+        <item name="AJPv13 Extension Proposal"  href="ajp/ajpv13ext.html"/>
+    </menu>
+
+    <menu name="Miscellaneous Documentation">
+        <item name="Frequently asked questions" href="miscellaneous/faq.html"/>
+        <item name="Changelog"                  href="miscellaneous/changelog.html"/>
+        <item name="Current Native:JK bugs"     href="http://issues.apache.org/bugzilla/buglist.cgi?query_format=advanced&amp;short_desc_type=allwordssubstr&amp;short_desc=&amp;product=Tomcat+5&amp;component=Native%3AJK&amp;long_desc_type=substring&amp;long_desc=&amp;bug_file_loc_type=allwordssubstr&amp;bug_file_loc=&amp;keywords_type=allwords&amp;keywords=&amp;bug_status=NEW&amp;bug_status=ASSIGNED&amp;bug_status=REOPENED&amp;emailassigned_to1=1&amp;emailtype1=substring&amp;email1=&amp;emailassigned_to2=1&amp;emailreporter2=1&amp;emailcc2=1&amp;emailtype2=substring&amp;email2=&amp;bugidtype=include&amp;bug_id=&amp;votes=&amp;chfieldfrom=&amp;chfieldto=Now&amp;chfieldvalue=&amp;cmdtype=doit&amp;order=Reuse+same+sort+as+last+time&amp;field0-0-0=noop&amp;type0-0-0=noop&amp;value0-0-0="/>
+        <item name="Contribute documentation"   href="miscellaneous/doccontrib.html"/>
+        <item name="JK Status Ant Tasks"        href="miscellaneous/jkstatustasks.html"/>
+        <item name="Reporting Tools"            href="miscellaneous/reporttools.html"/>
+        <item name="Old JK/JK2 documentation"   href="http://tomcat.apache.org/connectors-doc-archive/jk2/index.html"/>
+    </menu>
+
+    <menu name="News">
+        <item name="2007"                       href="news/20070301.html"/>
+        <item name="2006"                       href="news/20060101.html"/>
+        <item name="2005"                       href="news/20050101.html"/>
+        <item name="2004"                       href="news/20041100.html"/>
+    </menu>
+
+</body>
+</project>
diff --git a/connectors/jk/xdocs/reference/apache.xml b/connectors/jk/xdocs/reference/apache.xml
new file mode 100644
index 0000000..c5c1efa
--- /dev/null
+++ b/connectors/jk/xdocs/reference/apache.xml
@@ -0,0 +1,845 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="apache.html">
+
+    &project; 
+
+    <properties>
+        <author email="mturk@apache.org">Mladen Turk</author>
+        <title>Configuring Apache</title>
+    </properties>
+
+<body>
+
+<section name="Configuration Directives">
+<p>
+Most of the directives are allowed once in the global part of the Apache httpd
+configuration and once in every &lt;VirtualHost&gt; elements. Exceptions from this rule are
+explicitely listed in the table below.
+</p>
+<p>
+Values are inherited from the main server to the virtual hosts. 
+Since version 1.2.20 they can be overwritten in the virtual hosts.
+Exceptions from this rule are
+again explicitely listed in the table below.
+</p>
+<p><b>Waring: If Apache httpd and Tomcat are configured to serve content from
+the same filing system location then care must be taken to ensure that httpd is
+not able to serve inappropriate content such as the contents of the WEB-INF
+directory or JSP source code.</b> This could occur if the httpd DocumentRoot
+overlaps with a Tomcat Host's appBase or the docBase of any Context. It could
+also occur when using the httpd Alias directive with a Tomcat Host's appBase or
+the docBase of any Context.
+</p>
+<p>
+Here are the all directives supported by Apache:
+</p>
+<attributes name="Directive">
+<attribute name="JkWorkersFile" required="false"><p>
+The name of a worker file for the Tomcat servlet containers.
+<br/>
+This directive is only allowed once. It must be put into
+ the global part of the configuration.
+<br/>
+If you don't use the JkWorkerProperty directives, then you must
+define your workers with a valid JkWorkersFile. There is no default
+value.
+</p></attribute>
+<attribute name="JkWorkerProperty" required="false"><p>
+Enables setting worker properties inside Apache configuration file.
+The syntax is the same as in the JkWorkersFile (usually workers.properties).
+Simply prefix each line with "JkWorkerProperty" to put it directly into
+the Apache httpd config files.
+<br/>
+This directive is allowed multiple times.
+It must be put into the global part of the configuration.
+<br/>
+If you don't use the JkWorkerProperty directives, then you must
+define your workers with a valid JkWorkersFile. There is no default
+value.
+<br/>
+This directive is available in jk1.2.7 version and later.
+</p></attribute>
+<attribute name="JkShmFile" required="false"><p>
+Shared memory file name. Used only on unix platforms.
+<br/>
+This directive is only allowed once. It must be put into
+ the global part of the configuration.
+<br/>
+The default value is logs/jk-runtime-status.
+</p></attribute>
+<attribute name="JkShmSize" required="false"><p>
+Size of the shared memory file name.
+<br/>
+This directive is only allowed once. It must be put into
+ the global part of the configuration.
+<br/>
+The default value depends on the platform. It is usually less than 64KB.
+</p></attribute>
+<attribute name="JkMountFile" required="false"><p>
+File containing multiple mappings from a context to a Tomcat worker.
+It is usually called uriworkermap.properties.
+<br/>
+For inheritance rules, see: JkMountCopy.
+<br/>
+There is no default value.
+</p></attribute>
+<attribute name="JkMountFileReload" required="false"><p>
+This directive configures the reload check interval in seconds.
+The JkMountFile is checked periodically for changes.
+A changed file gets reloaded automatically. If you set
+this directive to "0", reload checking is turned off.
+<br/>
+The default value is 60 seconds.
+<br/>
+This directive has been added in version 1.2.20 of mod_jk.
+</p></attribute>
+<attribute name="JkMount" required="false"><p>
+A mount point from a context to a Tomcat worker.
+<br/>
+This directive is allowed multiple times.
+It is allowed in the global configuration and in VirtualHost.
+You can also use it inside Location with a different syntax.
+Inside Location, one omits the first argument (path),
+which gets inherited from the Location.
+For inheritance rules, see: JkMountCopy.
+</p></attribute>
+<attribute name="JkUnMount" required="false"><p>
+An exclusion mount point from a context to a Tomcat worker.
+All exclusion mounts are checked after mapping a request
+to a tomcat worker. If the request maps also to an exclusion,
+it will not be forwarded to tomcat, and instead be served locally.
+<br/>
+This directive is allowed multiple times.
+It is allowed in the global configuration and in VirtualHost.
+You can also use it inside Location with a different syntax.
+Inside Location, one omits the first argument (path),
+which gets inherited from the Location.
+For inheritance rules, see: JkMountCopy.
+<br/>
+This directive is available in jk1.2.7 version and later.
+</p></attribute>
+<attribute name="JkAutoAlias" required="false"><p>
+Automatically Alias webapp context directories into the Apache
+document space.
+<br/>
+Care should be taken to ensure that only static content is served via httpd as a
+result of using this directive. Any static content served by httpd will bypass any
+security constraints defined in the application's web.xml.
+<br/>
+For inheritance rules, see: JkMountCopy.
+<br/>
+There is no default value.
+</p></attribute>
+<attribute name="JkMountCopy" required="false"><p>
+If this directive is set to On in some virtual server,
+the mounts from the global server will be copied to the
+virtual server, more precisely all mounts defined by JkMount
+or JkUnMount. The Mounts defined by JkMountFile and JkAutoAlias
+will only be inherited, if the VirtualHost does not define
+it's own JkMountFile or JkAutoAlias.
+<br/>
+This directive is only allowed inside VirtualHost.
+<br/>
+The default is Off.
+</p></attribute>
+<attribute name="JkWorkerIndicator" required="false"><p>
+Name of the Apache environment variable that can be used to set worker names
+in combination with SetHandler jakarta-servlet.
+<br/>
+This directive is only allowed once per virtual server.
+It is allowed in the global configuration and in VirtualHost.
+<br/>
+The default value is JK_WORKER_NAME.
+</p></attribute>
+<attribute name="JkLogFile" required="false"><p>
+Full or server relative path to the Tomcat Connector module log file.
+It will also work with pipe, by using a value of the form "| ...".
+<br/>
+The default value is logs/mod_jk.log.
+<br/>
+Pipes are supported for Apache 1.3 only since version 1.2.16.
+The default value exists only since version 1.2.20.
+</p></attribute>
+<attribute name="JkLogLevel" required="false"><p>
+The Tomcat Connector module log level, can be debug, info, warn
+error or trace.
+<br/>
+The default value is info.
+</p></attribute>
+<attribute name="JkLogStampFormat" required="false"><p>
+The Tomcat Connector module <b>date</b> log format, follow strftime syntax.
+This format will be used for the time stamps in the JkLogFile.
+<br/>
+The default value is "[%a %b %d %H:%M:%S %Y] ".
+</p></attribute>
+<attribute name="JkRequestLogFormat" required="false"><p>
+Request log format string. See detailed description below.
+<br/>
+There is no default value. Without defining a value, the request logging
+is turned off.
+</p></attribute>
+<attribute name="JkExtractSSL" required="false"><p>
+Turns on SSL processing and information gathering by mod_jk
+<br/>
+The default value is On.
+</p></attribute>
+<attribute name="JkHTTPSIndicator" required="false"><p>
+Name of the Apache environment variable that contains SSL indication.
+<br/>
+The default value is "HTTPS".
+</p></attribute>
+<attribute name="JkCERTSIndicator" required="false"><p>
+Name of the Apache environment variable that contains SSL client certificates.
+<br/>
+The default value is "SSL_CLIENT_CERT".
+</p></attribute>
+<attribute name="JkCIPHERIndicator" required="false"><p>
+Name of the Apache environment variable that contains SSL client cipher.
+<br/>
+The default value is "SSL_CIPHER".
+</p></attribute>
+<attribute name="JkCERTCHAINPrefix" required="false"><p>
+Name of the Apache environment (prefix) that contains SSL client chain certificates.
+<br/>
+The default value is "SSL_CLIENT_CERT_CHAIN_".
+</p></attribute>
+<attribute name="JkSESSIONIndicator" required="false"><p>
+Name of the Apache environment variable that contains SSL session.
+<br/>
+The default value is "SSL_SESSION_ID".
+</p></attribute>
+<attribute name="JkKEYSIZEIndicator" required="false"><p>
+Name of the Apache environment variable that contains SSL key size in use.
+<br/>
+The default value is "SSL_CIPHER_USEKEYSIZE".
+</p></attribute>
+<attribute name="JkOptions" required="false"><p>
+Set one of more options to configure the mod_jk module. See below for
+details about this directive.
+<br/>
+This directive can be used multiple times per virtual server.
+<br/>
+The default value is "ForwardURICompatUnparsed" since version 1.2.23.
+Until version 1.2.22 the default value was "ForwardURICompat".
+</p></attribute>
+<attribute name="JkEnvVar" required="false"><p>
+Adds a name and an optional default value of environment variable
+that should be sent to servlet-engine as a request attribute.
+If the default value is not given explicitely, the variable
+will only be send, if it is set during runtime.
+<br/>
+This directive can be used multiple times per virtual server.
+<br/>
+The default is empty, so no additional variables will be sent.
+<br/>
+Empty default values are supported since version 1.2.20.
+Not sending variables with empty defaults and empty runtime value
+has been introduced in version 1.2.21.
+</p></attribute>
+<attribute name="JkStripSession" required="false"><p>
+If this directive is set to On in some virtual server,
+the session IDs <code>;jsessionid=...</code> will be
+removed for non matched URLs.
+<br/>
+This directive is only allowed inside VirtualHost.
+<br/>
+The default is Off.
+<br/>
+This directive has been introduced in version 1.2.21.
+</p></attribute>
+
+</attributes>
+</section>
+
+<section name="Configuration Directives Types">
+<p>
+We'll discuss here the mod_jk directive types.
+</p>
+
+<subsection name="Define workers">
+<p>
+<b>JkWorkersFile</b> specify the location where mod_jk will find the workers definitions.
+Take a look at <a href="workers.html">Workers documentation</a> for detailed description.
+
+<source>
+  
+  JkWorkersFile     /etc/httpd/conf/workers.properties
+</source>
+
+<br/>
+<br/>
+</p>
+
+</subsection>
+
+<subsection name="Logging">
+<p>
+<b>JkLogFile</b> specify the location where mod_jk is going to place its log file.
+</p>
+
+<source>
+  JkLogFile     /var/log/httpd/mod_jk.log
+</source>
+
+<p>
+Since JK 1.2.3 for Apache 2.0 and JK 1.2.16 for Apache 1.3 this can also
+be used for piped logging:
+</p>
+
+<source>
+  JkLogFile     "|/usr/bin/rotatelogs /var/log/httpd/mod_jk.log 86400"
+</source>
+
+<p>
+<b>JkLogLevel</b>
+set the log level between :
+</p>
+
+<ul>
+<li>
+<b>info</b> log will contain standard mod_jk activity (default).
+</li>
+<li>
+<b>warn</b> log will contain non fatal error reports.
+</li>
+<li>
+<b>error</b> log will contain also error reports.
+</li>
+<li>
+<b>debug</b> log will contain all information on mod_jk activity
+</li>
+<li>
+<b>trace</b> log will contain all tracing information on mod_jk activity
+</li>
+</ul>
+
+<source>  
+  JkLogLevel    info
+</source>
+
+<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 log file. 
+Using the strftime() format string it's set by<br />
+default to <b>"[%a %b %d %H:%M:%S %Y]"</b>
+</p>
+
+<source>
+  JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "
+</source>
+
+<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 similar to the Apache LogFormat command, 
+here is a list of the available request log format options:
+</p>
+                       
+<p>
+<attributes name="Options">
+  <attribute name="%b" required="false">Bytes sent, excluding HTTP headers (CLF format)</attribute>
+  <attribute name="%B" required="false">Bytes sent, excluding HTTP headers</attribute>
+  <attribute name="%H" required="false">The request protocol</attribute>
+  <attribute name="%m" required="false">The request method</attribute>
+  <attribute name="%p" required="false">The canonical Port of the server serving the request</attribute>
+  <attribute name="%q" required="false">The query string (prepended with a ? if a query string exists, otherwise an empty string)</attribute>
+  <attribute name="%r" required="false">First line of request</attribute>
+  <attribute name="%s" required="false">Request HTTP status code</attribute>
+  <attribute name="%T" required="false">Request duration, elapsed time to handle request in seconds '.' micro seconds</attribute>
+  <attribute name="%U" required="false">The URL path requested, not including any query string.</attribute>
+  <attribute name="%v" required="false">The canonical ServerName of the server serving the request</attribute>
+  <attribute name="%V" required="false">The server name according to the UseCanonicalName setting</attribute>
+  <attribute name="%w" required="false">Tomcat worker name</attribute>
+  <attribute name="%R" required="false">Real worker name</attribute>
+</attributes>
+
+<source>
+  JkRequestLogFormat     "%w %V %T"
+</source>
+
+<br/>
+<br/>
+</p>
+
+<p>
+You can also log mod_jk information using the Apache standard module <b>mod_log_config</b>.
+The module sets several notes in the Apache httpd notes table.
+Most of them are are only useful in combination with a load balancer worker.
+</p>
+                       
+<p>
+<attributes name="Note">
+  <attribute name="JK_WORKER_NAME" required="false">Name of the worker selected by the URI mapping</attribute>
+  <attribute name="JK_WORKER_TYPE" required="false">Type of the worker selected by the URI mapping</attribute>
+  <attribute name="JK_WORKER_ROUTE" required="false">Actual worker name selected by the URI mapping (usually a member of the load balancer)</attribute>
+  <attribute name="JK_REQUEST_DURATION" required="false">Request duration in seconds and microseconds.
+                                                         At the moment only available if JkRequestLogFormat is set.</attribute>
+  <attribute name="JK_LB_FIRST_NAME" required="false">Load-Balancer: Name of the first worker tried</attribute>
+  <attribute name="JK_LB_FIRST_TYPE" required="false">Load-Balancer: Type of the first worker tried</attribute>
+  <attribute name="JK_LB_FIRST_ACCESSED" required="false">Load-Balancer: Access count for the first worker tried</attribute>
+  <attribute name="JK_LB_FIRST_READ" required="false">Load-Balancer: Bytes read for the first worker tried</attribute>
+  <attribute name="JK_LB_FIRST_TRANSFERRED" required="false">Load-Balancer: Bytes transferred for the first worker tried</attribute>
+  <attribute name="JK_LB_FIRST_ERRORS" required="false">Load-Balancer: Error count for the first worker tried</attribute>
+  <attribute name="JK_LB_FIRST_BUSY" required="false">Load-Balancer: Busy count for the first worker tried</attribute>
+  <attribute name="JK_LB_FIRST_ACTIVATION" required="false">Load-Balancer: Activation state for the first worker tried</attribute>
+  <attribute name="JK_LB_FIRST_STATE" required="false">Load-Balancer: Error state for the first worker tried</attribute>
+  <attribute name="JK_LB_LAST_NAME" required="false">Load-Balancer: Name of the last worker tried</attribute>
+  <attribute name="JK_LB_LAST_TYPE" required="false">Load-Balancer: Type of the last worker tried</attribute>
+  <attribute name="JK_LB_LAST_ACCESSED" required="false">Load-Balancer: Access count for the last worker tried</attribute>
+  <attribute name="JK_LB_LAST_READ" required="false">Load-Balancer: Bytes read for the last worker tried</attribute>
+  <attribute name="JK_LB_LAST_TRANSFERRED" required="false">Load-Balancer: Bytes transferred for the last worker tried</attribute>
+  <attribute name="JK_LB_LAST_ERRORS" required="false">Load-Balancer: Error count for the last worker tried</attribute>
+  <attribute name="JK_LB_LAST_BUSY" required="false">Load-Balancer: Busy count for the last worker tried</attribute>
+  <attribute name="JK_LB_LAST_ACTIVATION" required="false">Load-Balancer: Activation state for the last worker tried</attribute>
+  <attribute name="JK_LB_LAST_STATE" required="false">Load-Balancer: Error state for the last worker tried</attribute>
+</attributes>
+
+<source>
+  LogFormat     "%h %l %u %t \"%r\" %>s %b %{JK_WORKER_NAME}n %{JK_LB_FIRST_NAME}n \
+                 %{JK_LB_FIRST_BUSY}n %{JK_LB_LAST_NAME}n %{JK_LB_LAST_BUSY}n" mod_jk_log
+  CustomLog     logs/access_log     mod_jk_log
+</source>
+
+<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. Without any leading signs, options will be enabled.
+<br/>
+<br/>
+</p>
+
+<p>
+The three following options <b>+ForwardURIxxx</b> are mutually exclusive.
+Exactly one of them is required, a negative sign prefix is not allowed with them.
+The default value is "ForwardURICompatUnparsed" since version 1.2.23.
+Until version 1.2.22 the default value was "ForwardURICompat".
+You can turn the default off by switching on one of the other two options.
+<br/>
+<br/>
+</p>
+
+<p>
+All options are inherited from the global server to virtual hosts.
+Options that support enabling (plus options) and disabling (minus options),
+are inherited in the following way:
+<br/>
+<br/>
+options(vhost) = plus_options(global) - minus_options(global) + plus_options(vhost) - minus_options(vhost)
+<br/>
+<br/>
+</p>
+
+<p>
+Using JkOptions <b>ForwardURICompatUnparsed</b>, the forwarded URI
+will be unparsed. It's spec compliant and also the safest option.
+It will always forward the original request URI, so rewriting
+URIs with mod_rewrite and then forwarding the rewritten URI
+will not work.
+
+<source>
+  JkOptions     +ForwardURICompatUnparsed
+</source>
+
+<br/>
+<br/>
+</p>
+
+<p>
+Using JkOptions <b>ForwardURICompat</b>, the forwarded URI will
+be decoded by Apache httpd. Encoded characters will be decoded and
+explicit path components like ".." will already be resolved.
+This is less spec compliant and is <b>not safe</b> if you are using
+prefix JkMount. This option will allow to rewrite URIs with
+mod_rewrite before forwarding.
+
+<source>
+  JkOptions     +ForwardURICompat
+</source>
+
+<br/>
+<br/>
+</p>
+
+<p>
+Using JkOptions <b>ForwardURIEscaped</b>, the forwarded URI will
+be the encoded form of the URI used by ForwardURICompat.
+Explicit path components like ".." will already be resolved.
+This will not work in combination with URL encoded session IDs,
+but it will allow to rewrite URIs with mod_rewrite before forwarding.
+
+<source>
+  JkOptions     +ForwardURIEscaped
+</source>
+
+<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.
+
+<source>  
+  JkOptions     +ForwardDirectories
+</source>
+<br/>
+<br/>
+</p>
+
+<p>
+JkOptions <b>ForwardLocalAddress</b>, you ask mod_jk to send the local address,
+of the Apache web server instead remote client address. This can be used by
+Tomcat remote address valve for allowing connections only from registered Apache
+web servers.
+
+<source>  
+  JkOptions     +ForwardLocalAddress
+</source>
+
+<br/>
+<br/>
+</p>
+
+<p>
+JkOptions <b>FlushPackets</b>, you ask mod_jk to flush Apache's connection
+buffer after each AJP packet chunk received from Tomcat. This option can have
+a strong performance penalty for Apache and Tomcat as writes are performed
+more often than would normally be required (ie: at the end of each
+response).
+
+<source>  
+  JkOptions     +FlushPackets
+</source>
+
+<br/>
+<br/>
+</p>
+
+<p>
+JkOptions <b>FlushHeader</b>, you ask mod_jk to flush Apache's connection
+buffer after the response headers have been  received from Tomcat.
+
+<source>  
+  JkOptions     +FlushHeader
+</source>
+
+<br/>
+<br/>
+</p>
+
+<p>
+JkOptions <b>DisableReuse</b>, you ask mod_jk to close connections immediately
+after their use. Normally mod_jk uses persistent connections and pools idle
+connections to reuse them, when new requests have to be sent to Tomcat.
+</p>
+
+<p>
+Using this option will have a strong performance penalty for Apache and Tomcat.
+Use this only as a last resort in case of unfixable network problems.
+If a firewall between Apache and Tomcat silently kills idle connections,
+try to use the worker attribute socket_keepalive in combination with an appropriate
+TCP keepalive value in your OS.
+
+<source>  
+  JkOptions     +DisableReuse
+</source>
+
+<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).
+
+<source>  
+  JkOptions     +ForwardKeySize
+</source>
+
+<br/>
+<br/>
+</p>
+
+<p>
+JkOptions <b>ForwardSSLCertChain</b>,  you ask mod_jk, when using ajp13,
+to forward SSL certificate chain (off by default).
+Mod_jk only passes the <code>SSL_CLIENT_CERT</code> to the AJP connector. This is not a
+problem with self-signed certificates or certificates directly signed by the
+root CA certificate. However, there's a large number of certificates signed by
+an intermediate CA certificate, where this is a significant problem: A servlet
+will not have the possibility to validate the client certificate on its own. The
+bug would be fixed by passing on the <code>SSL_CLIENT_CERT_CHAIN</code> to Tomcat via the AJP connector.
+<br/>
+This directive exists only since version 1.2.22.
+<source>  
+  JkOptions     +ForwardSSLCertChain
+</source>
+
+<br/>
+<br/>
+</p>
+
+<p>
+The directive <b>JkEnvVar</b> allows you to forward environment variables from Apache server to Tomcat engine.
+The variables can be retrieved on the Tomcat side as request attributes.
+You can add a default value as a second parameter to the directive.
+If the default value is not given explicitely, the variable
+will only be send, if it is set during runtime.
+<br/>
+<br/>
+The variables are inherited from the global server to virtual hosts.
+
+<source>  
+  JkEnvVar     SSL_CLIENT_V_START     undefined
+</source>
+<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>
+
+<source>
+  # send all requests ending in .jsp to worker1
+  JkMount /*.jsp worker1
+  # send all requests ending /servlet to worker1
+  JkMount /*/servlet/ worker1
+  # send all requests jsp requests to files located in /otherworker will go worker2
+  JkMount /otherworker/*.jsp worker2
+</source>
+
+<p>
+You can use the JkMount directive at the top level or inside &lt;VirtualHost&gt;
+sections of your httpd.conf file.
+</p>
+<p><b>JkUnmount</b> directive acts as an opposite to JkMount and blocks access
+to a particular URL. The purpose is to be able to filter out the particular content
+types from mounted context. The following example mounts /servlet/*
+context, but all .gif files that belongs to that context are not served.
+</p>
+<source>
+  # send all requests ending with /servlet to worker1
+  JkMount /servlet/* worker1
+  # do not send requests ending with .gif to worker1
+  JkUnMount /servlet/*.gif worker1
+</source>
+<p>
+JkUnMount takes precedence over JkMount directives, meaning that the JK
+will first look for unmount and then for mount directives. The following
+example will block all .gif files.
+</p>
+<source>
+  # do not send requests ending with .gif to worker1
+  JkUnMount /*.gif worker1
+  # The .gif files will not be mounted cause JkUnMount takes
+  # precedence over JkMount directive
+  JkMount /servlet/*.gif worker1
+</source>
+
+<p>
+<b>JkAutoAlias</b> directive automatically <b>Alias</b> webapp context directories into
+the Apache document space. It enables Apache to serve a static context while Tomcat
+serving dynamic context. This directive is used for convenience so that you don't
+have to put an apache Alias directive for each application directory inside Tomcat's
+webapp directory. For security reasons is is strongly recommended that JkMount
+is used to pass all requests to Tomcat by default and JkUnMount is used to
+explicitly exclude static content to be served by httpd. It should also be noted
+that content served by httpd will bypass any security constraints defined in the
+applciation's web.xml.
+</p>
+<source>
+  # enter the full path to the tomcat webapps directory
+  JkAutoAlias /opt/tomtact/webapps
+</source>
+<p>The following example shows how to serve a dynamic context by
+Tomcat and static using Apache. The webapps directory has to
+be accessible by apache.</p>
+
+<source>
+  # enter the full path to the tomcat webapps directory
+  JkAutoAlias /opt/tomtact/webapps
+
+  # Mount 'servlets-examples' directory. It's physical location
+  # is assumed to be in the /opt/tomtact/webapps/servlets-examples
+  # ajp13w is a worker defined in the workers.properties
+  JkMount /servlets-examples/* ajp13w
+
+  # Unmount desired static content from servlets-examples webapp.
+  # This content will be served by the httpd directly.
+  JkUnMount /servlets-examples/*.gif ajp13w
+  JkUnMount /servlets-examples/*.jpg ajp13w
+</source>
+<p>Note that you can have a single JkAutoAlias directive per virtual
+host inside your httpd.conf
+</p>
+<p>
+<b>JkWorkerProperty</b> is a new directive available from JK 1.2.7
+version. It is a convenient method for setting directives that are
+usually set inside <b>workers.propeties</b>file. The parameter for
+that directive is raw line from workers.properties file.
+</p>
+<source>
+  # Just like workers.properties but exact line is prefixed
+  # with JkWorkerProperty
+
+  # Minimal jk configuration
+  JkWorkerProperty worker.list=ajp13w
+  JkWorkerProperty worker.ajp13w.type=ajp13
+  JkWorkerProperty worker.ajp13w.host=localhost
+  JkWorkerProperty worker.ajp13w.port=8009   
+</source>
+<p>
+<b>JkMountFile</b> is a new directive available from JK 1.2.9
+version. It is used for dynamic updates of mount points at runtime.
+When the mount file is changed, JK will reload it's content.
+</p>
+<source>
+  # Load mount points
+
+  JkMountFile conf/uriworkermap.properties
+</source>
+<p>If the mount point uri starts with an exclamation mark '!'
+it defines an exclusion in the same way JkUnmount does.
+If the mount point uri starts with minus sign '-'
+the mount point will only be disabled. A disabled mount can be reenabled
+by deleting the minus sign and waiting for the JkMountFile to reload.
+An exclusion can be disabled by prefixing it with a mninus sign.
+</p>
+<source>
+  # Sample uriworkermap.properties file
+
+  /servlets-examples/*=ajp13w
+  # Do not map .jpeg files
+  !/servlets-examples/*.jpeg=ajp13w
+  # Make jsp examples initially disabled  
+  -/jsp-examples/*=ajp13w
+</source>
+<p>At run time you can change the content of this file. For example
+removing minus signs will enable the previously disabled uri mappings.
+You can add any number of new entries at runtime that reflects the newly deployed
+applications. Apache will reload the file and update the mount
+points within 60 second interval.
+</p>
+<p>
+There is no way to delete entries by dynamic reloading, but you can disable or
+exclude mappings.
+<br/>
+<br/>
+</p>
+
+</subsection>
+
+<subsection name="Using SetHandler and Environment Variables">
+<p>
+Alternatively to the mod_jk specific directives, you can also use
+SetHandler and environment variables to control, which requests
+are being forwarded via which worker. This gives you more flexibility,
+but the results might be more difficult to understand. If you mix both
+ways of defining the forwards, in general to mod_jk directives will win.
+</p>
+<p>
+<b>SetHandler jakarta-servlet</b> forces requests to be handled by mod_jk.
+If you neither specify any workers via JkMount and the related directives,
+not via the environment variable described below,
+the first worker in the list of all worker will be chosen. You can use SetHandler
+for example in Location blocks or with Apache 2.2 also in RewriteRule.
+</p>
+<p>
+In order to control the worker using <b>SetEnvIf</b> or <b>RewriteRule</b>
+for more complex rules, you can set the environment variable <b>JK_WORKER_NAME</b>
+to the name of your chosen target worker. This enables you to decide on
+the chosen worker in a more flexible way, including dependencies on cookie values.
+This feature has been added in version 1.2.19 of mod_jk.
+</p>
+<p>
+In order to use another variable than <b>JK_WORKER_NAME</b>, you can set the name
+of this variable via the <b>JkWorkerIndicator</b> directive.
+</p>
+<p>
+Finally you can define exclusions from mod_jk forwards by setting the environment
+variable <b>no-jk</b>.
+</p>
+<source>
+  # Automatically map all encoded urls
+  &lt;Location *;jsessionid=&gt;
+    SetHandler jakarta-servlet
+    SetEnv JK_WORKER_NAME my_worker
+  &lt;/Location&gt;
+
+  # Map all subdirs to workers via naming rule
+  # and exclude static content.
+  &lt;Location /apps/&gt;
+    SetHandler jakarta-servlet
+    SetEnvIf REQUEST_URI ^/apps/([^/]*)/ JK_WORKER_NAME=$1
+    SetEnvIf REQUEST_URI ^/apps/([^/]*)/static no-jk
+  &lt;/Location&gt;
+</source>
+
+</subsection>
+ </section>
+</body>
+</document>
diff --git a/connectors/jk/xdocs/reference/iis.xml b/connectors/jk/xdocs/reference/iis.xml
new file mode 100644
index 0000000..2f60d93
--- /dev/null
+++ b/connectors/jk/xdocs/reference/iis.xml
@@ -0,0 +1,177 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="iis.html">
+
+    &project; 
+
+    <properties>
+        <author email="mturk@apache.org">Mladen Turk</author>
+        <title>Configuring IIS</title>
+    </properties>
+
+<body>
+
+<section name="Requirements">
+<p>
+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="Registry settings">
+<p>
+ISAPI redirector reads configuration from the registry, create a new registry key named :
+</p>
+<p>
+<b>"HKEY_LOCAL_MACHINE\SOFTWARE\Apache Software Foundation\Jakarta Isapi Redirector\1.0"</b>
+</p>
+<attributes name="Key Name">
+<attribute name="extension_uri" required="true"><p>
+A string value pointing to the ISAPI extension <b>/jakarta/isapi_redirect.dll</b>
+</p></attribute>
+<attribute name="log_file" required="false"><p>
+A value pointing to location where log file will be created.
+(for example <b>c:\tomcat\logs\isapi.log</b>)
+</p></attribute>
+<attribute name="log_level" required="false"><p>
+A string value for log level 
+(can be debug, info, warn, error or trace).
+</p></attribute>
+<attribute name="worker_file" required="true"><p>
+A string value which is the full path to workers.properties file
+(for example <b>c:\tomcat\conf\workers.properties</b>)
+</p></attribute>
+<attribute name="worker_mount_file" required="true"><p>
+A string value which is the full path to uriworkermap.properties file
+(for example <b>c:\tomcat\conf\uriworkermap.properties</b>)
+</p></attribute>
+<attribute name="rewrite_rule_file" required="false"><p>
+A string value which is the full path to rewrite.properties file
+(for example <b>c:\tomcat\conf\rewrite.properties</b>)
+</p></attribute>
+<attribute name="shm_size" required="false"><p>
+A DWORD value size of the shared memory. Set this value to be
+the number of all defined workers * 400.
+(Set this value only if you have <b>more</b> then <b>64</b> workers)
+</p>
+<p>This directive has been added in version 1.2.20</p>
+</attribute>
+<attribute name="worker_mount_reload" required="false"><p>
+A DWORD value specifying the time in seconds upon which the
+<b>worker_mount_file</b> will be reloaded.
+</p>
+<p>This directive has been added in version 1.2.20</p>
+</attribute>
+<attribute name="strip_session" required="false"><p>
+A string value representing a boolean. If it is set to true,
+URL session suffixes of the form ";jsessionid=..." get stripped of URLs,
+even if the are served locally by the web server.
+A true value can be represented by the string "1" or any string starting
+with the letters "T" or "t". A false value will be assumed for "0"
+or any string starting with "F" or "f". The default value is false.
+</p>
+<p>This directive has been added in version 1.2.21</p>
+</attribute>
+
+</attributes>
+</section> 
+<section name="Using a properties file for configuration">
+<p>
+The ISAPI redirector can read it's configuration from a properties file instead of the registry. 
+This has the advantage that you can use multiple ISAPI redirectors with independant configurations on the same server.
+The redirector will check for the properties file during initialisation, and use it in preference to the registry if present.
+</p>
+<p>
+Create a properties file in the same directory as the ISAPI redirector called <b>isapi_redirect.properties</b> i.e. with the same name as the ISAPI redirector DLL but with a <em>.properties</em> extension. A sample isapi_redirect.properties can be found under the conf directory.
+</p>
+<p>
+The property names and values in the properties file are the same as for the registry settings described above. For example:
+</p>
+<p>
+<source>
+# Configuration file for the Jakarta ISAPI Redirector
+
+# The path to the ISAPI Redirector Extension, relative to the website
+# This must be in a virtual directory with execute privileges
+extension_uri=/jakarta/isapi_redirect.dll
+
+# Full path to the log file for the ISAPI Redirector
+log_file=c:\tomcat\logs\isapi_redirect.log
+
+# Log level (debug, info, warn, error or trace)
+log_level=info
+
+# Full path to the workers.properties file
+worker_file=c:\tomcat\conf\workers.properties
+
+# Full path to the uriworkermap.properties file
+worker_mount_file=c:\tomcat\conf\uriworkermap.properties
+</source>
+</p>
+<p>
+    Notes: 
+    <ul>
+        <li>
+            Back-slashes - '\' - are not escape characters.
+        </li>
+        <li>
+            Comment lines begin with '#'.
+        </li>
+    </ul>
+</p>
+</section>
+
+<section name="Using a simple rewrite rules">
+<p>
+The ISAPI redirector with version 1.2.16 can do a simple URL rewriting. Althought not
+as powerfull as Apache Httpd's mod_rewrite, it allows a simple exchange of request uris
+</p>
+<p>
+The rule is in the form rewritten=real-url.
+</p>
+<p>
+The rules must be simple strings. For example:
+</p>
+<p>
+<source>
+# Simple rewrite rules
+
+/jsp/=/jsp-examples/
+/servlets/=/servlets-examples/
+
+</source>
+</p>
+<p>
+Note that the uriworkermap or mount point must point to that new rule.
+</p>
+</section>
+
+</body>
+</document>
diff --git a/connectors/jk/xdocs/reference/project.xml b/connectors/jk/xdocs/reference/project.xml
new file mode 100644
index 0000000..c55ca5b
--- /dev/null
+++ b/connectors/jk/xdocs/reference/project.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<project name="Apache Tomcat Connector Documentation - Reference Guide"
+        href="http://tomcat.apache.org/">
+
+    <title>The Apache Tomcat Connector - Reference Guide</title>
+
+    <logo href="/images/tomcat.gif">
+      The Apache Tomcat Connector - Reference Guide
+    </logo>
+<body>
+
+    <menu name="Links">
+        <item name="Docs Home"                  href="../index.html"/>
+    </menu>
+
+    <menu name="Reference Guide">
+        <item name="workers.properties"         href="../reference/workers.html"/>
+        <item name="uriworkermap.properties"    href="../reference/uriworkermap.html"/>
+        <item name="Status Worker"              href="../reference/status.html"/>
+        <item name="Apache"                     href="../reference/apache.html"/>
+        <item name="IIS"                        href="../reference/iis.html"/>
+    </menu>
+
+    <menu name="Generic HowTo">
+        <item name="For the impatient"          href="../generic_howto/quick.html"/>
+        <item name="All about workers"          href="../generic_howto/workers.html"/>
+        <item name="Load Balancing"             href="../generic_howto/loadbalancers.html"/>
+    </menu>
+
+    <menu name="Webserver HowTo">
+        <item name="Apache"                     href="../webserver_howto/apache.html"/>
+        <item name="IIS"                        href="../webserver_howto/iis.html"/>
+        <item name="Netscape/SunOne/Sun"        href="../webserver_howto/nes.html"/>
+    </menu>
+
+    <menu name="AJP Protocol Reference">
+        <item name="AJPv13"                     href="../ajp/ajpv13a.html"/>
+        <item name="AJPv13 Extension Proposal"  href="../ajp/ajpv13ext.html"/>
+    </menu>
+
+    <menu name="Miscellaneous Documentation">
+        <item name="Frequently asked questions" href="../miscellaneous/faq.html"/>
+        <item name="Changelog"                  href="../miscellaneous/changelog.html"/>
+        <item name="Current Native:JK bugs"     href="http://issues.apache.org/bugzilla/buglist.cgi?query_format=advanced&amp;short_desc_type=allwordssubstr&amp;short_desc=&amp;product=Tomcat+5&amp;component=Native%3AJK&amp;long_desc_type=substring&amp;long_desc=&amp;bug_file_loc_type=allwordssubstr&amp;bug_file_loc=&amp;keywords_type=allwords&amp;keywords=&amp;bug_status=NEW&amp;bug_status=ASSIGNED&amp;bug_status=REOPENED&amp;emailassigned_to1=1&amp;emailtype1=substring&amp;email1=&amp;emailassigned_to2=1&amp;emailreporter2=1&amp;emailcc2=1&amp;emailtype2=substring&amp;email2=&amp;bugidtype=include&amp;bug_id=&amp;votes=&amp;chfieldfrom=&amp;chfieldto=Now&amp;chfieldvalue=&amp;cmdtype=doit&amp;order=Reuse+same+sort+as+last+time&amp;field0-0-0=noop&amp;type0-0-0=noop&amp;value0-0-0="/>
+        <item name="Contribute documentation"   href="../miscellaneous/doccontrib.html"/>
+        <item name="JK Status Ant Tasks"        href="../miscellaneous/jkstatustasks.html"/>
+        <item name="Reporting Tools"            href="../miscellaneous/reporttools.html"/>
+        <item name="Old JK/JK2 documentation"   href="http://tomcat.apache.org/connectors-doc-archive/jk2/index.html"/>
+    </menu>
+
+    <menu name="News">
+        <item name="2007"                       href="../news/20070301.html"/>
+        <item name="2006"                       href="../news/20060101.html"/>
+        <item name="2005"                       href="../news/20050101.html"/>
+        <item name="2004"                       href="../news/20041100.html"/>
+    </menu>
+    
+</body>
+</project>
diff --git a/connectors/jk/xdocs/reference/status.xml b/connectors/jk/xdocs/reference/status.xml
new file mode 100644
index 0000000..7aa2d6e
--- /dev/null
+++ b/connectors/jk/xdocs/reference/status.xml
@@ -0,0 +1,459 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="status.html">
+
+    &project;
+
+    <properties>
+        <author email="rjung@apache.org">Rainer Jung</author>
+        <title>Status Worker Reference</title>
+    </properties>
+
+<body>
+
+<section name="Introduction">
+<br/>
+<p>
+Tomcat Connectors has a special type of worker, the so-called status worker.
+The status worker does not forward requests to Tomcat instances. Instead it allows
+to retrieve status and configuration information at runtime,
+and furthermore to change many configuration items dynamically. This can be done
+via a simple embedded web interface.
+</p>
+<p>
+The status worker is especially powerful, when used together with load balancing workers.
+The dynamic management features of load balancers in combination with the status worker
+are a good reason, to use load balancer workers on top of ajp13 workers, even if there would
+be only one member worker in the load balancer.
+</p>
+<p>
+This document does not explain the HTML user interface of the status worker.
+Until now it is very simple, so just go ahead and use it. This doc instead
+tries to explain the less obvious features of the status worker. We also will give a
+complete coverage of the various request parameters and their meaning, so that you can
+include the status worker in your automation scripts. 
+</p>
+<p>
+The documentation of the status worker starts with <b>jk 1.2.20</b>
+</p>
+</section>
+
+<section name="Usage Patterns">
+<br/>
+<subsection name="Actions">
+<br/>
+<p>
+The status worker knows about six actions.
+<ul>
+<li>
+<b>list</b>: lists the configurations and runtime information of all configured workers.
+The output will be grouped by global information first (version data), then load balancer
+information, after that AJP worker information and finally the legend. For load balancers,
+there will be a summary part, and after that details for each member worker. For all workers,
+we also include the URL mappings (forward definitions).
+</li>
+<li>
+<b>show</b>: the same as list, but only shows data for one chosen worker
+</li>
+<li>
+<b>edit</b>: produces a form to edit configuration data for a chosen worker. There is a
+special subtype of "edit", that makes it easy to change one attribute for all members of
+a load balancer, e.g. their activation state.
+</li>
+<li>
+<b>update</b>: commit changes made in an edit form. <b>Caution</b>: the changes will not be
+persisted to the configuration files. As soon as your restart your web server, all changes
+made through the status worker will be lost! On the other hand, the changes done by the status
+worker will be applied during runtime without a restart of the web server.
+</li>
+<li>
+<b>reset</b>: reset all runtime information for a load balancer
+or one of its members.
+</li>
+<li>
+<b>recover</b>: Mark a member of a load balancer, that is in error state, for immediate recovery.
+</li>
+<li>
+<b>version</b>: only show version information of the web server and the JK software
+</li>
+</ul>
+</p>
+</subsection>
+
+<subsection name="Output Format">
+<br/>
+<p>
+For most actions you can choose between 4 output formats.
+<ul>
+<li>
+<b>HTML</b>: Used interactively with a browser
+</li>
+<li>
+<b>XML</b>: Mostly useful for automation, when your scripting environment is XML friendly.
+This format has rich structure information, but does not work line based, so you would really
+like to use it together with XML tools.
+</li>
+<li>
+<b>Properties</b>: This format is a line based format, that conforms to the rules of Java
+property files. Most structure information is contained in the hierarchical key. For information,
+that is of configuration nature, the format should produce lines very similar to the ones you can
+use in workers.properties. It will not produce a complete configuration file!
+</li>
+<li>
+<b>Text</b>: A simple textual output format.
+</li>
+</ul>
+The "edit" action does only make sense for the HTML output type.
+</p>
+</subsection>
+
+<subsection name="User Interface Features">
+<br/>
+<p>
+In the HTML view, there is an <b>automatic refresh</b> feature, implemented via the meta refresh
+option of HTML. Once you start the automatic refresh, the UI will will respect it for all actions
+except edit, update and maintain. Even if you navigate through one of those, the automatic refresh
+will start again as soon as you come back to one of the other actions.
+</p>
+<p>
+Many parts of the HTML page can be minimized, if they are not interesting for you. There are a couple
+of "Hide" links, which will collapse parts of the information. The feature exists for the following
+blocks of information:
+<ul>
+<li>
+<b>Legend</b>: Do not show the legend for the information presented in "list" and "show" actions
+</li>
+<li>
+<b>Load Balancer Workers</b>: Do not show workers of type "lb"
+</li>
+<li>
+<b>AJP Workers</b>: Do not show workers of type ajp
+</li>
+<li>
+<b>Member Workers</b>: Do not show detailed information concerning each member of load balancers
+</li>
+</ul>
+</p>
+</subsection>
+
+<subsection name="Special Considerations concerning URL Maps and Virtual Hosts">
+<br/>
+<p>
+The Apache module mod_jk makes use of the internal Apache httpd infrastructure concerning
+virtual hosts. The downside of this is, that the status worker can only show URL maps, for
+the virtual host it is defined in. It is not able to reach the configuration objects
+for other virtual hosts. Of course you can define a status worker in any virtual host you
+are using. All information presented apart from the URL maps will be the same, independant
+of the virtual host the status worker has been called in.
+</p>
+</subsection>
+
+<subsection name="Logging">
+<br/>
+<p>
+The status worker will log changes made to the configuration with log level "info" to the usual
+JK log file. Invalid requests will be logged with log level "warn". If you want to report some
+broken behaviour, log file content of level "debug" or even "trace" will be useful.
+</p>
+</subsection>
+
+</section>
+
+<section name="Configuration">
+<br/>
+<subsection name="Basic Configuration">
+<br/>
+<p>
+The basic configuration of a status worker is very similar to that of a usual ajp worker.
+You need to specify a name for the worker, and the URLs you want to map to it. The first
+part of the configuration happens in the workers.properties file. We define a worker named
+mystatus of type status:
+<source>
+worker.list=mystatus
+worker.mystatus.type=status
+</source>
+Then we define a URL, which should be mapped to this worker, i.e. the URL we use
+to reach the functionality of the status worker. You can use any method mod_jk supports
+for the web server of your choice. Possibilities are maps inside uriworkermap.properties,
+an additional mount attribute in workers.properties, or in Apache JkMount. Here's an
+example for a uriworkermap.properties line:
+<source>
+/private/admin/mystatus=mystatus
+</source>
+The URI pattern is case sensitive.
+</p>
+<p>
+As you will learn in the following sections, the status worker is very powerful. You should
+use the usual authentication and authorisation methods of your web server to secure this URL.
+</p>
+<p>
+You can also define multiple instances of the status worker, by using different names and URL mappings.
+For instance you might want to configure them individually
+and then allow special groups of people to use them
+</p>
+</subsection>
+
+<subsection name="Output Customization">
+<br/>
+<p>
+There are a couple of attributes for the workers.properties entries, which allow to customize
+various aspects of the output of the status worker.
+</p>
+<p>
+The attribute <b>css</b> can be set to the URL of a stylesheet:
+<source>
+worker.mystatus.css=/private/admin/static/mystatus.css
+</source>
+When writing HTML output, the status worker then includes the line
+<source>
+<link rel="stylesheet" type="text/css" href="/private/admin/static/mystatus.css" />
+</source>
+There is no sample stylesheet included with the mod_jk release, and by default the attribute css
+is empty, so no stylesheet reference will be included in the pages. The HTML code
+of the status worker output pages does not include any class attributes. If you like to contribute a
+stylesheet or improvements to the HTML layout, please contact us on the tomcat developers list.
+</p>
+<p>
+The properties output format can be customized via the attribute <b>prefix</b>. The names of all
+properties the status worker does output, will begin with this prefix. The default is "worker".
+</p>
+<p>
+Several attributes influence the format when writing XML output.
+The attribute <b>ns</b> allows to set a namespace prefix, that will be used for every status worker+element.
+The default is "jk:". Setting it to "-" disables the namesspace prefix.
+</p>
+<p>
+With the attribute xmlns you can map the prefix to a namespace URL. The default value
+is xmlns:jk="http://tomcat.apache.org". Setting it to "-" disables the output of the URL.
+</p>
+<p>
+Finally you can specify an XML document type via the attribute doctype. The specified string will 
+be inserted at the beginning of the document, directly after the xml header. The default is empty.
+</p>
+</subsection>
+
+<subsection name="Securing Access">
+<br/>
+<p>
+We urge you to use the builtin access control features of your web server to control
+access to the status worker URLs you have chosen. Nevertheless two configuration
+attributes of status workers are helpful. The attribute "read_only" disables all features of
+the status worker, that can be used to change configurations or runtime status of the other workers.
+A read_only status worker will not allow access to the edit, update, reset or recover actions.
+The default value is "False", ie. read/write. To enable read_only you need to set it to "True".
+</p>
+<p>
+You could configure two status workers, one has read_only and will be made available to a larger
+admin group, the other one will be used fully featured, but only by fewer people:
+<source>
+worker.list=jk-watch
+worker.jk-watch.type=status
+worker.jk-watch.read_only=True
+worker.jk-watch.mount=/user/status/jk
+worker.list=jk-manage
+worker.jk-manage.type=status
+worker.jk-manage.mount=/admin/status/jk
+</source>
+Starting with version 1.2.21, a read/write status worker can also be switched temporarily
+into read-only mode by the user via a link in the HTML GUI. The user can always switch it
+back to read/write. Only a status worker configured as read-only via the "read_only" attribute
+is completely safe from applying any changes.
+</p>
+<p>
+The other attribute you can use is <b>user</b>. By default this list is empty, which means
+no limit on the users. You can set "user" to a comma separated list of user names. If your
+web server is configured such that it sends the user names with the request, the status worker
+will check, if the name attached with the request is contained in it's "user" list.
+</p>
+<p>
+The user list can be split over multiple occurences of the "user" attribute.
+</p>
+<p>
+By default, the user names are matched case sensitively. Starting with version 1.2.21 you can set
+the attribute <b>user_case_insensitive</b> to "True". Then the comparison will be made case insensitive.
+</p>
+</subsection>
+
+<subsection name="Service Availability Rating">
+<br/>
+<p>
+For load balancing workers the status worker shows some interesting overview information.
+It categorizes the members of the load balancer into the classes "good", "bad" and degraded".
+This feature can be combined with external escalation procedures. Depending on your global
+system design and your operating practises your preferred categorization might vary.
+</p>
+<p>
+The categorization is based on the activation state of the workers (active, disabled or stopped),
+which is a pure configuration state, and the runtime state
+(OK, N/A, busy, recovering, probing, forced recovery, error)
+which only depends on the runtime situation.
+</p>
+<p>
+By default the status worker groups into "good" all members, that have activation "active" and
+runtime state not equal to "error". The "bad" group consists of the members, that have either activation
+"stopped", or are in runtime state "error".
+</p>
+<p>
+Workers that fit neither of the two groups, are considered to be "degraded".
+</p>
+<p>
+You can define other rules for the grouping into good, bad and degraded.
+The two attributes "good" and "bad" can be populated by a comma-separated list ob single characters or
+dot-separated pairs. Each character stands for the first character of one of the possible states "active",
+"disabled", "stopped", "ok", "na", "busy", "recovering" and "error". The additional states "probing"
+and "forced recovery" are always rated equivalent to "recovering".
+Comma-separated entries will be combined
+with logical "or", if you combine a configuration and a runtime state with a dot. the are combined with logical
+"and". So the default value for "good" is "a.o,a.n,a.b,a.r", for "bad" it is "e,s".
+</p>
+<p>
+The status worker first tries to match against the "bad" definitions, if this doesn't succeed
+it tries to macth against "good", and finally it choses "degarded", if no "bad" or "good" match
+can be found.
+</p>
+</subsection>
+</section>
+
+<section name="Request Parameters">
+<br/>
+<p>
+This section should help you building automation scripts based on the jk status
+management interface. This interface is still not stable, we expect further
+improvements in the next releases. It is well possible, that the request parameters
+might change in an incompatible way. So be prepared to change you scripts when
+updating to future versions.
+</p>
+<subsection name="Actions">
+<br/>
+<p>
+The action is determined by the parameter <b>cmd</b>. It can have the values "list", "show",
+"edit", "update", "reset", "recover" and "version". If you omit the "cmd" parameter,
+the default "list" will be used.
+All actions except for "list" and "refresh" need additional parameters.
+</p>
+</subsection>
+<subsection name="Output Format">
+<br/>
+<p>
+The format is determined by the parameter <b>mime</b>. It can have the values "html", "xml",
+"txt" and "prop". If you omit the "mime" parameter, the default "html"
+will be used. The action "edit" (the edit form) does only make sense for "mime=html".
+</p>
+</subsection>
+<subsection name="Worker Selection">
+<br/>
+<p>
+Actions that operate on a single worker need one or two additional parameters to select
+this worker. The parameter <b>w</b> contains the name of the worker from the worker list.
+If an action operates on a member (sub worker) of a load balancer, the parameter "w"
+contains the name of the load balancer worker, and the additional parameter <b>sw</b> contains the
+name of the sub worker.
+</p>
+</subsection>
+<subsection name="Automatic Refresh">
+<br/>
+<p>
+During automatic refresh, the parameter <b>re</b> contain the refresh interval in seconds.
+If you omit this parameter, automatic refresh will be off.
+</p>
+</subsection>
+<subsection name="Hide Options">
+<br/>
+<p>
+The parameter <b>opt</b> contains a bit mask of activated options. The default is 0, so
+by default no options are activated. The following options exist:
+<ul>
+<li>
+<b>0x0001</b>: hide members of lb workers
+</li>
+<li>
+<b>0x0002</b>: hide URL maps
+</li>
+<li>
+<b>0x0004</b>: hide the legend
+</li>
+<li>
+<b>0x0008</b>: hide load balancer workers
+</li>
+<li>
+<b>0x0010</b>: hide ajp workers
+</li>
+<li>
+<b>0x0020</b>: only allow read_only actions for a read/write status worker.
+</li>
+</ul>
+</p>
+</subsection>
+<subsection name="Data Parameters for the standard Update Action">
+<br/>
+<p>
+You can use the edit action with a final click to the update button, to change settings of workers.
+But you can also make direct calls to the update action. The following request parameters 
+contain the configuration information, you want to change. First the list for load balancer workers:
+<ul>
+<li>
+<b>lr</b>: retries (number)
+</li>
+<li>
+<b>lt</b>: recover_time (seconds)
+</li>
+<li>
+<b>ls</b>: sticky_session (0/f/n/off=off, 1/t/y/on=on; case insensitive)
+</li>
+<li>
+<b>lf</b>: sticky_session_force (0/f/n/off=off, 1/t/y/on=on; case insensitive)
+</li>
+<li>
+<b>lm</b>: method (0/r="Requests", 1/t="Traffic", 2/b="Busyness", 3/s="Sessions"; case insensitive, only first character is used)
+</li>
+<li>
+<b>ll</b>: lock (0/o="Optimistic", 1/p="Pessimistic"; case insensitive, only first character is used)
+</li>
+</ul>
+And now the list of parameters you can use to change settings for load balancer members:
+<ul>
+<li>
+<b>wa</b>: activation flag (0/a="active", 1/d="disabled", 2/s="stopped"; case insensitive, only first character is used)
+</li>
+<li>
+<b>wf</b>: load balancing factor (integer weight)
+</li>
+<li>
+<b>wn</b>: route for use with sticky sessions (string)
+</li>
+<li>
+<b>wr</b>: redirect to define simple failover rules (string)
+</li>
+<li>
+<b>wc</b>: domain to tell JK about your replication design (string)
+</li>
+<li>
+<b>wd</b>: distance to express preferences (integer)
+</li>
+</ul>
+For the details of all parameters, we refer to the <a href="workers.html">workers.properties Reference</a>.
+</p>
+</subsection>
+<subsection name="Aspect Editing for Load Balancer Members">
+<br/>
+<p>
+You can use the edit action to edit all settings for a load balancer or for a
+member of a load balancer respectively on one page. If you want to edit one
+configuration aspect for all members of a load balancer simultaneously, this
+will be triggered by the parameter <b>att</b>. The value of the parameter indicates,
+which aspect you want to edit. The list is the same as in the previous section:
+"wa", "wf", "wn", "wr", "wc" and "wd". But here you
+need to put the name into the parameter "att", instead of using it as a request
+parameter name.
+</p>
+<p>
+The values of the common aspect for all the load balancer members will be given
+in parameters named "val1", "val2", ....
+</p>
+</subsection>
+</section>
+
+</body>
+</document>
diff --git a/connectors/jk/xdocs/reference/uriworkermap.xml b/connectors/jk/xdocs/reference/uriworkermap.xml
new file mode 100644
index 0000000..a49bb91
--- /dev/null
+++ b/connectors/jk/xdocs/reference/uriworkermap.xml
@@ -0,0 +1,270 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="uriworkermap.html">
+
+    &project;
+
+    <properties>
+        <author email="rjung@apache.org">Rainer Jung</author>
+        <author email="mturk@apache.org">Mladen Turk</author>
+        <title>uriworkermap.properties configuration</title>
+    </properties>
+
+<body>
+
+<section name="Introduction">
+<br/>
+<p>
+The forwarding of requests from the web server to tomcat gets configured by defining mapping rules.
+Such a rule maps requests to workers. The request part of the map is described by a URI pattern,
+the worker by it's worker name.
+</p>
+<p>
+The so-called <b>uriworkermap</b> file is a mechanism of defining rules,
+which works for all web servers. There exist also other web server specific configuration
+options for defining rules, which will be mostly discussed on the reference pages for
+configuring tomcat connectors for the individual web servers.
+</p>
+<p>
+The name of the file is usually uriworkermap.properties,
+although this is configurable in the web server.
+Please consult the web server specific documentation pages on
+how to enable the uriworkermap file.
+</p>
+<p>
+The main features supported by the uriworkermap file are
+<ul>
+<li>
+Exact and wildchar matches, shortcuts to map a directory and all including content.
+</li>
+<li>
+Exclusion rules, disabling of rules an defined preferences behaviour.
+</li>
+<li>
+Virtual host integration: uri mapping rules can be expressed per virtual host.
+The details are web server specific though.
+</li>
+<li>
+Dynamic reloading: The file gets checked periodically for changes.
+New versions are automatically reloaded without web server restarts.
+</li>
+<li>
+Integration with the status worker.
+</li>
+<li>
+Support for comments in the rule file.
+</li>
+</ul>
+The following sections describe these aspects in more detail.
+</p>
+</section>
+
+<section name="Syntax">
+<br/>
+<subsection name="Line format">
+<br/>
+<p>
+The file has a line based format. There are no continuation characters,
+so each rule needs to be defined on a single line. Each rule is a pair consisting
+of a URI pattern and a worker name, combined by an equals sign '=':
+<source>
+  /myapp=myworker
+</source>
+The URI pattern is case sensitive.
+</p>
+</subsection>
+<subsection name="Comments, white space">
+<br/>
+<p>
+All text after and including the character '#' gets ignored and can be used for comments.
+Leading and trailing white space gets trimmed around the URI pattern and also around the worker name.
+The following definitions are all equivalent:
+<source>
+  # This is a white space example
+  /myapp=myworker
+     /myapp=myworker
+  /myapp  =  myworker
+</source>
+</p>
+</subsection>
+
+<subsection name="URI patterns">
+<br/>
+<p>
+Inside the URI pattern three special characters can be used, '*', '?' and '|'.
+The character '*' is a wildchar that matches any number of arbitrary characters
+in the URI, '?' matches exactly one character.
+Each URI pattern has to start with the character '/', or with '*' or with '?',
+optionally prefixed by any combination of the modifiers '!' and '-' (see next section).
+<source>
+  # Mapping the URI /myapp1 and everything under /myapp1/:
+  /myapp1=myworker-a
+  /myapp1/*=myworker-a
+  # Mapping all URI which end with a common suffix:
+  *.jsp=myworker
+  *.do=myworker
+</source>
+Since the first case of mapping a certain location and everything inside
+it is very common, the character '|' gives a handy shortcut:
+<source>
+  # Mapping the URI /myapp1 and everything under /myapp1/:
+  /myapp1|/*=myworker-a
+</source>
+The pattern 'X|Y' is exactly equivalent to the two maps 'X' and 'XY'.
+</p>
+</subsection>
+</section>
+
+<section name="Exclusion, Disabling and Preferences">
+<br/>
+
+<subsection name="Exclusions and rule disabling">
+<br/>
+<p>
+Exclusion rules allows to define exclusions from URI rules, which would forward
+requests to tomcat. If the exclusion rule matches, the request will not be forwarded.
+This is usually used to serve static content by the web server.
+An rule is an exclusion rule, if it is suffixed with '!':
+<source>
+  # Mapping the URI /myapp and everything under /myapp/:
+  /myapp|/*=myworker
+  # Exclude the subdirectory static:
+  !/myapp/static|/*=myworker
+  # Exclude some suffixes:
+  !*.html=myworker
+</source>
+</p>
+<p>
+Rule disabling comes into play, if your web server merges rules from various sources,
+and you want to disable any rule defined previously. Since the uriworkermap file gets
+reloaded dynamically, you can use this to temporarily disable request forwarding:
+An rule gets disabled, if it is suffixed with '-':
+<source>
+  # We are not in maintenance.
+  # The maintenance rule got defined someahere else.
+  -/*=maintenance
+</source>
+Exclusion rules can get disabled as well, then the rule starts with '-!'.
+</p>
+</subsection>
+
+<subsection name="Mapping preferences">
+<br/>
+<p>
+The most restrictive URI pattern is applied first. More precisely the URI patterns are
+sorted by the number of '/' characters in the pattern (highest number first), and
+rules with equal numbers are sorted by their string length (longest first).
+</p>
+<p>
+If both distinctions still do not suffice, then the defining source of the rule is considered.
+Rules defined in uriworkermap.properties come first, before rules defined by JkMount (Apache)
+and inside workers.properties using the mount attribute.
+</p>
+<p>
+All disabled rules are ignored. Exclusion rules are applied after all normal rules
+have been applied.
+</p>
+<p>
+There is no defined behaviour, for the following configuration conflict:
+using literally the same URI pattern in the same defining source but with
+different worker targets.
+</p>
+</subsection>
+</section>
+
+<section name="Virtual host integration">
+<br/>
+
+<subsection name="IIS">
+<br/>
+<p>
+When using IIS you can restrict individual rules to special virtual hosts
+by prefixing the URI pattern with the virtual host information.
+The rules is that the url must be prefixed with the host name.
+<source>
+  # Use www.foo.org as virtual host
+  /www.foo.org/myapp/*=myworker
+  # Use www.bar.org as virtual host
+  /www.bar.org/myapp/*=myworker
+  # Normal mapping
+  /mysecondapp/*=myworker
+</source>
+</p>
+<p>
+Note that /mysecondapp/* will be mapped to all virtual hosts present.
+In  case one needs to prevent the mappings to some particular virual host then
+the exclusion rule must be used
+<source>
+  # Make sure the myapp is accessible by all virtual hosts
+  /myapp/*=myworker
+  # Disable mapping myapp for www.foo.org virtual host
+  !/www.foo.org/myapp/*=myworker
+</source>
+</p>
+</subsection>
+
+<subsection name="Apache httpd">
+<br/>
+<p>
+For Apache you can define individual uriworkermap files per virtual host.
+The directive JkMountFile can be used in the main server and in each virtual host.
+If a virtual host does not use JkMountfile, but JkMountCopy is set to 'On',
+then it inhertis the JkMountFile from the main server.
+</p>
+</subsection>
+</section>
+
+<section name="Dynamic reloading">
+<br/>
+<p>
+When a request is being processed, tomcat connectors check the file modification time
+of the uriworkermap file. To keep the performance penalty low, this happens only,
+if the last check happened at least n seconds ago.
+</p>
+<p>
+For Apache you can configure the interval "n" using the directive JkMountFile,
+for IIS you would use the attribute worker_mount_reload.
+The default value is 60 seconds. A value of "0" turns off the reloading.
+</p>
+<p>
+If the file changed, it gets reloaded completely. If there exist rules coming
+from other sources than the uriworkermap file (e.g. the workers.properties mount
+attribute or JkMount with Apache httpd), the new uriworkermap file gets dynamically
+merged with these ones exactly like when you do a web server restart.
+</p>
+<p>
+Until version 1.2.19 reloading behaved slightly differently: it continuously added
+the full contents of the uriworkermap file to the rule mapping. The merging rules
+were, that duplicated got eliminated and old rules could be disabled, by defining the
+rule as disabled in the new file. Rules never got deleted.
+</p>
+</section>
+
+<section name="Status worker integration">
+<br/>
+<p>
+The configuration view of the status worker also shows the various mapping rules.
+After each worker's configuration, the rules are listed, that forward to this worker.
+The list contains three columns:
+<ul>
+<li>
+the type of the rule: Exact or Wildchar, eventually prefixed with Disabled or Unmount (for exclusion rules)
+</li>
+<li>
+the URI pattern
+</li>
+<li>
+and the source of the rule definition: 'worker definition' for the workers.properties file (mount attribute),
+'JkMount' for Apache httpd JkMount and it's relatives and finally 'uriworkermap' for the uriworkermap file.
+</li>
+</ul>
+For Apache httpd, there is an important subtlety: the request going to the status worker
+gets executed in the context of some server (main or virtual). The status worker will only show the
+mapping rules, that are defined for this server (main or virtual).
+</p>
+</section>
+
+</body>
+</document>
diff --git a/connectors/jk/xdocs/reference/workers.xml b/connectors/jk/xdocs/reference/workers.xml
new file mode 100644
index 0000000..2241330
--- /dev/null
+++ b/connectors/jk/xdocs/reference/workers.xml
@@ -0,0 +1,847 @@
+<?xml version="1.0"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="workers.html">
+
+    &project;
+
+    <properties>
+        <author email="mturk@apache.org">Mladen Turk</author>
+        <title>workers.properties configuration</title>
+    </properties>
+
+<body>
+
+<section name="Introduction">
+<br/>
+<p>
+A <b>Tomcat worker</b> is a Tomcat instance that is waiting to execute servlets or any other content
+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 <b>workers.properties</b> and this tutorial
+explains how to work with it.
+</p>
+</section>
+
+<section name="Directives">
+<br/>
+<p>Each workers.properties directive consists of three words separated by dot. The first word is always
+<b>worker</b>. The second word is the worker name that can be any name. The worker name reflects the
+name of the <b>jvmRoute</b> defined in Tomcat's server.xml configuration file.
+</p>
+<p>
+<warn>
+The name of the worker can contain only the alphanumeric characters <b>[a-z][A-Z][0-9][_\-]</b> and is case sensitive.
+</warn>
+</p>
+
+<subsection name="Defining workers">
+<br/>
+<p>The generic workers.properties directive is in the form:</p>
+<p><strong>worker.&lt;worker name&gt;.&lt;directive&gt;=&lt;value&gt;</strong></p>
+<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>
+<directives>
+<directive name="worker.list" default="ajp13" required="true">
+A comma separated list of workers names that the JK will use. When starting up,
+the web server plugin will instantiate the workers whose name appears in the
+worker.list property, these are also the workers to whom you can map requests.
+<p>
+This directive can be used multiple times.
+</p>
+</directive>
+<directive name="worker.maintain" default="60" required="false">
+Worker connection pool maintain interval in seconds. If set to the positive
+value JK will scan all connections for all workers specified in worker.list
+directive and check if connections needs to be recycled.
+<p>
+Furthermore any load balancer does a global maintenance every worker.maintain
+seconds. During global maintenance load counters are decayed and workers
+in error are checked for recover_time.
+</p>
+<p>
+This feature has been added in <b>jk 1.2.13</b>.
+</p>
+</directive>
+</directives>
+</subsection>
+
+<subsection name="Mandatory directives">
+<br/>
+<p>Mandatory directives are the one that each worker <b>must</b> contain. Without them the worker will
+be unavailable or will misbehave.
+</p>
+<directives>
+<directive name="type" default="ajp13" required="true">
+Type of the worker (can be one of ajp13, ajp14, jni, lb or status). The type of the worker
+defines the directives that can be applied to the worker.
+<p>AJP13 worker is the preferred worker type that JK uses for communication
+between web server and Tomcat. This type of worker uses sockets as communication
+channel. For detailed description of the AJP13 protocol stack browse to
+<a href="../ajp/ajpv13a.html">AJPv13 protocol specification</a>
+</p>
+</directive>
+</directives>
+</subsection>
+
+<subsection name="Connection directives">
+<p>Connection directives defines the parameters needed to connect and maintain
+the connections pool of persistent connections between JK and remote Tomcat.
+</p>
+<directives>
+
+<directive name="host" default="localhost" required="false">
+Host name or IP address of the backend Tomcat instance. The remote Tomcat must
+support the ajp13 protocol stack. The host name can have a <b>port</b> number
+embedded separated by the colon (':') character.
+</directive>
+
+<directive name="port" default="8009" required="false">
+Port number of the remote Tomcat instance listening for defined protocol requests.
+The default value depends on the worker type. For AJP13 workers the default port is
+<b>8009</b>, while for AJP14 type of worker that value is <b>8011</b>.
+</directive>
+
+<directive name="socket_timeout" default="0" required="false">
+Socket timeout in seconds used for communication channel between JK and remote host.
+If remote host does not respond inside that timeout the JK will generate an error,
+and retry again. If set to value zero (default) the JK will wait for infinite
+on all socket operations.
+</directive>
+
+<directive name="socket_keepalive" default="False" required="false">
+This directive 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 120 minutes), and thus prevent the firewall to cut the connection.
+To enable keepalive set this property value to the <b>True</b>.
+<p>
+The problem with Firewall cutting inactive connections is that sometimes, neither webserver or tomcat
+have information about the cut and couldn't handle it.
+</p>
+</directive>
+
+<directive name="retries" default="2" required="false">
+The number of retries that the worker will try in case of error returned from remote
+Tomcat. If the number of retries set is greater than two (the default value), on
+each retry after default an extra wait of 100ms will be inserted.
+</directive>
+
+<directive name="connection_pool_size" default="see text" required="false">
+This defines the number of connections made to the AJP backend that
+are maintained as a connection pool.
+It will limit the number of those connection that each web server child
+process can made.
+<p>
+Connection pool size property is used only for multi threaded
+web servers such as Apache 2.0 (worker), IIS and Netscape. The connection_pool_size property
+should reflect the number of threads per child process. JK will discover
+the number of threads per child process on Apache 2 web server with worker-mpm and set
+its default value to match the ThreadsPerChild Apache directive. For IIS the default
+value is 10. For other web servers than Apache or IIS this value has to be set manually.
+</p>
+<warn>Do not use connection_pool_size with values higher then 1 on <b>Apache 2.x prefork</b> or <b>Apache 1.3.x</b>!</warn>
+</directive>
+
+<directive name="connection_pool_minsize" default="(pool+1)/2" required="false">
+Minimum size of the connection pool that will be maintained.
+<p>
+Its default value is (connection_pool_size+1)/2.
+</p>
+<warn>Do not use connection_pool_size with values higher then 1 on <b>Apache 2.x prefork</b> or <b>Apache 1.3.x</b>!</warn>
+<p>
+This feature has been added in <b>jk 1.2.16</b>.
+</p>
+</directive>
+
+<directive name="connection_pool_timeout" default="0" required="false">
+Cache timeout property should be used with <b>connection_pool_size</b> to specify how many seconds JK should keep
+an inactive socket in cache before closing it. This property should be used to reduce the number of threads
+on the Tomcat web server. The default value zero disables the closing (infinite timeout).
+<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>
+</directive>
+
+<directive name="lbfactor" default="1" required="false">
+Only used for a member worker of a load balancer.
+<p>
+The integer number lbfactor (load-balancing factor) is
+<i>how much we expect this worker to work</i>, or
+<i>the worker's work quota</i>. Load balancing factor is compared with other workers
+that makes the load balancer. For example if one worker has lb_factor 5 times higher then
+other worker, then it will receive five times more requests.
+</p>
+</directive>
+
+</directives>
+
+</subsection>
+
+<subsection name="Load balancing directives">
+<br/>
+<p>Load balancer is a virtual worker that does not really communicate with Tomcat workers.
+Instead it is responsible for the management of several "real" workers.
+The worker is supposed to be a load balancer if it's worker type is <b>lb</b>.
+See worker's <b>type</b> directive. The workers that
+are member of load balancer must not appear in the <b>worker.list</b> directive.
+</p>
+<p>Loadbalancer directives defines the parameters needed to create a workers that are
+connecting to a remote cluster of backend Tomcat servers. Each cluster node has to
+have a worker defined.
+</p>
+<p>
+Load balancer 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:
+</p>
+
+<directives>
+<directive name="balance_workers" default="" required="true">
+A comma separated list of workers that the load balancer
+need to manage.
+<p>
+This directive can be used multiple times for the same load balancer.
+</p>
+<p>
+This directive replaces old <b>balanced_workers</b> directive and
+can be used only with mod_jk versions 1.2.7 and up.
+</p>
+<warn>These workers should <b>not</b> appear in the worker.list property!</warn>
+</directive>
+
+<directive name="sticky_session" default="True" required="false">
+Specifies whether requests with SESSION ID's should be routed back to the same
+Tomcat worker. If sticky_session is set to <b>True</b> or <b>1</b> sessions are sticky, otherwise
+sticky_session is set to <b>False</b>. Set sticky_session to <b>False</b> when Tomcat
+is using a Session Manager which can persist session data across multiple
+instances of Tomcat. By default sticky_session is set to True.
+</directive>
+
+<directive name="sticky_session_force" default="False" required="false">
+Specifies whether requests with SESSION ID's for workers that are in error state
+should be rejected. If sticky_session_force is set to <b>True</b> or <b>1</b>
+and the worker that matches that SESSION ID is in error state, client will
+recieve 500 (Server Error). If set to <b>False</b> or <b>0</b> failover on
+another worker will be issued with loosing client session. This directive is
+used only when you set <b>sticky_session=True</b>.
+<p>
+This feature has been added in <b>jk 1.2.9</b>.
+</p>
+</directive>
+
+<directive name="method" default="Request" required="false">
+Specifies what method load balancer is using for electing the best worker.
+Please note, that session stickyness and perfect load balancing are
+conflicting targets, especially when the number
+of sessions is small, or the usage of sessions is extremely varying
+For huge numbers of sessions this usually is not a problem.
+<p>
+Some methods note, that they aggregate in a sliding time window. They add up
+accesses, and on each run of the global maintain method, the load counters
+get divided by 2. Usually this happens once a minute, depending on the
+setting of worker.maintain. The value of the load counters can be inspected
+using the status worker.
+</p>
+<p>
+If method is set to <b>R[equest]</b> the balancer will use number of requests
+to find the best worker. Accesses will be distributed according to the
+lbfactor in a sliding time window. This is the default value and should be
+working well for most applications.
+</p>
+<p>
+If method is set to <b>S[ession]</b> the balancer will use number of sessions
+to find the best worker. Accesses will be distributed according to the
+lbfactor in a sliding time window. Because the balancer does not keep any state,
+it actually does not know the number of sessions. Instead it counts each request
+without a session cookie or URL encoding as a new session. This method will neither
+know, when a session is being invalidated, nor will it correct its load numbers
+according to session timeouts or worker failover. This method should be used,
+if sessions are your limiting resource, e.g. when you only have limited memory
+and your sessions need a lot of memory.
+</p>
+<p>
+If set to <b>T[raffic]</b> the balancer will use
+the network traffic between JK and Tomcat to find the best worker.
+Accesses will be distributed according to the lbfactor in a sliding time window.
+This method should be used, if network to and from the backends is your
+limiting resource.
+</p>
+<p>
+If set to <b>B[usyness]</b> the balancer will
+pick the worker with the lowest current load, based on how many requests the
+worker is currently serving. This number is divided by the workers lbfactor,
+and the lowest value (least busy) worker is picked. This method is especially
+interesting, if your request take a long time to process, like for a download
+application.
+</p>
+<p>
+This feature has been added in <b>jk 1.2.9</b>.
+The Session method has been added in <b>jk 1.2.20</b>.
+</p>
+</directive>
+
+<directive name="lock" default="Optimistic" required="false">
+Specifies what lock method the load balancer will use for synchronizing
+shared memory runtime data.
+If lock is set to <b>O[ptimistic]</b> balancer will not use shared memory lock
+to find the best worker. If set to <b>P[essimistic]</b> balancer will use
+shared memory lock. The balancer will work more accurately in case of
+Pessimistic locking, but can slow down the average response time.
+<p>
+This feature has been added in <b>jk 1.2.13</b>.
+</p>
+</directive>
+
+<directive name="secret" default="" required="false">
+Set a default secret word for all defined workers.
+See worker secret attribute description for more info.
+<p>
+This feature has been added in <b>jk 1.2.12</b>.
+</p>
+</directive>
+
+</directives>
+
+</subsection>
+
+<subsection name="Status Worker directives">
+<br />
+<p>
+The status worker does not communicate with Tomcat.
+Instead it is responsible for the load balancer management.
+</p>
+<directives>
+<directive name="css" default="" required="false">
+Specifies the url for cascading stylesheet to use.
+</directive>
+<directive name="read_only" default="False" required="false">
+A status worker with read_only=True will not allow any operations,
+that change the runtime state or configuration of the other workers.
+These are edit/update/reset/recover.
+<p>
+This feature has been added in <b>jk 1.2.20</b>.
+</p>
+</directive>
+<directive name="user" default="" required="false">
+It is a list of users
+which gets compared to the user name authenticated by the web server.
+If the name is not contained in this list, access is denied. Per
+default the list is empty and then access is allowed to anybody.
+<p>
+This directive can be used multiple times.
+</p>
+<p>
+This feature has been added in <b>jk 1.2.20</b>.
+</p>
+</directive>
+<directive name="user_case_insensitive" default="False" required="false">
+By default, the user names are matched case sensitively. You can set
+user_case_insensitive=True to make the comparison case insensitive.
+This may be especially useful on the Windows platform.
+<p>
+This feature has been added in <b>jk 1.2.21</b>.
+</p>
+</directive>
+<directive name="good" default="a.o,a.n,a.b,a.r" required="false">
+For every load balancer worker, the status worker shows a summary
+of the state of its members. There are three such states,
+"good", "bad" and "degraded".
+<p>
+These states are determined depending on the activation of the members
+(active, disabled, stopped) and their runtime state
+(ok, n/a, busy, recovering, probing, forced recovery, error).
+By default, members are assumed to be "good", if their activation
+is "active" and their runtime state is not "error".
+</p>
+<p>
+You can change this mapping, by assigning a list of values to the
+attribute "good". Each value gives a possible match for the members,
+and one match suffices. Each value is either a single character, or two
+characters combined with a dot ".". The single characters are the
+first characters in the words "active", "disabled", "stopped",
+"ok", "na", "busy", "recovering", "error". The additional states "probing"
+and "forced recovery" are always rated equivalent to "recovering".
+If a value consists only
+of a single character, then all members with this activation or runtime
+state will be assumed good. A combination of an activation and a runtime
+state concatenated with a dot "." does only apply to a member, that has
+exactly this activation and state.
+</p>
+<p>
+Members of a load balancer will first be matched against the state "bad",
+if they don't match, the state "good" will be tried, and if they
+still don't match, their state will be "degraded".
+</p>
+<p>
+This directive can be used multiple times.
+</p>
+<p>
+This feature has been added in <b>jk 1.2.20</b>.
+</p>
+</directive>
+<directive name="bad" default="s,e" required="false">
+See: "good".
+<p>
+By default, members are assumed to be "bad", if their activation
+is "stopped" or their runtime state is "error".
+</p>
+<p>
+This directive can be used multiple times.
+</p>
+<p>
+This feature has been added in <b>jk 1.2.20</b>.
+</p>
+</directive>
+<directive name="prefix" default="worker" required="false">
+The prefix, which will be used by the status worker
+when producing properties output (mime=prop).
+Each property key will be prefixed by this value.
+<p>
+This feature has been added in <b>jk 1.2.20</b>.
+</p>
+</directive>
+<directive name="ns" default="jk:" required="false">
+This directive can be used to customize the XML output from the
+status worker. If set to <b>-</b> no namespace will be used.
+<p>
+This feature has been added in <b>jk 1.2.20</b>.
+</p>
+</directive>
+<directive name="xmlns" default="" required="false">
+This directive can be used to customize the XML output from the
+status worker. If set to <b>-</b> no xmlns will be used.
+<p>
+Default value is set to xmlns:jk=&quot;http://tomcat.apache.org&quot;
+</p>
+<p>
+This feature has been added in <b>jk 1.2.20</b>.
+</p>
+</directive>
+<directive name="doctype" default="" required="false">
+This directive can be used to customize the XML output from the
+status worker. This value will be inserted to the output xml
+after the xml header.
+<p>
+This feature has been added in <b>jk 1.2.20</b>.
+</p>
+</directive>
+
+</directives>
+</subsection>
+
+<subsection name="Advanced worker directives">
+<br />
+<directives>
+<directive name="connect_timeout" default="0" required="false">
+Connect timeout 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.
+The default value zero disables the timeout (infinite timeout).
+<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>
+</directive>
+
+<directive name="prepost_timeout" default="0" required="false">
+Prepost timeout 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.
+The default value zero disables the timeout (infinite timeout).
+<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>
+</directive>
+
+<directive name="reply_timeout" default="0" required="false">
+Reply_timeout 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 (value zero) the 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 carefully if you
+have long running servlets.
+<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>
+</directive>
+
+<directive name="recover_time" default="60" required="false">
+Only used for load balancer workers.
+<p>
+The recover time is the time in seconds the load balancer will not try
+to use a worker, after it went into error state. Only after this time has passed,
+a worker in error state will be marked as in recovering, so that it will be
+tried for new requests.
+</p>
+<p>
+This interval is not checked every time a request is being processed.
+Instead it is being checked during global maintenance. The time between two
+runs of global maintenance is controlled by worker.maintain.
+</p>
+<p>
+Do not set recover_time to a very short time unless you understand the implications.
+Every recovery attempt for a worker in error is done by a real request!
+</p>
+</directive>
+
+<directive name="max_packet_size" default="8192" required="false">
+This attribute sets the maximal AJP packet size in Bytes.
+The maximum value is 65536. If you change it from the default,
+you <b>must</b> also change the packetSize attribute of your AJP
+connector on the tomcat side! The attribute packetSize is only available
+in Tomcat 5.5.20+ and 6.0.2+.
+<p>
+Normally it is not necessary to change the maximum packet size. Problems
+with the default value have been reported when sending certificates or
+certificate chains.
+</p>
+<p>
+This feature has been added in <b>jk 1.2.19</b>.
+</p>
+</directive>
+
+<directive name="recovery_options" default="0" required="false">
+Only used for a member worker of a load balancer.
+<p>
+Recovery options 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>
+<p>If the value 4 is added to the recovery options, the connection
+between the webserver and tomcat will be closed if the client connection
+to the webserver is terminated during the request/response cycle. This allows
+to inform the servlet engine about broken client connections during lengthy operations.
+This feature has been added in <b>jk 1.2.16</b>
+</p>
+</directive>
+
+<directive name="distance" default="0" required="false">
+Only used for a member worker of a load balancer.
+<p>
+An integer number to express preferences between
+the balanced workers of an lb worker.
+A load balancer will never choose some balanced worker 
+in case there is another usable worker with lower distance.
+</p>
+<p>
+Only in case all workers below a given distance are in error, disabled or stopped,
+workers of a larger distance are eligible for balancing.
+</p>
+<p>
+This feature has been added in <b>jk 1.2.16</b>.
+</p>
+</directive>
+
+<directive name="domain" default="" required="false">
+Only used for a member worker of a load balancer.
+<p>
+Domain directive can be used only when the worker is a member of the load balancer.
+Workers that share the same domain name are treated as single worker. If sticky_session
+is used, then the domain name is used as session route.
+</p>
+<p>
+This directive is used for large system with more then 6 Tomcats, to be able
+to cluster the Tomcats in two groups and thus lowering the session replication
+transfer between them.
+</p>
+<p>
+This feature has been added in <b>jk 1.2.8</b>.
+</p>
+</directive>
+
+<directive name="redirect" default="" required="false">
+Only used for a member worker of a load balancer.
+<p>
+Set to the preferred failover worker. If worker matching SESSION ID is in
+error state then the redirect worker will be used instead. It will be used
+even if being disabled, thus offering hot standby.
+</p>
+<p>
+This feature has been added in <b>jk 1.2.9</b>.
+</p>
+</directive>
+
+<directive name="activation" default="Active" required="false">
+Only used for a member worker of a load balancer.
+<p>
+Using this directive, a balanced worker of a load balancer
+can be configured as disabled or stopped. A disabled worker only gets
+requests, which belong to sessions for that worker. A stopped
+worker does not get any requests. Users will loose their sessions,
+unless session replication via clustering is used.
+</p>
+<p>
+Use <b>d</b> or <b>D</b> to disable and <b>s</b> or <b>S</b> to stop.
+If this directive is not present the deprecated directives
+"disabled" or "stopped" are used.
+</p>
+<p>
+This flag can be changed at runtime using status worker.
+</p>
+<p>
+This feature has been added in <b>jk 1.2.19</b>.
+</p>
+</directive>
+
+<directive name="route" default="worker name" required="false">
+Normally the name of a balanced worker in a load balancer is equal to the jvmRoute
+of the corresponding Tomcat instance. If you want to include a worker corresponding
+to a Tomcat instance into several load balancers with different balancing configuration
+(e.g. disabled, stopped) you can use this attribute.
+<p>
+Define a seperate worker per lb and per Tomcat instance with an arbitrary worker name and 
+set the route attribute of the worker equal to the jvmRoute of the target Tomcat instance.
+</p>
+<p>
+If this attribute is left empty, the name of the worker will be used.
+</p>
+<p>
+This attribute can be changed at runtime using status worker.
+</p>
+<p>
+If the route name contains a period, the part before the first period will be
+used as domain name, unless domain is set explicitly.
+</p>
+<p>
+This feature has been added in <b>jk 1.2.16</b>.<br/>
+The automatic domain rule has been added in <b>jk 1.2.20</b>.<br/>
+The attribute has been renamed from jvm_route to route in <b>jk 1.2.20</b>.
+</p>
+</directive>
+
+<directive name="reference" default="" required="false">
+This attribute can be used for normal workers and for load balancer workers.
+<p>
+This directive allows to copy configurations between workers
+in a hierarchical way. If worker castor sets <b>worker.castor.reference=worker.pollux</b>
+then it inherits all properties of <b>pollux</b>, except for the ones that
+are explicitely set for <b>castor</b>.
+</p>
+<p>
+Please note, that the value of the directive is not only the name of the referred worker,
+but the complete prefix including "worker.".
+</p>
+<p>
+This directive is especially useful, if one has a lot of balanced workers in a load balancer
+and these workers share most of their properties. You can set all of these properties
+in a phantom worker, e.g. using the prefix "worker.template1", and then simply
+reference those common properties in all balanced workers.
+</p>
+<p>
+This feature has been added in <b>jk 1.2.19</b>.
+</p>
+</directive>
+
+<directive name="secret" default="" required="false">
+This attribute can be used for normal workers and for load balancer workers.
+<p>
+If set to AJP Connector secret keyword, only request with this keyword are successfull responding.
+Use <b>request.useSecret="true"</b> and <b>request.secret="secret key word"</b> at your tomcat ajp
+Connector configuration.
+</p>
+</directive>
+
+<directive name="mount" default="" required="false">
+This attribute can be used for normal workers and for load balancer workers.
+<p>
+Space delimited list of uri maps the worker should handle. It is only used,
+if the worker is included in worker.list.
+</p>
+<p>
+This directive can be used multiple times for the same worker.
+</p>
+</directive>
+
+<directive name="fail_on_status" default="0" required="false">
+Set this value to the HTTP status code that will cause a worker to fail
+if returned from Servlet contatiner. Use this directive to deal with
+cases when the servlet container can temporary return non-200 responses
+for a short amount of time, e.g during redeployment.
+<p>
+This feature has been added in <b>jk 1.2.20</b>.
+</p>
+<p>
+Starting with <b>jk 1.2.22</b> it is possible to define multiple
+status codes separated by space or comma characters.
+For example: <code>worker.xxx.fail_on_status=500,503</code>
+</p>
+</directive>
+
+
+</directives>
+</subsection>
+
+<subsection name="Deprecated worker directives">
+<br/>
+<p>The following directives have been deprecated in the past. We include their documentation
+in case you need to use an older version of mod_jk. We urge you to update and not use
+them any more. Please migrate your existing configurations.
+</p>
+<deprecations>
+<directive name="cachesize" successor="connection_pool_size" default="see text" required="false">
+<warn>This directive has been deprecated since 1.2.16.</warn>
+Cachesize defines the number of connections made to the AJP backend that
+are maintained as a connection pool.
+It will limit the number of those connection that each web server child
+process can make.
+<p>
+Cachesize property is used only for multi threaded 
+web servers such as Apache 2.0 (worker), IIS and Netscape. The cachesize property
+should reflect the number of threads per child process. JK will discover
+the number of threads per child process on Apache 2 web server with worker-mpm and set
+its default value to match the ThreadsPerChild Apache directive. For IIS the default
+value is 10. For other web servers than Apache or IIS this value has to be set manually.
+</p>
+<warn>Do not use cachesize with values higher then 1 on <b>Apache 2.x prefork</b> or <b>Apache 1.3.x</b>!</warn>
+</directive>
+
+<directive name="cache_timeout" successor="connection_pool_timeout" default="0" required="false">
+<warn>This directive has been deprecated since 1.2.16.</warn>
+Cache timeout 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 web server.
+<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>
+</directive>
+
+<directive name="recycle_timeout" successor="connection_pool_timeout" default="0" required="false">
+<warn>This directive has been deprecated since 1.2.16.</warn>
+The number of seconds that 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.
+If set to value zero (default) no recycle will took place. 
+</directive>
+
+<directive name="balanced_workers" successor="balance_workers" default="" required="true">
+<warn>This directive has been deprecated since 1.2.7.</warn>
+A comma separated list of workers that the load balancer
+need to manage.
+</directive>
+
+<directive name="disabled" successor="activation" default="False" required="false">
+<warn>This directive has been deprecated since 1.2.19.</warn>
+If set to <b>True</b> or <b>1</b> the worker will be disabled if member
+of load balancer. This flag can be changed at runtime using status worker.
+<p>
+This feature has been added in <b>jk 1.2.9</b>.
+</p>
+</directive>
+
+<directive name="stopped" successor="activation" default="False" required="false">
+<warn>This directive has been deprecated since 1.2.19.</warn>
+If set to <b>True</b> or <b>1</b> the worker will be stopped if member
+of load balancer. The flag is needed for stop complete traffic of a sticky session
+worker. It is only usefull, when you have a cluster that replicated the sessions.
+This flag can be changed at runtime using status worker.
+<p>
+This feature has been added in <b>jk 1.2.11</b>.
+</p>
+</directive>
+
+<directive name="jvm_route" successor="route" default="worker name" required="false">
+<warn>This directive has been deprecated since 1.2.20.</warn>
+Normally the name of a balanced worker in a load balancer is equal to the jvmRoute
+of the corresponding Tomcat instance. If you want to include a worker corresponding
+to a Tomcat instance into several load balancers with different balancing configuration
+(e.g. disabled, stopped) you can use this attribute.
+<p>
+Define a seperate worker per lb and per Tomcat instance with an arbitrary worker name and 
+set the jvm_route attribute of the worker equal to the jvmRoute of the target Tomcat instance.
+</p>
+<p>
+If this attribute is left empty, the name of the worker will be used.
+</p>
+<p>
+This attribute can be changed at runtime using status worker.
+</p>
+<p>
+This feature has been added in <b>jk 1.2.16</b>.
+</p>
+</directive>
+
+</deprecations>
+</subsection>
+
+</section>
+
+</body>
+</document>
diff --git a/connectors/jk/xdocs/style.css b/connectors/jk/xdocs/style.css
new file mode 100644
index 0000000..923e737
--- /dev/null
+++ b/connectors/jk/xdocs/style.css
@@ -0,0 +1,57 @@
+div.screen {
+    margin: 10px 0px 10px 20px;
+    font-size: smaller;
+    color: #ffffff; 
+}
+div.example {
+    background-color: #e5ecf3;
+    color: #000;
+    padding: 0.5em;
+    margin: 1em 2em 1em 1em;
+}
+pre {
+    font-family: "Courier New", Courier, monospace;
+    font-weight: normal;
+    font-style: normal;
+    font-size: smaller;
+}
+em.screen {
+    font-weight: normal;
+    font-style: normal;
+    color: #c0c0c0;
+}
+p.screen {
+    background-color: #000000;
+    border-style: none;
+    color: #c0c0c0;
+    margin-left: 10px;
+    margin-right: 0px;
+    text-align: left;         
+}
+b.screen {
+    font-weight: normal;
+    font-style: normal;
+    color: #c0c0c0;
+}   
+code.screen {
+    background-color: #000000;
+    border-style: none;
+    color: #c0c0c0;
+    margin-left: 10px;
+    margin-right: 0px;
+    text-align: left; 
+}
+b.code {
+    font-weight: normal;
+    font-style: normal;
+    color: #023264;
+}
+p.todo {
+    background-color: #ffffff;
+    border-style: none;
+    color: #000000;
+    margin-left: 20px;
+    margin-right: 10px;
+    text-align: justify;
+    font-size: smaller;
+}                                  
diff --git a/connectors/jk/xdocs/style.xsl b/connectors/jk/xdocs/style.xsl
new file mode 100644
index 0000000..ff45d03
--- /dev/null
+++ b/connectors/jk/xdocs/style.xsl
@@ -0,0 +1,635 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!-- Content Stylesheet for "tomcat-docs" Documentation -->
+
+<!-- $Id$ -->
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+  version="1.0">
+
+
+  <!-- Output method -->
+  <xsl:output method="html"
+            encoding="iso-8859-1"
+              indent="no"/>
+
+
+  <!-- Defined parameters (overrideable) -->
+  <xsl:param    name="home-name"        select="'Apache Tomcat'"/>
+  <xsl:param    name="home-href"        select="'http://tomcat.apache.org/'"/>
+  <xsl:param    name="home-logo"        select="'/images/tomcat.gif'"/>
+  <xsl:param    name="printer-logo"     select="'/images/printer.gif'"/>
+  <xsl:param    name="relative-path"    select="'.'"/>
+  <xsl:param    name="void-image"       select="'/images/void.gif'"/>
+  <xsl:param    name="project-menu"     select="'menu'"/>
+  <xsl:param    name="standalone"       select="''"/>
+  <xsl:param    name="buglink"          select="'http://issues.apache.org/bugzilla/show_bug.cgi?id='"/>
+
+  <!-- Defined variables (non-overrideable) -->
+  <xsl:variable name="body-bg"          select="'#ffffff'"/>
+  <xsl:variable name="body-fg"          select="'#000000'"/>
+  <xsl:variable name="body-link"        select="'#525D76'"/>
+  <xsl:variable name="banner-bg"        select="'#525D76'"/>
+  <xsl:variable name="banner-fg"        select="'#ffffff'"/>
+  <xsl:variable name="sub-banner-bg"    select="'#828DA6'"/>
+  <xsl:variable name="sub-banner-fg"    select="'#ffffff'"/>
+  <xsl:variable name="source-color"     select="'#023264'"/>
+  <xsl:variable name="attributes-color" select="'#023264'"/>
+  <xsl:variable name="table-th-bg"      select="'#039acc'"/>
+  <xsl:variable name="table-td-bg"      select="'#a0ddf0'"/>
+
+  <!-- Process an entire document into an HTML page -->
+  <xsl:template match="document">
+    <html>
+    <head>
+    <title><xsl:value-of select="project/title"/> - <xsl:value-of select="properties/title"/></title>
+    <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" value="{$name}"/>
+      <meta name="email" value="{$email}"/>
+    </xsl:for-each>
+    <link href="{$relative-path}/style.css" type="text/css" rel="stylesheet"/>    
+    </head>
+
+    <body bgcolor="{$body-bg}" text="{$body-fg}" link="{$body-link}"
+          alink="{$body-link}" vlink="{$body-link}">
+
+    <table border="0" width="100%" cellspacing="4">
+
+      <xsl:comment>PAGE HEADER</xsl:comment>
+      <tr><td colspan="2">
+
+        <xsl:comment>TOMCAT LOGO</xsl:comment>
+        <xsl:variable name="alt">
+          <xsl:value-of select="$home-name"/>
+        </xsl:variable>
+        <xsl:variable name="href">
+          <xsl:value-of select="$home-href"/>
+        </xsl:variable>
+        <xsl:variable name="src">
+          <xsl:value-of select="$relative-path"/><xsl:value-of select="$home-logo"/>
+        </xsl:variable>
+        <a href="{$href}">
+          <img src="{$src}" align="left" alt="{$alt}" border="0"/>
+        </a>
+        <xsl:if test="project/logo">
+          <xsl:variable name="alt">
+            <xsl:value-of select="project/logo"/>
+          </xsl:variable>
+          <xsl:variable name="home">
+            <xsl:value-of select="project/@href"/>
+          </xsl:variable>
+          <xsl:variable name="src">
+            <xsl:value-of select="$relative-path"/><xsl:value-of select="project/logo/@href"/>
+          </xsl:variable>
+
+          <xsl:comment>APACHE LOGO</xsl:comment>
+          <a href="http://www.apache.org/">
+            <img src="http://www.apache.org/images/asf-logo.gif"
+                 align="right" alt="Apache Logo" border="0"/>
+          </a>
+
+        </xsl:if>
+
+      </td></tr>
+
+      <xsl:comment>HEADER SEPARATOR</xsl:comment>
+      <tr>
+        <td colspan="2">
+          <hr noshade="noshade" size="1"/>
+        </td>
+      </tr>
+
+      <tr>
+
+        <!-- Don't generate a menu if styling printer friendly docs -->
+        <xsl:if test="$project-menu = 'menu'">
+          <xsl:comment>LEFT SIDE NAVIGATION</xsl:comment>
+          <td width="20%" valign="top" nowrap="true">
+            <xsl:apply-templates select="project/body/menu"/>
+          </td>
+        </xsl:if>
+
+        <xsl:comment>RIGHT SIDE MAIN BODY</xsl:comment>
+        <td width="80%" valign="top" align="left">
+          <table border="0" width="100%" cellspacing="4">
+            <tr>
+              <td align="left" valign="top">
+                <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:variable name="src">
+                    <xsl:value-of select="$relative-path"/><xsl:value-of select="$printer-logo"/>
+                  </xsl:variable>
+                  <xsl:variable name="url">
+                    <xsl:value-of select="/document/@url"/>
+                  </xsl:variable>
+                  <small>
+                    <a href="printer/{$url}">
+                      <img src="{$src}" border="0" alt="Printer Friendly Version"/>
+                      <br />print-friendly<br />version
+                    </a>
+                  </small>
+                </xsl:if>
+                <xsl:if test="$project-menu != 'menu'">
+                  <xsl:variable name="void">
+                    <xsl:value-of select="$relative-path"/><xsl:value-of select="$void-image"/>
+                    </xsl:variable>
+                  <img src="{$void}" width="1" height="1" vspace="0" hspace="0" border="0"/>
+                </xsl:if>
+              </td>
+            </tr>
+          </table>
+          <xsl:apply-templates select="body/section"/>
+        </td>
+
+      </tr>
+
+      <xsl:comment>FOOTER SEPARATOR</xsl:comment>
+      <tr>
+        <td colspan="2">
+          <hr noshade="noshade" size="1"/>
+        </td>
+      </tr>
+
+      <xsl:comment>PAGE FOOTER</xsl:comment>
+      <tr><td colspan="2">
+        <div align="center"><font color="{$body-link}" size="-1"><em>
+        Copyright &#169; 1999-2005, Apache Software Foundation
+        </em></font></div>
+      </td></tr>
+
+    </table>
+    </body>
+    </html>
+
+  </xsl:template>
+
+
+  <!-- Process a menu for the navigation bar -->
+  <xsl:template match="menu">
+    <p><strong><xsl:value-of select="@name"/></strong></p>
+    <ul>
+      <xsl:apply-templates select="item"/>
+    </ul>
+  </xsl:template>
+
+
+  <!-- Process a menu item for the navigation bar -->
+  <xsl:template match="item">
+    <xsl:variable name="href">
+      <xsl:value-of select="@href"/>
+    </xsl:variable>
+    <li><a href="{$href}"><xsl:value-of select="@name"/></a></li>
+  </xsl:template>
+
+
+  <!-- Process a documentation section -->
+  <xsl:template match="section">
+    <xsl:variable name="name">
+      <xsl:value-of select="@name"/>
+    </xsl:variable>
+    <table border="0" cellspacing="0" cellpadding="2" width="100%">
+      <!-- Section heading -->
+      <tr><td bgcolor="{$banner-bg}">
+          <font color="{$banner-fg}" face="arial,helvetica.sanserif">
+          <a name="{$name}">
+          <strong><xsl:value-of select="@name"/></strong></a></font>
+      </td></tr>
+      <!-- Section body -->
+      <tr><td><blockquote>
+        <xsl:apply-templates/>
+      </blockquote></td></tr>
+    </table>
+  </xsl:template>
+
+
+  <!-- Process a documentation subsection -->
+  <xsl:template match="subsection">
+    <xsl:variable name="name">
+      <xsl:value-of select="@name"/>
+    </xsl:variable>
+    <table border="0" cellspacing="0" cellpadding="2" width="100%">
+      <!-- Subsection heading -->
+      <tr><td bgcolor="{$sub-banner-bg}">
+          <font color="{$sub-banner-fg}" face="arial,helvetica.sanserif">
+          <a name="{$name}">
+          <strong><xsl:value-of select="@name"/></strong></a></font>
+      </td></tr>
+      <!-- Subsection body -->
+      <tr><td><blockquote>
+        <xsl:apply-templates/>
+      </blockquote></td></tr>
+    </table>
+  </xsl:template>
+
+
+  <!-- Process a source code example -->
+  <xsl:template match="source">
+    <xsl:variable name="void">
+      <xsl:value-of select="$relative-path"/><xsl:value-of select="$void-image"/>
+    </xsl:variable>
+    <div class="example"><pre>
+        <xsl:value-of select="."/>
+        </pre>
+    </div>
+  </xsl:template>
+
+
+  <!-- Process an attributes list with nested attribute elements -->
+  <xsl:template match="attributes">
+    <table border="1" cellpadding="5">
+      <tr>
+        <th width="20%" bgcolor="{$attributes-color}">
+     	  <xsl:choose>
+            <xsl:when test="@name != ''">
+               <font color="#ffffff"><xsl:value-of select="@name"/></font>
+            </xsl:when>
+            <xsl:otherwise>
+               <font color="#ffffff">Attribute</font>
+            </xsl:otherwise>
+          </xsl:choose>          
+        </th>
+        <th width="80%" bgcolor="{$attributes-color}">
+          <font color="#ffffff">Description</font>
+        </th>
+      </tr>
+      <xsl:for-each select="attribute">
+        <tr>
+          <td align="left" valign="center">
+            <xsl:if test="@required = 'true'">
+              <strong><code><xsl:value-of select="@name"/></code></strong>
+            </xsl:if>
+            <xsl:if test="@required != 'true'">
+              <code><xsl:value-of select="@name"/></code>
+            </xsl:if>
+          </td>
+          <td align="left" valign="center">
+            <xsl:apply-templates/>
+          </td>
+        </tr>
+      </xsl:for-each>
+    </table>
+  </xsl:template>
+
+  <!-- Process an directives list with nested directive elements -->
+  <xsl:template match="directives">
+    <table border="1" cellpadding="5">
+      <tr>
+        <th width="15%" bgcolor="{$attributes-color}">
+          <font color="#ffffff">Directive</font>
+        </th>
+        <th width="10%" bgcolor="{$attributes-color}">
+          <font color="#ffffff">Default</font>
+        </th>
+        <th width="75%" bgcolor="{$attributes-color}">
+          <font color="#ffffff">Description</font>
+        </th>
+      </tr>
+      <xsl:for-each select="directive">
+        <tr>
+          <td align="left" valign="center">
+            <xsl:if test="@required = 'true'">
+              <strong><code><xsl:value-of select="@name"/></code></strong>
+            </xsl:if>
+            <xsl:if test="@required != 'true'">
+              <code><xsl:value-of select="@name"/></code>
+            </xsl:if>
+          </td>
+     	  <xsl:choose>
+            <xsl:when test="@default != ''">
+               <td align="center" valign="center">          
+               <code><xsl:value-of select="@default"/></code>
+              </td>
+            </xsl:when>
+            <xsl:otherwise>
+              <td align="center" valign="center">          
+              <code>-</code>
+              </td>
+            </xsl:otherwise>
+          </xsl:choose>          
+          <td align="left" valign="center">
+            <xsl:apply-templates/>
+          </td>
+        </tr>
+      </xsl:for-each>
+    </table>
+  </xsl:template>
+
+  <!-- Process an directives list with nested directive elements -->
+  <xsl:template match="deprecations">
+    <table border="1" cellpadding="5">
+      <tr>
+        <th width="15%" bgcolor="{$attributes-color}">
+          <font color="#ffffff">Directive</font>
+        </th>
+        <th width="15%" bgcolor="{$attributes-color}">
+          <font color="#ffffff">Successor</font>
+        </th>
+        <th width="10%" bgcolor="{$attributes-color}">
+          <font color="#ffffff">Default</font>
+        </th>
+        <th width="60%" bgcolor="{$attributes-color}">
+          <font color="#ffffff">Description</font>
+        </th>
+      </tr>
+      <xsl:for-each select="directive">
+        <tr>
+          <td align="left" valign="center">
+            <code><xsl:value-of select="@name"/></code>
+          </td>
+     	  <xsl:choose>
+            <xsl:when test="@successor != ''">
+               <td align="center" valign="center">          
+               <code><xsl:value-of select="@successor"/></code>
+              </td>
+            </xsl:when>
+            <xsl:otherwise>
+              <td align="center" valign="center">          
+              <code>-</code>
+              </td>
+            </xsl:otherwise>
+          </xsl:choose>          
+     	  <xsl:choose>
+            <xsl:when test="@default != ''">
+               <td align="center" valign="center">          
+               <code><xsl:value-of select="@default"/></code>
+              </td>
+            </xsl:when>
+            <xsl:otherwise>
+              <td align="center" valign="center">          
+              <code>-</code>
+              </td>
+            </xsl:otherwise>
+          </xsl:choose>          
+          <td align="left" valign="center">
+            <xsl:apply-templates/>
+          </td>
+        </tr>
+      </xsl:for-each>
+    </table>
+  </xsl:template>
+
+  <!-- Fix relative links in printer friendly versions of the docs -->
+  <xsl:template match="a">
+    <xsl:variable name="href" select="@href"/>
+    <xsl:choose>
+      <xsl:when test="$standalone = 'standalone'">
+        <xsl:apply-templates/>
+      </xsl:when>
+      <xsl:when test="$project-menu != 'menu' and starts-with(@href,'../')">
+        <a href="../{$href}"><xsl:apply-templates/></a>
+      </xsl:when>
+      <xsl:when test="$project-menu != 'menu' and starts-with(@href,'./') and contains(substring(@href,3),'/')">
+        <a href=".{$href}"><xsl:apply-templates/></a>
+      </xsl:when>
+      <xsl:when test="$project-menu != 'menu' and not(contains(@href,'//')) and not(starts-with(@href,'/')) and not(starts-with(@href,'#')) and contains(@href,'/')">
+        <a href="../{$href}"><xsl:apply-templates/></a>
+      </xsl:when>
+      <xsl:when test="$href != ''">
+        <a href="{$href}"><xsl:apply-templates/></a>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:variable name="name" select="@name"/>
+        <a name="{$name}"><xsl:apply-templates/></a>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+    
+  <!-- Warning -->
+  <xsl:template match="warn">
+    <p>
+    <font color="#ff0000">
+    <xsl:apply-templates/>
+    </font>
+    </p>
+  </xsl:template>
+
+  <!-- Changelog related tags -->
+  <xsl:template match="changelog">
+    <table border="0" cellpadding="2" cellspacing="2">
+      <xsl:apply-templates/>
+    </table>
+  </xsl:template>
+
+  <xsl:template match="changelog/add">
+    <tr>
+      <xsl:variable name="src"><xsl:value-of select="$relative-path"/>/images/add.gif</xsl:variable>
+      <td valign="top"><img alt="add" class="icon" src="{$src}"/></td>
+      <td><xsl:apply-templates/></td>
+    </tr>
+  </xsl:template>
+
+  <xsl:template match="changelog/update">
+    <tr>
+      <xsl:variable name="src"><xsl:value-of select="$relative-path"/>/images/update.gif</xsl:variable>
+      <td valign="top"><img alt="update" class="icon" src="{$src}"/></td>
+      <td><xsl:apply-templates/></td>
+    </tr>
+  </xsl:template>
+
+  <xsl:template match="changelog/design">
+    <tr>
+      <xsl:variable name="src"><xsl:value-of select="$relative-path"/>/images/design.gif</xsl:variable>
+      <td valign="top"><img alt="design" class="icon" src="{$src}"/></td>
+      <td><xsl:apply-templates/></td>
+    </tr>
+  </xsl:template>
+
+  <xsl:template match="changelog/docs">
+    <tr>
+      <xsl:variable name="src"><xsl:value-of select="$relative-path"/>/images/docs.gif</xsl:variable>
+      <td valign="top"><img alt="docs" class="icon" src="{$src}"/></td>
+      <td><xsl:apply-templates/></td>
+    </tr>
+  </xsl:template>
+
+  <xsl:template match="changelog/fix">
+    <tr>
+      <xsl:variable name="src"><xsl:value-of select="$relative-path"/>/images/fix.gif</xsl:variable>
+      <td valign="top"><img alt="fix" class="icon" src="{$src}"/></td>
+      <td><xsl:apply-templates/></td>
+    </tr>
+  </xsl:template>
+
+  <xsl:template match="changelog/scode">
+    <tr>
+      <xsl:variable name="src"><xsl:value-of select="$relative-path"/>/images/code.gif</xsl:variable>
+      <td valign="top"><img alt="code" class="icon" src="{$src}"/></td>
+      <td><xsl:apply-templates/></td>
+    </tr>
+  </xsl:template>
+
+  <!-- Process an attributes list with nested attribute elements -->
+  <xsl:template match="status">
+    <table border="1" cellpadding="5">
+      <tr>
+        <th width="15%" bgcolor="{$attributes-color}">
+          <font color="#ffffff">Priority</font>
+        </th>
+        <th width="50%" bgcolor="{$attributes-color}">
+          <font color="#ffffff">Action Item</font>
+        </th>
+        <th width="25%" bgcolor="{$attributes-color}">
+          <font color="#ffffff">Volunteers</font>
+        </th>
+        <xsl:for-each select="item">
+        <tr>
+          <td align="left" valign="center">
+            <xsl:value-of select="@priority"/>
+          </td>
+          <td align="left" valign="center">
+            <xsl:apply-templates/>
+          </td>
+          <td align="left" valign="center">
+            <xsl:value-of select="@owner"/>
+          </td>
+        </tr>
+        </xsl:for-each>
+      </tr>
+    </table>
+  </xsl:template>
+
+  <!-- Link to a bug report -->
+  <xsl:template match="bug">
+      <xsl:variable name="link"><xsl:value-of select="$buglink"/><xsl:value-of select="text()"/></xsl:variable>
+      <a href="{$link}"><xsl:apply-templates/></a>
+  </xsl:template>
+
+
+  <xsl:template match="code">
+    <b class="code"><xsl:apply-templates select="text()"/></b>
+  </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>
+ 
+  <!-- Screens -->
+
+  <xsl:template match="screen">
+    <p class="screen">
+      <div align="left">
+        <table width="80%" border="1" cellspacing="0" cellpadding="2" bgcolor="#000000">
+          <tr>
+            <td bgcolor="#000000" align="left">
+              <xsl:apply-templates select="note|wait|type|typedos|type5250|typenext|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="typedos">
+    <code>
+      <nobr>
+        <em class="screen">
+          <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="screen"><xsl:value-of select="text()"/></b>
+        </xsl:if>
+      </nobr>
+    </code>
+    <br/>
+  </xsl:template>
+ 
+  <xsl:template match="type5250">
+    <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="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>
+ 
+  
+
+  <!-- Process everything else by just passing it through -->
+  <xsl:template match="*|@*">
+    <xsl:copy>
+      <xsl:apply-templates select="@*|*|text()"/>
+    </xsl:copy>
+  </xsl:template>
+
+</xsl:stylesheet>
diff --git a/connectors/jk/xdocs/webserver_howto/apache.xml b/connectors/jk/xdocs/webserver_howto/apache.xml
new file mode 100644
index 0000000..224ad11
--- /dev/null
+++ b/connectors/jk/xdocs/webserver_howto/apache.xml
@@ -0,0 +1,1224 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="apache.html">
+
+  &project;
+<copyright>
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT 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>
+<body>
+<section name="Introduction">
+<p>
+This document explains how to connect Tomcat to the popular open source web server, Apache.
+There is actually three versions of Apache, 1.3, 2.0 and 2.2 and all can be used with mod_jk,
+the Tomcat redirector module.
+</p>
+
+<p>
+It is recommended that you also read the <a href="workers.html">Workers HowTo</a> document
+to learn how to setup the working entities between your web server and Tomcat Engines.
+For more detailed configuration information consult the Reference Guide for
+<a href="../reference/worker.html">workers.properties</a>,
+<a href="../reference/uriworkermap.html">uriworkermap</a>
+and <a href="../reference/apache.html">Apache</a>.
+</p>
+
+<p><b>Waring: If Apache httpd and Tomcat are configured to serve content from
+the same filing system location then care must be taken to ensure that httpd is
+not able to serve inappropriate content such as the contents of the WEB-INF
+directory or JSP source code.</b> This could occur if the httpd DocumentRoot
+overlaps with a Tomcat Host's appBase or the docBase of any Context. It could
+also occur when using the httpd Alias directive with a Tomcat Host's appBase or
+the docBase of any Context.
+</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="../generic_howto/workers.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, Solaris and should works on major Unixes platforms
+supporting Apache 1.3 and/or 2.0/2.2
+</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>
+i5/OS V5R4 (System I) with Apache 2.0.58. 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, Tomcat 5.0.x, Tomcat 5.5.x and Tomcat 6.
+</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.x, 5 and 5.5 and Tomcat 6.
+</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://tomcat.apache.org/download-connectors.cgi">
+here</a>
+</p>
+
+<p>
+The binaries for mod_jk are now available for several platforms.
+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.x can be downloaded from a mirror
+<a href="http://tomcat.apache.org/download-connectors.cgi">
+here</a> (look for JK 1.2 Binary Releases). The "JK 1.2 Binary Releases" link 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.nlm or
+or MOD_JK.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 auto-configure works only for a single Tomcat running on the same machine where Apache (httpd) is running.
+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>
+
+<source>
+    #To be added at the end of your httpd.conf
+    Include $TOMCAT_HOME/conf/jk/mod_jk.conf-auto
+</source>
+
+<p>
+Note: this file may also be generated as $TOMCAT_HOME/conf/auto/mod_jk.conf
+</p>
+
+<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 by creating your workers.properties file at
+$TOMCAT_HOME/conf/jk/workers.properties and adding the listener to the Engine
+element in the server.xml file as per the following example.
+<b>Please note that this example is specific to Tomcat 5.x, unlike other sections of this document
+   which also apply to previous Tomcat branches.</b>
+</p>
+<source>
+  ...
+  &lt;Engine ...&gt;
+    ...
+    &lt;Listener className="org.apache.jk.config.ApacheConfig" modJk="/path/to/mod_jk.so" /&gt;
+    ...
+  &lt;/Engine&gt;
+  ...
+</source>
+
+<p>
+Then restart Tomcat and mod_jk.conf should be generated. For more information on
+this topic, please refer to the API documentation at the
+<a href="http://tomcat.apache.org/tomcat-5.5-doc/catalina/docs/api/org/apache/jk/config/ApacheConfig.html">
+Tomcat docs website</a>.
+</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 web server,
+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>
+
+<source>
+    # Load mod_jk module
+    LoadModule    jk_module  libexec/mod_jk.so
+    # Declare the module for &lt;IfModule directive&gt; (remove this line on Apache 2.0.x)
+    AddModule     mod_jk.c
+    # Where to find workers.properties
+    JkWorkersFile /etc/httpd/conf/workers.properties
+    # Where to put jk shared memory
+    JkShmFile     /var/log/httpd/mod_jk.shm
+    # Where to put jk logs
+    JkLogFile     /var/log/httpd/mod_jk.log
+    # Set the jk log level [debug/error/info]
+    JkLogLevel    info
+    # Select the timestamp log format
+    JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "
+    # Send servlet for context /examples to worker named worker1
+    JkMount  /examples/servlet/* worker1
+    # Send JSPs  for context /examples to worker named worker1
+    JkMount  /examples/*.jsp worker1
+</source>
+
+</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.
+
+<source>
+  JkWorkersFile     /etc/httpd/conf/workers.properties
+</source>
+
+<br/>
+<br/>
+</p>
+</subsection>
+
+<subsection name="Logging">
+<p>
+<b>JkLogFile</b> specify the location where mod_jk is going to place its log file.
+</p>
+
+<source>
+  JkLogFile     /var/log/httpd/mod_jk.log
+</source>
+
+<p>
+Since JK 1.2.3 for Apache 2.0/2.2 and JK 1.2.16 for Apache 1.3 this can also
+be used for piped logging:
+</p>
+
+<source>
+  JkLogFile     "|/usr/bin/rotatelogs /var/log/httpd/mod_jk.log 86400"
+</source>
+
+<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>
+
+<source>
+  JkLogLevel    info
+</source>
+
+<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>
+
+<source>
+  JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "
+</source>
+
+<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>
+  <tr><td>%R</td><td>Session route name (available with 1.2.19 and up)</td></tr>
+</table>
+
+<source>
+  JkRequestLogFormat     "%w %V %T"
+</source>
+
+<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. Without any leading signs, options will be enabled.
+<br/>
+<br/>
+</p>
+
+<p>
+The three following options <b>+ForwardURIxxx</b> are mutually exclusive.
+Exactly one of them is required, a negative sign prefix is not allowed with them.
+The default value is "ForwardURICompatUnparsed" since version 1.2.23.
+Until version 1.2.22 the default value was "ForwardURICompat".
+You can turn the default off by switching on one of the other two options.
+<br/>
+<br/>
+</p>
+
+<p>
+All options are inherited from the global server to virtual hosts.
+Options that support enabling (plus options) and disabling (minus options),
+are inherited in the following way:
+<br/>
+<br/>
+options(vhost) = plus_options(global) - minus_options(global) + plus_options(vhost) - minus_options(vhost)
+<br/>
+<br/>
+</p>
+
+<p>
+Using JkOptions <b>ForwardURICompatUnparsed</b>, the forwarded URI
+will be unparsed. It's spec compliant and also the safest option.
+It will always forward the original request URI, so rewriting
+URIs with mod_rewrite and then forwarding the rewritten URI
+will not work.
+
+<source>
+  JkOptions     +ForwardURICompatUnparsed
+</source>
+
+<br/>
+<br/>
+</p>
+<p>
+Using JkOptions <b>ForwardURICompat</b>, the forwarded URI will
+be decoded by Apache httpd. Encoded characters will be decoded and
+explicit path components like ".." will already be resolved.
+This is less spec compliant and is <b>not safe</b> if you are using
+prefix JkMount. This option will allow to rewrite URIs with
+mod_rewrite before forwarding.
+
+<source>
+  JkOptions     +ForwardURICompat
+</source>
+
+<br/>
+<br/>
+</p>
+<p>
+Using JkOptions <b>ForwardURIEscaped</b>, the forwarded URI will
+be the encoded form of the URI used by ForwardURICompat.
+Explicit path components like ".." will already be resolved.
+This will not work in combination with URL encoded session IDs,
+but it will allow to rewrite URIs with mod_rewrite before forwarding.
+
+<source>
+  JkOptions     +ForwardURIEscaped
+</source>
+
+<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.
+
+<source>
+  JkOptions     +ForwardDirectories
+</source>
+<br/>
+<br/>
+</p>
+
+<p>
+JkOptions <b>ForwardLocalAddress</b>, you ask mod_jk to send the local address,
+of the Apache web server instead remote client address. This can be used by
+Tomcat remote address valve for allowing connections only from registered Apache
+web servers.
+
+<source>
+  JkOptions     +ForwardLocalAddress
+</source>
+
+<br/>
+<br/>
+</p>
+
+<p>
+JkOptions <b>FlushPackets</b>, you ask mod_jk to flush Apache's connection
+buffer after each AJP packet chunk received from Tomcat. This option can have
+a strong performance penalty for Apache and Tomcat as writes are performed
+more often than would normally be required (ie: at the end of each
+response).
+
+<source>
+  JkOptions     +FlushPackets
+</source>
+
+<br/>
+<br/>
+</p>
+
+<p>
+JkOptions <b>FlushHeader</b>, you ask mod_jk to flush Apache's connection
+buffer after the response headers have been  received from Tomcat.
+
+<source>
+  JkOptions     +FlushHeader
+</source>
+
+<br/>
+<br/>
+</p>
+
+<p>
+JkOptions <b>DisableReuse</b>, you ask mod_jk to close connections immediately
+after their use. Normally mod_jk uses persistent connections and pools idle
+connections to reuse them, when new requests have to be sent to Tomcat.
+</p>
+
+<p>
+Using this option will have a strong performance penalty for Apache and Tomcat.
+Use this only as a last resort in case of unfixable network problems.
+If a firewall between Apache and Tomcat silently kills idle connections,
+try to use the worker attribute socket_keepalive in combination with an appropriate
+TCP keepalive value in your OS.
+
+<source>
+  JkOptions     +DisableReuse
+</source>
+
+<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 (off by default).
+
+<source>
+  JkOptions     +ForwardKeySize
+</source>
+
+<br/>
+<br/>
+</p>
+
+<p>
+JkOptions <b>ForwardSSLCertChain</b>,  you ask mod_jk, when using ajp13,
+to forward SSL certificate chain (off by default).
+Mod_jk only passes the <code>SSL_CLIENT_CERT</code> to the AJP connector. This is not a
+problem with self-signed certificates or certificates directly signed by the
+root CA certificate. However, there's a large number of certificates signed by
+an intermediate CA certificate, where this is a significant problem: A servlet
+will not have the possibility to validate the client certificate on its own. The
+bug would be fixed by passing on the <code>SSL_CLIENT_CERT_CHAIN</code> to Tomcat via the AJP connector.
+<br/>
+This directive exists only since version 1.2.22.
+<source>
+  JkOptions     +ForwardSSLCertChain
+</source>
+
+<br/>
+<br/>
+</p>
+
+<p>
+The directive <b>JkEnvVar</b> allows you to forward environment variables from Apache server to Tomcat engine.
+The variables can be retrieved on the Tomcat side as request attributes.
+You can add a default value as a second parameter to the directive.
+If the default value is not given explicitely, the variable
+will only be send, if it is set during runtime.
+<br/>
+<br/>
+The variables are inherited from the global server to virtual hosts.
+
+<source>
+  JkEnvVar     SSL_CLIENT_V_START     undefined
+</source>
+<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>
+
+<source>
+  # send all requests ending in .jsp to worker1
+  JkMount /*.jsp worker1
+  # send all requests ending /servlet to worker1
+  JkMount /*/servlet/ worker1
+  # send all requests jsp requests to files located in /otherworker will go worker2
+  JkMount /otherworker/*.jsp worker2
+</source>
+
+<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: For security reasons is is strongly recommended that JkMount is used to
+pass all requests to Tomcat by default and JkUnMount is used to explicitly
+exclude static content to be served by httpd. It should also be noted that
+content served by httpd will bypass any security constraints defined in the
+applciation's web.xml.
+</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>
+
+<source>
+  # Static files in the examples webapp are served by apache
+  Alias /examples /vat/tomcat3/webapps/examples
+  # All requests go to worker1 by default
+  JkMount /* worker1
+  # Serve html, jpg and gif using httpd
+  JkUnMount /*.html worker1
+  JkUnMount /*.jpg  worker1
+  JkUnMount /*.gif  worker1
+</source>
+
+<p>
+Starting with mod_jk 1.2.6 for Apache 2.0/2.2 and 1.2.19 for Apache 1.3, 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>
+
+<source>
+  # All URL goes to tomcat except the one containing /home
+  &lt;VirtualHost *:80&gt;
+      ServerName testxxx.mysys
+      DocumentRoot /www/testxxx/htdocs
+
+  # Use SetEnvIf to st no-jk when /home/ is encountered
+      SetEnvIf Request_URI "/home/*" no-jk
+
+  # Now /home will goes to /home/dataxxx/
+      Alias /home /home/dataxxx/
+
+      &lt;Directory "/home/dataxxx"&gt;
+          Options Indexes MultiViews
+          AllowOverride None
+          Order allow,deny
+          Allow from all
+      &lt;/Directory&gt;
+
+      JkMount /* myssys-xxx
+
+  &lt;/VirtualHost&gt;
+</source>
+
+
+<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>
+
+<source>
+  # Static files in all Tomcat webapp context directories are served by apache
+  JkAutoAlias /var/tomcat3/webapps
+
+  # All requests go to worker1 by default
+  JkMount /* ajp13
+  # Serve html, jpg and gif using httpd
+  JkUnMount /*.html ajp13
+  JkUnMount /*.jpg  ajp13
+  JkUnMount /*.gif  ajp13
+</source>
+
+<p>
+If you encoded all your URLs to contain the session id
+(<code>;jsessionid=...</code>), and you later decide, you want to
+move part of the content to Apache httpd, you can tell
+mod_jk to strip off all session ids from URLs for
+those requests, that do not get forwarded via mod_jk.
+</p>
+
+<p>
+You enable this feature by setting JkStripSession to On.
+It can be enabled individually for virtual servers. The default
+value is Off.
+</p>
+
+</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 subversion">
+In case you get source from subversion, ie without an existing configure script,
+you should have autoconf for configuration and installation.
+<p>
+To create tomcat-connectors's autoconf script, you will need libtool 1.5.2 or higher,
+and autoconf 2.59 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] [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 different version of Apache httpd, like 1.3, 2.0 and 2.2,
+you need to go through the full build process for each of them.
+Please note, that httpd 2.0 and 2.2 modules are <b>not</b> compatible. The mod_jk directory
+used is "apache-2.0" in both cases, but you need to compile separately.
+<ul>
+<li>
+use configure and indicate the correct Apache httpd apxs location (--with-apxs)
+</li>
+<li>
+use make
+</li>
+<li>
+copy the resulting mod_jk.so binary from the apache-1.3 or apache-2.0 subdirectory
+to the Apache httpd modules location.
+</li>
+<li>
+make clean (to remove all previously compiled object files)
+</li>
+<li>
+Start over with the apxs location for your next Apache httpd version.
+</li>
+</ul>
+
+</p>
+</subsection>
+
+<subsection name="configure arguments">
+<p>
+<table>
+  <tr valign="top"><th>Apache related parameters</th><th></th></tr>
+  <tr valign="top">
+  <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/2.2 and 1.3)</td>
+  </tr>
+  <tr valign="top"><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 valign="top"><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>
+  <tr valign="top"><td>--enable-prefork</td>
+  <td>
+In case you build mod_jk for a multi-threaded Apache httpd 2.0/2.2 MPM (Multi-Processing Module),
+some areas of mod_jk code need to be synchronized to make it thread-safe.
+Because configure can not easily detect, whether your are using a multi-threaded MPM,
+mod_jk by default is always build thread-safe for Apache httpd 2.0/2.2.
+If you are sure, that your MPM is not multi-threaded, you can use "--enable-prefork"
+to force the removal of the synchronization code (thus increasing performance a bit).
+For instance, the prefork MPM is not multi-threaded. For Apache httpd 1.3
+this flag will be set automatically.</td>
+</tr>
+  <tr valign="top"><td>--enable-flock</td>
+  <td>
+In case the operating system supports flock system call use this flag to enable this
+faster locks that are implemented as system call instead emulated by GNU C library.<br/>
+However those locks does not work on NFS mounted volumes, so you can use
+"--enable-flock" during compile time to force the flocks() calls.</td>
+</tr>
+
+</table>
+<br/>
+<table>
+  <tr valign="top"><th>JNI related parameters</th><th></th></tr>
+  <tr valign="top"><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 valign="top"><td>--with-java-home=DIR</td>
+  <td>DIR is the  patch to the JDK root directory. Something like: /opt/java/jdk12</td>
+  </tr>
+  <tr valign="top"><td>--with-os-type=SUBDIR</td><td>SUBDIR is the os-type subdirectory,
+  configure should guess it correctly.</td>
+  </tr>
+  <tr valign="top"><td>--with-arch-type=SUBDIR</td><td>SUBDIR is the arch subdirectory,
+  configure should guess it correctly.</td>
+  </tr>
+  <tr valign="top"><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/2.2 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/2.2 build with JNI support</note>
+<type>./configure --with-apxs2=/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.so to Apache's modules directory.
+</li>
+</ul>
+<p>
+An example on how to build mod_jk for Apache 1.3:
+</p>
+<screen>
+<note>Set location for Apache 1.3 sources</note>
+<typedos>set APACHE1_HOME=c:\apache13</typedos>
+<note>Change directory to the mod_jk module for Apache 1.3</note>
+<typedos>cd c:\home\apache\jk\native\apache-1.3</typedos>
+<note>Build the sources using MSDEV</note>
+<typedos>MSDEV mod_jk.dsp /MAKE ALL</typedos>
+<note>Copy the dll to your apache modules directory</note>
+<typedos>cp release\mod_jk.so c:\apache13\modules\</typedos>
+</screen>
+
+<p>
+An example on how to build mod_jk for Apache 2.0:
+</p>
+<screen>
+<note>Set location for Apache 2.0 sources</note>
+<typedos>set APACHE2_HOME=c:\apache20</typedos>
+<note>Change directory to the mod_jk module for Apache 2.0</note>
+<typedos>cd c:\home\apache\jk\native\apache-2.0</typedos>
+<note>Build the sources using MSDEV</note>
+<typedos>MSDEV mod_jk.dsp /MAKE ALL</typedos>
+<note>Copy the dll to your apache modules directory</note>
+<typedos>cp release\mod_jk.so c:\apache20\modules\</typedos>
+</screen>
+
+<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 System I - i5/OS (OS400)">
+<p>
+Since OS400 V4R5, System I (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 System I thanks to the help of the IBM
+Rochester Labs which has provided information and patches to adapt mod_jk to i5/OS.
+</p>
+<p>
+You should have at least Apache 2.0.58 (product 5722DG1), a C Compiler and IFS.
+Apache 2.0.58 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>
+The all latest Apache 2 for i5/OS V5R3 (or V5R4) is now 2.0.58 (as of 2007/04/17).
+Be sure to have the latest PTFs loaded if you want to make use of jk 1.2.15 and higher.
+NB: The latest mod_jk known to work on i5/OS V5R3 was 1.2.19.
+</p>
+<p>
+New in i5/OS V5R4, UTF is required, also for Apache modules, as such Apache modules do not require
+translations to/from EBCDIC but works should be done to port mod_jk 1.2.23 (and higher) to V5R4.
+
+From the V5R4 Infocenter :
+
+As of i5/OS(tm) V5R4, modules must be recompiled with a UTF locale. This creates an environment where locale-dependent C runtime functions assume
+that string data is encoded in UTF-8. Any hardcoded constants can be encoded in UTF-8 by adding a #pragma convert(1208) statement in the module.
+Additionally, input data from the client will no longer be converted to EBCDIC but will be passed as-is.
+Output data sent from the module is not converted either so it must be encoded in ASCII or UTF8 as required.
+APR and HTTP APIs as of V5R4, expect data in UTF-8. Note that several APIs have additional functions that allow a CCSID to be set to
+indicate the encoding of the parameters being passed. Conversion functions between UTF-8 and EBCDIC have been added.
+Be sure to review APIs used by your module to be aware of current changes.
+
+</p>
+<p>
+To configure mod_jk on System I 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 System I directory via FTP.
+</li>
+<li>
+Then go to the System I command line :
+</li>
+</ul>
+<screen>
+<note>Create mod_jk library</note>
+<type5250>CRTLIB MOD_JK TEXT(‘Apache mod'jk tomcat connector module')</type5250>
+<note>Create service program source file</note>
+<type5250>CRTSRCPF MOD_JK/QSRVSRC TEXT(‘Service program source file’)</type5250>
+<note>Create the CL build program source file</note>
+<type5250>CRTSRCPF FILE(MOD_JK/QCLSRC) TEXT(‘Build program source file’)</type5250>
+<note>Edit the service program source file</note>
+<type5250>STRSEU MOD_JK/QSRVSRC MOD_JK</type5250>
+</screen>
+<p>
+In the edited file, specify that only jk_module should be exported :
+<screen>
+<note> Columns   . . :    1  71     Edit                               MOD_JK/QSRVSRC </note>
+<note> SEU==>                                                                  MOD_JK </note>
+<note>        *************** Beginning of data ************************************* </note>
+<note>0001.00 STRPGMEXP PGMLVL(*CURRENT)                                              </note>
+<note>0002.00 EXPORT SYMBOL("jk_module")                                              </note>
+<note>0003.00 ENDPGMEXP                                                               </note>
+<note>        ****************** End of data **************************************** </note>
+</screen>
+</p>
+<p>
+You could start to build all the modules of mod_jk (cases for V5R4 or previous releases):
+</p>
+<screen>
+<note>Copy the CL build program source for i5/OS before V5R4 from IFS</note>
+<type5250>CPYFRMSTMF FROMSTMF('/home/apache/jk/native/apache-2.0/bldjk.qclsrc') +</type5250>
+<note>TOMBR('/QSYS.LIB/MOD_JK.LIB/QCLSRC.FILE/BLDJK.MBR') MBROPT(*REPLACE)</note>
+<note>Build the CL build program</note>
+<type5250>CRTCLPGM PGM(MOD_JK/BLDJK) SRCFILE(MOD_JK/QCLSRC) TEXT('Apache mod_jk build program')</type5250>
+<note>Launch the build</note>
+<type5250>CALL MOD_JK/BLDJK</type5250><br/>
+<note>If the build if successfull, copy the new mod_jk module</note>
+<type5250>CRTDUPOBJ OBJ(MOD_JK) FROMLIB(MOD_JK) OBJTYPE(*SRVPGM) TOLIB(QHTTPSVR) NEWOBJ(MOD_JK)</type5250>
+</screen>
+<screen>
+<note>Copy the CL build program source for i5/OS V5R4 from IFS</note>
+<type5250>CPYFRMSTMF FROMSTMF('/home/apache/jk/native/apache-2.0/bldjk54.qclsrc') +</type5250>
+<note>TOMBR('/QSYS.LIB/MOD_JK.LIB/QCLSRC.FILE/BLDJK54.MBR') MBROPT(*REPLACE)</note>
+<note>Build the CL build program for i5/OS V5R4</note>
+<type5250>CRTCLPGM PGM(MOD_JK/BLDJK54) SRCFILE(MOD_JK/QCLSRC) TEXT('Apache mod_jk build program') TGTRLS(*CURRENT)</type5250>
+<note>Launch the build for i5/OS V5R4</note>
+<type5250>CALL MOD_JK/BLDJK54</type5250><br/>
+<note>If the build if successfull, copy the new mod_jk module</note>
+<type5250>CRTDUPOBJ OBJ(MOD_JK) FROMLIB(MOD_JK) OBJTYPE(*SRVPGM) TOLIB(QHTTPSVR) NEWOBJ(MOD_JK)</type5250>
+</screen>
+<p>
+Next, you should restart your Apache 2.0 instance and enjoy this piece of OpenSource on System I.
+</p>
+<screen>
+<note>ENDTCPSVR SERVER(*HTTP) HTTPSVR(MYSERVER)</note>
+<note>STRTCPSVR SERVER(*HTTP) HTTPSVR(MYSERVER)</note>
+</screen>
+</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>
+</body>
+</document>
diff --git a/connectors/jk/xdocs/webserver_howto/iis.xml b/connectors/jk/xdocs/webserver_howto/iis.xml
new file mode 100644
index 0000000..265c787
--- /dev/null
+++ b/connectors/jk/xdocs/webserver_howto/iis.xml
@@ -0,0 +1,713 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="iis.html">
+
+  &project;
+<copyright>
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT 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>
+<body>
+<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 recommended that you also read the <a href="workers.html">Workers HowTo</a> document
+to learn how to setup the working entities between your web server and Tomcat Engines.
+For more detailed configuration information consult the Reference Guide for
+<a href="../reference/worker.html">workers.properties</a>,
+<a href="../reference/uriworkermap.html">uriworkermap</a>
+and <a href="../reference/iis.html">IIS</a>.
+</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:\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, Tomcat 5 and 5.5 and Tomcat 6
+</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/tomcat/Tomcat/Links">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, 5 and 5.5 and Tomcat 6.
+</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 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 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><a href="../reference/workers.html">workers.properties</a></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><a href="../reference/uriworkermap.html">uriworkermap.properties</a></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:\tomcat\bin\win32\i386\isapi_redirect.dll</b> and 
+that you created the properties files are in <b>c:\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:\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:\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:\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:\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 tomcat), 
+its executable must be our c:\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 tomcat 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>
+
+<source>
+  /context/*=worker_name
+</source>
+
+<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>
+
+<source>
+  /shop/*=defworker
+</source>
+
+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:\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 : 
+
+<source>
+  For the examples context it requires to replace the following line
+  /examples/*=defworker
+  with the following two lines
+  /examples/*.jsp=defworker
+  /examples/servlet/*=defworker
+</source>
+</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:
+
+<source>
+  /example/servletname=defworker
+</source>
+</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>
+<source>
+  # An entry that lists all the workers defined
+  worker.list=worker1, worker2
+  # Entries that define the host and port associated with these workers
+  worker.worker1.host=localhost
+  worker.worker1.port=8009
+  worker.worker1.type=ajp13
+  worker.worker2.host=otherhost
+  worker.worker2.port=8009
+  worker.worker2.type=ajp13
+</source>
+</p>
+
+<p>
+The above example defined two workers, now we can use these workers to serve two different contexts 
+each with its own worker: 
+<source>
+  example uriworkermap.properties fragment
+  /examples/*=worker1
+  /webpages/*=worker2
+</source>
+</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="../generic_howto/workers.html">Workers HowTo</a>
+and in the <a href="../reference/workers.html">worker.properties configuration reference</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>
+<screen>
+<note>Change directory to the isapi plugins source directory</note>
+<typedos>cd c:\home\apache\jk\iis</typedos>
+<note>Build the sources using MSDEV</note>
+<typedos>MSDEV isapi.dsp /MAKE ALL</typedos>
+</screen>
+</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>
+<source>
+  GET "/examples/jsp/index.html HTTP/1.1" 404
+</source>
+<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>
+
+<source>
+  GET "/jakarta/isapi_redirect.dll HTTP1.1"
+</source>
+
+<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>
+
+<source>
+  Error 404
+  GET "/..." 404
+</source>
+
+<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>
+
+<source>
+  Error 500
+  GET "/..." 500
+</source>
+
+<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>
+
+<source>
+  Error 200 or 403
+  GET "/..." 200
+  GET "/..." 403
+</source>
+
+<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 tomcat 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>
+
+<source>
+  Error 404
+  GET "/..." 404
+</source>
+
+<ul>
+<li>
+Make sure you entered the URL correctly.
+</li>
+</ul>
+
+<source>
+  Error 500
+  GET "/..." 500
+</source>
+
+<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>
+
+<source>
+  Error 200 or 403
+  GET "/..." 200
+  GET "/..." 403
+</source>
+
+<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>
+</body>
+</document>
diff --git a/connectors/jk/xdocs/webserver_howto/nes.xml b/connectors/jk/xdocs/webserver_howto/nes.xml
new file mode 100644
index 0000000..9770401
--- /dev/null
+++ b/connectors/jk/xdocs/webserver_howto/nes.xml
@@ -0,0 +1,487 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE document [
+  <!ENTITY project SYSTEM "project.xml">
+]>
+<document url="nes.html">
+
+  &project;
+<copyright>
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT 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>SunOne -- Netscape/iPlanet HowTo</title>
+<author email="hgomez@apache.org">Henri Gomez</author>
+<author email="jim@apache.org">Jim Jagielski</author>
+<author email="shachor@il.ibm.com">Gal Shachor</author>
+<author email="mturk@apache.org">Mladen Turk</author>
+<date>$Date$</date>
+</properties>
+<body>
+<section name="Introduction">
+<p>
+This document explains how to set up Sun ONE Web Server previously known as
+Netscape web servers to cooperate with Tomcat. 
+</p>
+
+<p>
+Normally the Sun ONE Web Servers come with their own Servlet engine, 
+but you can also configure them to send servlet and JSP requests to Tomcat 
+using the NSAPI redirector plugin.
+</p>
+
+<p>
+It is recommended that you also read the <a href="../generic_howto/workers.html">Workers HowTo</a> document
+to learn how to setup the working entities between your web server 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:\tomcat</b>.
+A worker is defined to be a tomcat process that accepts work from the Sun ONE Web Server.
+</p>
+</subsection>
+
+
+<subsection name="Supported Configuration">
+<p>
+The NSAPI-Tomcat redirector was developed and tested on:
+<ul>
+<li>
+WINNT 2000/XP/2003 (should be able to work with other service packs) and some Unixes
+</li>
+<li>
+Sun ONE Web Server 6.1
+</li>
+<li>
+Tomcat 4.1.x , Tomcat 5.0.x and Tomcat 5.5.x
+</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, 5.0.x, 5.5.x and 6.
+</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 NSAPI-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 NSAPI redirector, nsapi_redirect.dll, may be available under 
+the win32/i386 directory of tomcat-connectors distribution. 
+For those using Netscape as your browser, try downloading a zip version of the file, if available. 
+
+You can also build a copy locally from the source present in tomcat-connectors distribution.
+
+
+The Tomcat redirector requires two entities:
+<ul>
+<li>
+nsapi_redirect.dll (Windows) -or- nsapi_redirector.so (Unix) - The NSAPI server plugin, either obtain a pre-built DLL/so 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 built in servlet support is working disable it.
+</li>
+<li>
+Add the redirector plugin into the Netscape server configuration. 
+Edit your server <b>magnus.conf</b> and add the following lines:
+</li>
+</ul>
+
+<source>
+  
+  Init fn="load-modules" funcs="jk_init,jk_service" shlib="c:/jk/lib/nsapi_redirect.dll" shlib_flags="(global|now)"
+  Init fn="jk_init" worker_file="c:/jk/conf/workers.properties" log_level="debug" log_file="c:/jk/logs/nsapi.log" shm_file="c:/jk/logs/jk_shm"
+</source>
+<ul>
+<li>
+Edit your server <b>obj.conf</b> and add the following lines:
+</li>
+</ul>
+<source>
+
+  
+  In the default object NameTrans section
+  &lt;Object name="default"&gt;
+      
+  NameTrans fn="assign-name" from="/servlets-examples(|/*)" name="jknsapi" 
+  NameTrans fn="assign-name" from="/jsp-examples(|/*)" name="jknsapi"
+  ....
+  &lt;/Object&gt;
+  
+  Create a new configuration object by adding the following lines to the end of the obj.conf file
+  
+  &lt;Object name="jknsapi"&gt;
+    ObjectType fn=force-type type=text/plain
+    Service fn="jk_service" method="*" worker="worker1"
+  &lt;/Object&gt;
+</source>
+
+<ul>
+<li>
+Restart Web Server (stop and start the server)
+</li>
+</ul>
+
+<p>
+That's all, now you should start tomcat and ask for http://server:port/servlets-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>
+
+<source>
+  NameTrans fn="assign-name" from="/&lt;context name&gt;/*" name="jknsapi"
+</source>
+
+<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:\tomcat\webapps\examples</b> directory. 
+</p>
+
+<p>
+To add a new virtual directory add the following line to your <b>obj.conf</b>:
+</p>
+
+<source>
+  NameTrans fn=pfx2dir from=/examples dir="c:/tomcat/webapps/examples"
+</source>
+
+<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>
+
+<source>
+  PathCheck fn="deny-existence" path="*/WEB-INF/*"
+  
+  This line instructs the Netscape server to reject any request with a URL that contain the path /WEB-INF/.
+</source>
+
+<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>
+
+<source>
+  For the examples context it requires to replace the following line:
+  
+  NameTrans fn="assign-name" from="/examples/*" name="jknsapi"
+  
+  with the following two lines:
+  
+  NameTrans fn="assign-name" from="/examples/jsp/*.jsp" name="jknsapi"
+  NameTrans fn="assign-name" from="/examples/servlet/*" name="jknsapi"
+</source>
+
+<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>
+
+<source>
+  NameTrans fn="assign-name" from="/examples/servletname" name="jknsapi"
+  
+  Instructs Netscape to assign the redirector request whose URL-Path equals /example/servletname
+</source>
+
+</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>
+
+<source>
+  #An entry that lists all the workers defined. For example:
+  worker.list=worker1,worker2
+  
+  # Entries that define the host and port associated with these workers.
+  worker.worker1.host=localhost
+  worker.worker1.port=8009
+  worker.worker1.type=ajp13
+
+  worker.worker2.host=otherhost
+  worker.worker2.port=8009
+  worker.worker2.type=ajp13
+</source>
+
+<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>
+
+<source>
+  &lt;Object name="jknsapi"&gt;
+    ObjectType fn=force-type type=text/plain
+    Service fn="jk_service" worker="worker1" path="/examples/*"
+    Service fn="jk_service" worker="worker2" path="/webpages/*"
+    Service fn="jk_service" worker="worker1"
+  &lt;/Object&gt;
+</source>
+
+<p>
+More informations on using and configuring workers in the <a href="../generic_howto/workers.html">Workers HowTo</a>
+and in the <a href="../reference/workers.html">worker.properties configuration reference</a>.
+
+</p>
+</subsection>
+
+</section>
+
+<section name="Building NSAPI DLL redirector for Windows">
+<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>
+<section name="Building NSAPI so plugin redirector for Unix">
+<p>
+The redirector requires either gcc or the native Solaris cc compiler.
+
+The steps that you need to take are:
+<ul>
+<li>
+Change directory to the nsapi plugins source directory (src/native).
+</li>
+<li>
+configure for Netscape/iPlanet/SunONE webserver.
+</li>
+<li>
+Change directory to the nsapi netscape directory (./netstape).
+</li>
+<li>
+Edit <b>Makefile.solaris</b> and update the SUITSPOT_HOME and JAVE_HOME path to reflect your own Netscape server installation.
+</li>
+<li>
+Make the source with gmake.
+</li>
+</ul>
+<screendos>
+<notedos>Change directory to the nsapi plugins source directory</notedos>
+<typedos>cd /usr/local/src/tomcat-connectors-xxx-src/native</typedos>
+<notedos>configure for Netscape/iPlanet/SunONE webserver</notedos>
+<typedos>./configure --enable-netscape</typedos>
+<notedos>Change directory to the nsapi netscape directory</notedos>
+<typedos>cd netscape</typedos>
+<notedos>Edit Makefile.solaris</notedos>
+<typedos>vi Makefile.solaris</typedos>
+<notedos>Make the source with gmake</notedos>
+<typedos>gmake -f Makefile.solaris</typedos>
+</screendos>
+</p>
+<p>
+After the build, you will have the required nsapi_redirector.so plugin.
+</p>
+</section>
+</body>
+</document>
diff --git a/connectors/jk/xdocs/webserver_howto/project.xml b/connectors/jk/xdocs/webserver_howto/project.xml
new file mode 100644
index 0000000..30a6fe5
--- /dev/null
+++ b/connectors/jk/xdocs/webserver_howto/project.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<project name="Apache Tomcat Connector Documentation - Webserver HowTo"
+        href="http://tomcat.apache.org/">
+
+    <title>The Apache Tomcat Connector - Webserver HowTo</title>
+
+    <logo href="/images/tomcat.gif">
+      The Apache Tomcat Connector - Webserver HowTo
+    </logo>
+<body>
+
+    <menu name="Links">
+        <item name="Docs Home"                  href="../index.html"/>
+    </menu>
+
+    <menu name="Reference Guide">
+        <item name="workers.properties"         href="../reference/workers.html"/>
+        <item name="uriworkermap.properties"    href="../reference/uriworkermap.html"/>
+        <item name="Status Worker"              href="../reference/status.html"/>
+        <item name="Apache"                     href="../reference/apache.html"/>
+        <item name="IIS"                        href="../reference/iis.html"/>
+    </menu>
+
+    <menu name="Generic HowTo">
+        <item name="For the impatient"          href="../generic_howto/quick.html"/>
+        <item name="All about workers"          href="../generic_howto/workers.html"/>
+        <item name="Load Balancing"             href="../generic_howto/loadbalancers.html"/>
+    </menu>
+
+    <menu name="Webserver HowTo">
+        <item name="Apache"                     href="../webserver_howto/apache.html"/>
+        <item name="IIS"                        href="../webserver_howto/iis.html"/>
+        <item name="Netscape/SunOne/Sun"        href="../webserver_howto/nes.html"/>
+    </menu>
+
+    <menu name="AJP Protocol Reference">
+        <item name="AJPv13"                     href="../ajp/ajpv13a.html"/>
+        <item name="AJPv13 Extension Proposal"  href="../ajp/ajpv13ext.html"/>
+    </menu>
+
+    <menu name="Miscellaneous Documentation">
+        <item name="Frequently asked questions" href="../miscellaneous/faq.html"/>
+        <item name="Changelog"                  href="../miscellaneous/changelog.html"/>
+        <item name="Current Native:JK bugs"     href="http://issues.apache.org/bugzilla/buglist.cgi?query_format=advanced&amp;short_desc_type=allwordssubstr&amp;short_desc=&amp;product=Tomcat+5&amp;component=Native%3AJK&amp;long_desc_type=substring&amp;long_desc=&amp;bug_file_loc_type=allwordssubstr&amp;bug_file_loc=&amp;keywords_type=allwords&amp;keywords=&amp;bug_status=NEW&amp;bug_status=ASSIGNED&amp;bug_status=REOPENED&amp;emailassigned_to1=1&amp;emailtype1=substring&amp;email1=&amp;emailassigned_to2=1&amp;emailreporter2=1&amp;emailcc2=1&amp;emailtype2=substring&amp;email2=&amp;bugidtype=include&amp;bug_id=&amp;votes=&amp;chfieldfrom=&amp;chfieldto=Now&amp;chfieldvalue=&amp;cmdtype=doit&amp;order=Reuse+same+sort+as+last+time&amp;field0-0-0=noop&amp;type0-0-0=noop&amp;value0-0-0="/>
+        <item name="Contribute documentation"   href="../miscellaneous/doccontrib.html"/>
+        <item name="JK Status Ant Tasks"        href="../miscellaneous/jkstatustasks.html"/>
+        <item name="Reporting Tools"            href="../miscellaneous/reporttools.html"/>
+        <item name="Old JK/JK2 documentation"   href="http://tomcat.apache.org/connectors-doc-archive/jk2/index.html"/>
+    </menu>
+
+    <menu name="News">
+        <item name="2007"                       href="../news/20070301.html"/>
+        <item name="2006"                       href="../news/20060101.html"/>
+        <item name="2005"                       href="../news/20050101.html"/>
+        <item name="2004"                       href="../news/20041100.html"/>
+    </menu>
+    
+</body>
+</project>
diff --git a/connectors/jk3/ROADMAP b/connectors/jk3/ROADMAP
new file mode 100644
index 0000000..fbea18d
--- /dev/null
+++ b/connectors/jk3/ROADMAP
@@ -0,0 +1,52 @@
+APACHE TOMCAT JK3 CONNECTOR ROADMAP                                   -*-text-*-
+
+ApacheCon EU 2007 DISCUSSION POINTS:
+
+    * Lets make next generation connector and software load balancer
+      for application servers.
+    * Lets put JK 1.2 in maintenance mode
+      - Stop adding things to the JK 1.2 and only fix bugs
+      - We'll still need to vote how to maintain this
+    * It should be APR based and supported platforms should be
+      - Apache Httpd 2+
+      - Microsoft IIS 5+ (Windows 2000 and up)
+      - Generic API for standalone based programs
+    * Java code will be Java5 based
+      - Java5 is mandatory
+      - Both server and client reference implementation
+        will be developed
+    * Scripting support for the Load Balancer
+      - Load balancer will have some sort of scripting
+        to be able to make rule based load balancers
+      - We still have to decide if we need a full-blown
+        scripting support, or a simple rule engine will do
+    * Extended AJP 1.3 protocol
+      - Keep AJP 1.3 backward compatible
+      - CPING/CPONG will be mandatory when establishing
+        new connections.
+      - Add connection close notification.
+        Done by extending CPING/CPONG
+      - See how we can have 64K+ headers
+        Idea is to treat them as POST data if the
+        header doesn't fit inside header AJP packet
+    * Make JK3 protocol independent
+      - Although developed with protocol independence in mind
+        JK 1.2 is practically unusable for non AJP 1.3 protocols.
+        JK3 should allow custom protocols like http or fastcgi
+    * Add persistence to the dynamic configuration
+      - Have a way to merge the static configuration with
+        modified dynamic configuration via jkstatus
+    * More powerful log framework
+      - Using hierarchical logger names with corresponding
+        log levels to be able to debug or trace only
+        appropriate parts of the code
+    * Add management thread for each child
+      - APR_HAS_THREADS presence will be mandatory
+      - For each child process in web server a management thread
+        will be created that will monitor state and dynamic
+        configuration.
+    * COMET support
+      - Finding our ways how to drop idle connections between web
+        server and backend
+      - Maybe transporting the backend connector events back to the web
+        server
diff --git a/connectors/jni/NOTICE.txt b/connectors/jni/NOTICE.txt
new file mode 100644
index 0000000..439eb83
--- /dev/null
+++ b/connectors/jni/NOTICE.txt
@@ -0,0 +1,3 @@
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org/).
+
diff --git a/connectors/jni/README.txt b/connectors/jni/README.txt
new file mode 100644
index 0000000..50a3746
--- /dev/null
+++ b/connectors/jni/README.txt
@@ -0,0 +1,18 @@
+This directory contains both the native and java-side code for
+Tomcat Native Library.
+
+Building
+========
+ant
+To build the native part see native/BUILDING
+
+Running the examples
+====================
+before running the examples you may have to set LD_LIBRARY_PATH, something like
+LD_LIBRARY_PATH=/opt/SMAWoIS/openssl/lib; export LD_LIBRARY_PATH
+1 - echo: (port in examples/org/apache/tomcat/jni/Echo.properties).
+    ant echo-example
+2 - ssl server:
+    (see parameters in ./examples/org/apache/tomcat/jni/SSL.properties)
+    The certificate and key should be in dist/classes/examples.
+    ant server-example
diff --git a/connectors/jni/build.properties.sample b/connectors/jni/build.properties.sample
new file mode 100644
index 0000000..42f8e92
--- /dev/null
+++ b/connectors/jni/build.properties.sample
@@ -0,0 +1,6 @@
+# The directory containing your binary distribution of JUnit,
+# version 3.8 or later
+junit.home = /usr/local/java/junit3.8.1
+
+# The pathname of the "junit.jar" JAR file
+junit.jar = ${junit.home}/junit.jar
diff --git a/connectors/jni/build.xml b/connectors/jni/build.xml
new file mode 100644
index 0000000..0b7283f
--- /dev/null
+++ b/connectors/jni/build.xml
@@ -0,0 +1,322 @@
+<?xml version="1.0"?>
+<!-- Build file for Tomcat Native -->
+<project name="Tomcat Native" default="compile" basedir=".">
+    <!-- Give user a chance to override without editing this file
+        (and without typing -D each time it compiles it
+    -->
+    <property file="${user.home}/.ant.properties" />
+    <property file="${user.home}/build.properties" />
+    <property file="build.properties" />
+    <property file=".ant.properties" />
+
+    <!-- Initialization properties -->
+    <property name="name" value="Tomcat Native"/>
+    <property name="title" value="Tomcat Native Library"/>
+    <property name="version" value="1.0.0"/>
+    <property name="version.number" value="100"/>
+    <property name="project" value="tomcat-native"/>
+    <property name="build.dir" value="./dist"/>
+    <property name="build.src" value="${build.dir}/src"/>
+    <property name="build.dest" value="${build.dir}/classes"/>
+    <property name="src.dir" value="."/>
+    <property name="final.name" value="${project}-${version}"/>
+    <property name="dist.root" value="./dist"/>
+    <property name="ant.home" value="."/>
+
+    <property name="debug" value="off"/>
+    <property name="optimize" value="on"/>
+    <property name="deprecation" value="on"/>
+
+    <property name="docs.src" value="./xdocs"/>
+    <property name="docs.dest" value="${dist.root}/doc"/>
+    <property name="docs.dest.print" value="${dist.root}/doc/printable"/>
+    <property name="test.runner" value="junit.textui.TestRunner"/>
+    <property name="test.dir" value="${build.dest}/test"/>
+    <property name="examples.dir" value="${build.dest}/examples"/>
+    <property name="junit.home" value="/usr/local/junit3.8"/>
+    <property name="junit.jar" value="${junit.home}/junit.jar"/>
+    <property name="tc.library.path" value="${basedir}/native/.libs"/>
+
+      <!-- The base directory for component sources -->
+      <property name="source.home"             value="java"/>
+
+    <!-- Build classpath -->
+    <path id="classpath">
+        <pathelement location="${build.dest}/java"/>
+    </path>
+
+    <!-- Test classpath -->
+    <path id="test.classpath">
+        <pathelement location="${build.dest}/java"/>
+        <pathelement location="${build.dest}/test"/>
+        <pathelement location="${junit.jar}"/>
+    </path>
+
+    <!-- Examples classpath -->
+    <path id="examples.classpath">
+        <pathelement location="${build.dest}/java"/>
+        <pathelement location="${build.dest}/examples"/>
+    </path>
+
+    <!-- =================================================================== -->
+    <!-- prints the environment                                              -->
+    <!-- =================================================================== -->
+    <target name="env">
+        <echo message="java.home = ${java.home}"/>
+        <echo message="user.home = ${user.home}"/>
+        <!--
+        <echo message="java.class.path = ${java.class.path}"/>
+        -->
+        <echo message="tc.library.path = ${tc.library.path}"/>
+        <echo message=""/>
+    </target>
+
+    <target name="prepare" depends="env">
+       <mkdir dir="${build.dir}"/>
+    </target>
+
+    <!-- =================================================================== -->
+    <!-- Creates the API documentation                                       -->
+    <!-- =================================================================== -->
+    <target name="javadocs" description="Java documentation">
+        <mkdir dir="${docs.dest}"/>
+        <mkdir dir="${docs.dest}/api"/>
+        <javadoc sourcepath="${build.src}/java"
+            destdir="${docs.dest}/api"
+            author="true"
+            version="true"
+            overview="${src.dir}/java/overview.html"
+            packagenames="org.apache.tomcat.*"
+            windowtitle="${title} (Version ${version})"
+            doctitle="&lt;h1&gt;${title} (Version ${version})&lt;/h1&gt;"
+            bottom="Copyright 2002-2004 The Apache Software Foundation.&lt;!--
+
+Licensed under the Apache License, Version 2.0 (the 'License');
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an 'AS IS' BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.--&gt;">
+            <classpath refid="classpath"/>
+        </javadoc>
+     </target>
+
+    <!-- =================================================================== -->
+    <!-- Cleans up the build directory                                       -->
+    <!-- =================================================================== -->
+    <target name="clean">
+        <delete dir="${build.dir}"/>
+    </target>
+
+    <!-- =================================================================== -->
+    <!-- Compiles the source directory                                       -->
+    <!-- =================================================================== -->
+    <target name="compile" depends="prepare">
+        <mkdir dir="${build.dest}"/>
+        <mkdir dir="${build.dest}/java"/>
+        <mkdir dir="${build.src}"/>
+        <mkdir dir="${build.src}/java"/>
+        <tstamp>
+            <format property="TODAY" pattern="MMM d yyyy" locale="en"/>
+            <format property="TSTAMP" pattern="hh:mm:ss"/>
+        </tstamp>
+        <!-- Copy static resource files -->
+        <filter token="VERSION" value="${version}"/>
+        <filter token="VERSION_NUMBER" value="${version.number}"/>
+        <filter token="VERSION_BUILT" value="${TODAY} ${TSTAMP}"/>
+        <copy todir="${build.src}/java" filtering="yes">
+            <fileset dir="${src.dir}/java">
+                <include name="**/*.java"/>
+                <include name="**/*.xml"/>
+                <include name="**/*.properties"/>
+            </fileset>
+        </copy>
+
+        <javac srcdir="${build.src}/java"
+            destdir="${build.dest}/java"
+            debug="${debug}"
+            deprecation="${deprecation}"
+            optimize="${optimize}">
+            <classpath refid="classpath"/>
+        </javac>
+        <copy todir="${build.dest}/java" filtering="yes">
+            <fileset dir="${build.src}/java">
+                <include name="**/*.xml"/>
+                <include name="**/*.properties"/>
+            </fileset>
+        </copy>
+    </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="classpath"/>
+        </javac>
+        <copy    todir="${build.home}/classes" filtering="on">
+          <fileset dir="${source.home}" excludes="**/*.java"/>
+        </copy>
+      </target>
+
+    <!-- =================================================================== -->
+    <!-- Compiles the examples directory                                     -->
+    <!-- =================================================================== -->
+    <target name="examples" depends="compile">
+        <mkdir dir="${build.dest}"/>
+        <mkdir dir="${build.dest}/examples"/>
+        <mkdir dir="${build.src}"/>
+        <mkdir dir="${build.src}/examples"/>
+        <tstamp>
+            <format property="TODAY" pattern="MMM d yyyy" locale="en"/>
+            <format property="TSTAMP" pattern="hh:mm:ss"/>
+        </tstamp>
+        <!-- Copy static resource files -->
+        <filter token="VERSION" value="${version}"/>
+        <filter token="VERSION_NUMBER" value="${version.number}"/>
+        <filter token="VERSION_BUILT" value="${TODAY} ${TSTAMP}"/>
+        <copy todir="${build.src}/examples" filtering="yes">
+            <fileset dir="${src.dir}/examples">
+                <include name="**/*.java"/>
+                <include name="**/*.xml"/>
+                <include name="**/*.properties"/>
+            </fileset>
+        </copy>
+
+        <javac srcdir="${build.src}/examples"
+            destdir="${build.dest}/examples"
+            debug="${debug}"
+            deprecation="${deprecation}"
+            optimize="${optimize}">
+            <classpath refid="examples.classpath"/>
+        </javac>
+        <copy todir="${build.dest}/examples" filtering="yes">
+            <fileset dir="${build.src}/examples">
+                <include name="**/*.xml"/>
+                <include name="**/*.properties"/>
+            </fileset>
+        </copy>
+    </target>
+
+    <!-- ================================================================== -->
+    <!-- Make Tomcat Native jar                                             -->
+    <!-- ================================================================== -->
+    <target name="jar" depends="compile"
+        description="Generates the Jar file">
+        <jar
+            destfile="${build.dir}/${final.name}.jar"
+            basedir="${build.dir}/classes/java"
+            excludes="**/*.java">
+            <manifest>
+                <section name="org/apache/tomcat/jni">
+                    <attribute name="Specification-Title" value="Tomcat Native"/>
+                    <attribute name="Specification-Version" value="${version}"/>
+                    <attribute name="Specification-Vendor" value="Apache Software Foundation"/>
+                    <attribute name="Implementation-Title" value="org.apache.tomcat.jni"/>
+                    <attribute name="Implementation-Vendor" value="Apache Software Foundation"/>
+                    <attribute name="Implementation-Vendor-Id" value="org.apache"/>
+                    <attribute name="Implementation-Version" value="${version} (build ${DSTAMP} ${TSTAMP})"/>
+                </section>
+            </manifest>
+        </jar>
+    </target>
+
+    <!-- =================================================================== -->
+    <!-- Compiles the test directory                                         -->
+    <!-- =================================================================== -->
+    <target name="compile-tests" depends="compile">
+        <mkdir dir="${build.dest}/test"/>
+        <mkdir dir="${build.src}/test"/>
+        <copy todir="${build.src}/test" filtering="yes">
+            <fileset dir="${src.dir}/test">
+                <include name="**/*.java"/>
+                <include name="**/*.xml"/>
+                <include name="**/*.properties"/>
+            </fileset>
+        </copy>
+        <javac srcdir="${build.src}/test"
+            destdir="${build.dest}/test"
+            debug="${debug}"
+            deprecation="${deprecation}"
+            optimize="${optimize}">
+
+            <classpath refid="test.classpath"/>
+        </javac>
+    </target>
+
+    <!-- =================================================================== -->
+    <!-- Junit tests                                                         -->
+    <!-- =================================================================== -->
+    <target name="test.file" depends="compile-tests">
+        <echo message="Running Tomcat Native package tests ..."/>
+        <java dir="${test.dir}" classname="${test.runner}" fork="yes" failonerror="${test.failonerror}">
+            <arg value="org.apache.tomcat.jni.FileTestSuite"/>
+            <classpath refid="test.classpath"/>
+        </java>
+    </target>
+
+    <!-- =================================================================== -->
+    <!-- Compiles the examples directory                                     -->
+    <!-- =================================================================== -->
+    <target name="compile-examples" depends="compile">
+        <mkdir dir="${build.dest}/examples"/>
+        <mkdir dir="${build.src}/examples"/>
+        <copy todir="${build.src}/examples" filtering="yes">
+            <fileset dir="${src.dir}/examples">
+                <include name="**/*.java"/>
+                <include name="**/*.xml"/>
+                <include name="**/*.properties"/>
+            </fileset>
+        </copy>
+        <javac srcdir="${build.src}/examples"
+            destdir="${build.dest}/examples"
+            debug="${debug}"
+            deprecation="${deprecation}"
+            optimize="${optimize}">
+
+            <classpath refid="examples.classpath"/>
+        </javac>
+    </target>
+
+    <!-- =================================================================== -->
+    <!-- excutes the examples                                                -->
+    <!-- =================================================================== -->
+    <target name="echo-example" depends="examples">
+        <echo message="Running Tomcat Native Echo example ..."/>
+        <java dir="${examples.dir}" classname="org.apache.tomcat.jni.Echo"
+             fork="yes" failonerror="${test.failonerror}">
+            <classpath refid="examples.classpath"/>
+            <env key="PATH" path="${tc.library.path}:${java.library.path}"/>
+            <env key="Path" path="${tc.library.path}:${java.library.path}"/>
+            <jvmarg value="-Djava.library.path=${tc.library.path}"/>
+        </java>
+    </target>
+    <target name="server-example" depends="examples">
+        <echo message="Running Tomcat Native SSL Server example ..."/>
+        <java dir="${examples.dir}" classname="org.apache.tomcat.jni.SSLServer"
+             fork="yes" failonerror="${test.failonerror}">
+            <env key="PATH" path="${tc.library.path}:${java.library.path}"/>
+            <env key="Path" path="${tc.library.path}:${java.library.path}"/>
+            <classpath refid="examples.classpath"/>
+            <jvmarg value="-Djava.library.path=${tc.library.path}"/>
+        </java>
+    </target>
+    <target name="locals-example" depends="examples">
+        <echo message="Running Tomcat Native Local Server example ..."/>
+        <java dir="${examples.dir}" classname="org.apache.tomcat.jni.LocalServer"
+             fork="yes" failonerror="${test.failonerror}">
+            <classpath refid="examples.classpath"/>
+            <env key="PATH" path="${tc.library.path}:${java.library.path}"/>
+            <env key="Path" path="${tc.library.path}:${java.library.path}"/>
+            <jvmarg value="-Djava.library.path=${tc.library.path}"/>
+        </java>
+    </target>
+</project>
diff --git a/connectors/jni/examples/mkcerts b/connectors/jni/examples/mkcerts
new file mode 100644
index 0000000..a611ca9
--- /dev/null
+++ b/connectors/jni/examples/mkcerts
@@ -0,0 +1,216 @@
+#!/bin/sh
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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 the configuration file to treate the CA certificate of the
+# _DEMONSTRATION ONLY_ 'Coyote' Certificate Authority.
+# This CA is used to sign the localhost.crt and user.crt
+# because self-signed server certificates are not accepted by all browsers.
+# NEVER USE THIS CA YOURSELF FOR REAL LIFE! INSTEAD EITHER USE A PUBLICALLY
+# KNOWN CA OR CREATE YOUR OWN CA!
+
+if [ -z "$OPENSSL" ]; then OPENSSL=openssl; fi
+
+PASSPHRASE="pass:secret"
+# Encrypt all keys
+GENRSA="$OPENSSL genrsa -des3"
+# Uncomment for no key encryption
+# GENRSA="$OPENSSL genrsa"
+REQ="$OPENSSL req -new"
+CA="$OPENSSL ca"
+X509="$OPENSSL x509"
+
+$OPENSSL rand -out .rnd 8192
+$GENRSA -passout $PASSPHRASE -out ca.key -rand .rnd 1024
+
+cat >ca.cfg <<EOT
+[ ca ]
+default_ca                      = default_db
+[ default_db ]
+dir                             = .
+certs                           = .
+new_certs_dir                   = ca.certs
+database                        = ca.index
+serial                          = ca.serial
+RANDFILE                        = .rnd
+certificate                     = ca.crt
+private_key                     = ca.key
+default_days                    = 365
+default_crl_days                = 30
+default_md                      = md5
+preserve                        = no
+name_opt                        = ca_default
+cert_opt                        = ca_default
+unique_subject                  = no
+[ server_policy ]
+countryName                     = supplied
+stateOrProvinceName             = supplied
+localityName                    = supplied
+organizationName                = supplied
+organizationalUnitName          = supplied
+commonName                      = supplied
+emailAddress                    = supplied
+[ server_cert ]
+subjectKeyIdentifier            = hash
+authorityKeyIdentifier          = keyid:always
+extendedKeyUsage                = serverAuth,clientAuth,msSGC,nsSGC
+basicConstraints                = critical,CA:false
+[ user_policy ]
+commonName                      = supplied
+emailAddress                    = supplied
+[ user_cert ]
+subjectAltName                  = email:copy
+basicConstraints                = critical,CA:false
+authorityKeyIdentifier          = keyid:always
+extendedKeyUsage                = clientAuth,emailProtection
+
+[ req ]
+default_bits                    = 1024
+default_keyfile                 = ca.key
+distinguished_name              = default_ca
+x509_extensions                 = extensions
+string_mask                     = nombstr
+req_extensions                  = req_extensions
+input_password                  = secret
+output_password                 = secret
+[ default_ca ]
+countryName                     = Country Code
+countryName_value               = US
+countryName_min                 = 2
+countryName_max                 = 2
+stateOrProvinceName             = State Name
+stateOrProvinceName_value       = Delaware
+localityName                    = Locality Name
+localityName_value              = Wilmington
+organizationName                = Organization Name
+organizationName_value          = Apache Software Foundation
+organizationalUnitName          = Organizational Unit Name
+organizationalUnitName_value    = Apache Tomcat
+commonName                      = Common Name
+commonName_value                = Apache Tomcat demo root CA
+commonName_max                  = 64
+emailAddress                    = Email Address
+emailAddress_value              = coyote@tomcat.apache.org
+emailAddress_max                = 40
+[ extensions ]
+subjectKeyIdentifier            = hash
+authorityKeyIdentifier          = keyid:always
+basicConstraints                = critical,CA:true
+[ req_extensions ]
+nsCertType                      = objsign,email,server
+EOT
+
+$REQ -x509 -days 3650 -batch -config ca.cfg -key ca.key -out ca.crt
+
+# Create cabundle.crt that can be used for CAfile
+cat >cabundle.crt <<EOT
+Tomcat Demo Root CA
+=========================================
+`$X509 -noout -fingerprint -in ca.crt`
+PEM Data:
+`$X509 -in ca.crt`
+`$X509 -noout -text -in ca.crt`
+EOT
+
+$GENRSA -passout $PASSPHRASE -out localhost.key  -rand .rnd 1024
+
+cat >localhost.cfg <<EOT
+[ req ]
+default_bits                    = 1024
+distinguished_name              = localhost
+string_mask                     = nombstr
+req_extensions                  = extensions
+input_password                  = secret
+output_password                 = secret
+[ localhost ]
+countryName                     = Country Code
+countryName_value               = US
+countryName_min                 = 2
+countryName_max                 = 2
+stateOrProvinceName             = State Name
+stateOrProvinceName_value       = Delaware
+localityName                    = Locality Name
+localityName_value              = Wilmington
+organizationName                = Organization Name
+organizationName_value          = Apache Software Foundation
+organizationalUnitName          = Organizational Unit Name
+organizationalUnitName_value    = Apache Tomcat
+commonName                      = Common Name
+commonName_value                = Apache Tomcat localhost secure demo server
+commonName_max                  = 64
+emailAddress                    = Email Address
+emailAddress_value              = tomcat@localhost.edu
+emailAddress_max                = 40
+[ extensions ]
+nsCertType                      = server
+basicConstraints                = critical,CA:false
+EOT
+
+$REQ -passin $PASSPHRASE -batch -config localhost.cfg -key localhost.key -out localhost.csr
+rm -f localhost.cfg
+
+#  make sure environment exists
+if [ ! -d ca.certs ]; then
+    mkdir ca.certs
+    echo '01' >ca.serial
+    cp /dev/null ca.index
+fi
+
+$CA -passin $PASSPHRASE -batch -config ca.cfg -extensions server_cert -policy server_policy  -out x.crt -infiles localhost.csr
+$X509 -in x.crt -out localhost.crt
+rm -f x.crt
+# Create PKCS12 localhost certificate
+$OPENSSL pkcs12 -export -passout $PASSPHRASE -passin $PASSPHRASE -in localhost.crt -inkey localhost.key -certfile ca.crt -out localhost.p12
+
+$GENRSA -passout $PASSPHRASE -out user.key -rand .rnd 1024
+
+cat >user.cfg <<EOT
+[ req ]
+default_bits            = 1024
+distinguished_name      = admin
+string_mask             = nombstr
+req_extensions          = extensions
+input_password          = secret
+output_password         = secret
+[ admin ]
+commonName              = User Name
+commonName_value        = Localhost Administrator
+commonName_max          = 64
+emailAddress            = Email Address
+emailAddress_value      = admin@localhost.edu
+emailAddress_max        = 40
+[ extensions ]
+nsCertType              = client,email
+basicConstraints        = critical,CA:false
+EOT
+
+$REQ -passin $PASSPHRASE -batch -config user.cfg -key user.key -out user.csr
+rm -f user.cfg
+$CA -passin $PASSPHRASE -batch -config ca.cfg -extensions user_cert -policy user_policy  -out x.crt -infiles user.csr
+$X509 -in x.crt -out user.crt
+rm -f x.crt
+
+# $OPENSSL verify -CAfile ca.crt localhost.crt
+# $OPENSSL verify -CAfile ca.crt user.crt
+
+# Create PKCS12 user certificate
+$OPENSSL pkcs12 -export -passout $PASSPHRASE -passin $PASSPHRASE -in user.crt -inkey user.key -certfile ca.crt -out user.p12
+
+rm -f ca.cfg
+rm -f *.old
+rm -f ca.index.attr
+rm -f .rnd
diff --git a/connectors/jni/examples/org/apache/tomcat/jni/Echo.java b/connectors/jni/examples/org/apache/tomcat/jni/Echo.java
new file mode 100644
index 0000000..9e1e8ff
--- /dev/null
+++ b/connectors/jni/examples/org/apache/tomcat/jni/Echo.java
@@ -0,0 +1,340 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+import java.io.InputStream;
+import java.util.Properties;
+
+/** Echo server example
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public class Echo {
+
+    public static String echoEcho = null;
+    public static String echoAddr = null;
+    public static int echoPort    = 0;
+    public static int echoNmax    = 0;
+    public static int echoNrun    = 0;
+    public static long echoPool   = 0;
+
+    private static Poller echoPoller     = null;
+    private static Acceptor echoAcceptor = null;
+
+    private static Object threadLock = new Object();
+
+    static {
+
+        try {
+            InputStream is = Echo.class.getResourceAsStream
+                ("/org/apache/tomcat/jni/Echo.properties");
+            Properties props = new Properties();
+            props.load(is);
+            is.close();
+            echoAddr = props.getProperty("echo.ip", "127.0.0.1");
+            echoPort = Integer.decode(props.getProperty("echo.port", "23")).intValue();
+            echoNmax = Integer.decode(props.getProperty("echo.max", "1")).intValue();
+        }
+        catch (Throwable t) {
+            ; // Nothing
+        }
+    }
+
+    /* Acceptor thread. Listens for new connections */
+    private class Acceptor extends java.lang.Thread {
+        private long serverSock = 0;
+        private long inetAddress = 0;
+        private long pool = 0;
+        public Acceptor() throws Exception {
+            try {
+
+                pool = Pool.create(Echo.echoPool);
+                System.out.println("Accepting: " +  Echo.echoAddr + ":" +
+                                   Echo.echoPort);
+                inetAddress = Address.info(Echo.echoAddr, Socket.APR_INET,
+                                           Echo.echoPort, 0,
+                                           pool);
+                serverSock = Socket.create(Socket.APR_INET, Socket.SOCK_STREAM,
+                                           Socket.APR_PROTO_TCP, pool);
+                long sa = Address.get(Socket.APR_LOCAL, serverSock);
+                Sockaddr addr = new Sockaddr();
+                if (Address.fill(addr, sa)) {
+                    System.out.println("Host: " + addr.hostname);
+                    System.out.println("Server: " + addr.servname);
+                    System.out.println("IP: " + Address.getip(sa) +
+                                       ":" + addr.port);
+                }
+                int rc = Socket.bind(serverSock, inetAddress);
+                if (rc != 0) {
+                  throw(new Exception("Can't create Acceptor: bind: " + Error.strerror(rc)));
+                }
+                Socket.listen(serverSock, 5);
+            }
+            catch( Exception ex ) {
+                ex.printStackTrace();
+                throw(new Exception("Can't create Acceptor"));
+            }
+        }
+
+        public void run() {
+            int i = 0;
+            try {
+                while (true) {
+                    long clientSock = Socket.accept(serverSock);
+                    System.out.println("Accepted id: " +  i);
+
+                    try {
+                        long sa = Address.get(Socket.APR_REMOTE, clientSock);
+                        Sockaddr raddr = new Sockaddr();
+                        if (Address.fill(raddr, sa)) {
+                            System.out.println("Remote Host: " + Address.getnameinfo(sa, 0));
+                            System.out.println("Remote IP: " + Address.getip(sa) +
+                                               ":" + raddr.port);
+                        }
+                        sa = Address.get(Socket.APR_LOCAL, clientSock);
+                        Sockaddr laddr = new Sockaddr();
+                        if (Address.fill(laddr, sa)) {
+                            System.out.println("Local Host: " + laddr.hostname);
+                            System.out.println("Local Server: " + Address.getnameinfo(sa, 0));
+                            System.out.println("Local IP: " + Address.getip(sa) +
+                                               ":" + laddr.port);
+                        }
+
+                    } catch (Exception e) {
+                        // Ignore
+                        e.printStackTrace();
+                    }
+
+                    Socket.timeoutSet(clientSock, 10000000);
+                    Worker worker = new Worker(clientSock, i++,
+                                               this.getClass().getName());
+                    Echo.incThreads();
+                    worker.start();
+                }
+            }
+            catch( Exception ex ) {
+                ex.printStackTrace();
+            }
+        }
+    }
+
+    /* Poller thread. Listens for new recycled connections */
+    private class Poller extends java.lang.Thread {
+        private long serverPollset = 0;
+        private long pool = 0;
+        private int nsocks = 0;
+        public Poller() {
+            try {
+
+                pool = Pool.create(Echo.echoPool);
+                serverPollset = Poll.create(16, pool, 0, 10000000);
+            }
+            catch( Exception ex ) {
+                ex.printStackTrace();
+            }
+        }
+
+        public void add(long socket) {
+            int rv = Poll.add(serverPollset, socket,
+                              Poll.APR_POLLIN);
+            if (rv == Status.APR_SUCCESS) {
+                System.out.println("Added worker to pollset");
+                nsocks++;
+            }
+        }
+
+        public void remove(long socket) {
+            int rv = Poll.remove(serverPollset, socket);
+            if (rv == Status.APR_SUCCESS) {
+               nsocks--;
+               System.out.println("Removed worker from pollset");
+            }
+            else {
+               System.out.println("Failed removing worker from pollset");
+            }
+        }
+
+        public void run() {
+            while (true) {
+                try {
+                    if (nsocks < 1) {
+                        java.lang.Thread.sleep(1);
+                        continue;
+                    }
+                    /* Two times size then  created pollset */
+                    long [] desc = new long[64];
+                    /* USe 1 second poll timeout */
+                    int rv = Poll.poll(serverPollset, 1000000, desc, false);
+                    if (rv > 0) {
+                        for (int n = 0; n < rv; n++) {
+                            long clientSock = desc[n*2+1];
+                            System.out.println("Poll flags " + desc[n*2]);
+                            remove(clientSock);
+                            Worker worker = new Worker(clientSock, n,
+                                                       this.getClass().getName());
+                            Echo.incThreads();
+                            worker.start();
+                        }
+                    }
+                    else {
+                        if (Status.APR_STATUS_IS_TIMEUP(-rv))
+                            System.out.println("Timeup");
+                        else {
+                            System.out.println("Error " + (-rv));
+                        }
+                    }
+                }
+                /* XXX: JFC quick hack
+                catch(Error err ) {
+                    if (Status.APR_STATUS_IS_TIMEUP(err.getError())) {
+                        /0 TODO: deal with timeout 0/
+                    }
+                    else {
+                        err.printStackTrace();
+                        break;
+                    }
+                }
+                 */
+                catch( Exception ex ) {
+                    ex.printStackTrace();
+                    break;
+                }
+            }
+        }
+    }
+
+    private class Worker extends java.lang.Thread {
+        private int workerId = 0;
+        private long clientSock = 0;
+        private byte [] wellcomeMsg = null;
+        public Worker(long clientSocket, int workerId, String from) {
+            this.clientSock = clientSocket;
+            this.workerId = workerId;
+            wellcomeMsg = ("Echo server id: " + this.workerId + " from " +
+                           from + "\r\n").getBytes();
+        }
+
+        public void run() {
+            boolean doClose = false;
+            try {
+                Socket.send(clientSock, wellcomeMsg, 0, wellcomeMsg.length);
+                /* Do a blocking read byte at a time */
+                byte [] buf = new byte[1];
+                while (Socket.recv(clientSock, buf, 0, 1) == 1) {
+                    if (buf[0] == '\n')
+                        break;
+                    else if (buf[0] == '!') {
+                        doClose = true;
+                        break;
+                    }
+                }
+                if (doClose) {
+                    try {
+                        byte [] msg = ("Bye from worker: " + workerId + "\r\n").getBytes();
+                        Socket.send(clientSock, msg, 0, msg.length);
+                    } catch(Exception e) { }
+
+                    Socket.close(clientSock);
+                }
+                else {
+                    try {
+                        byte [] msg = ("Recycling worker: " + workerId + "\r\n").getBytes();
+                        Socket.send(clientSock, msg, 0, msg.length);
+                    } catch(Exception e) { }
+                    /* Put the socket to the keep-alive poll */
+                    Echo.echoPoller.add(clientSock);
+                }
+            } catch (Exception e) {
+                Socket.close(clientSock);
+                e.printStackTrace();
+            }
+            Echo.decThreads();
+            System.out.println("Worker: " +  workerId + " finished");
+        }
+    }
+
+    public Echo()
+    {
+        echoPool = Pool.create(0);
+        try {
+            echoAcceptor = new Acceptor();
+            echoAcceptor.start();
+            echoPoller = new Poller();
+            echoPoller.start();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+    }
+
+    public static void incThreads() {
+        synchronized(threadLock) {
+            echoNrun++;
+        }
+    }
+
+    public static void decThreads() {
+        synchronized(threadLock) {
+            echoNrun--;
+        }
+    }
+
+    public static void main(String [] args) {
+        try {
+            Library.initialize(null);
+            long [] inf = new long[16];
+            System.out.println("Info ...");
+            System.out.println("  Native        " + Library.versionString());
+            System.out.println("  APR           " + Library.aprVersionString());
+            OS.info(inf);
+            System.out.println("OS Info ...");
+            System.out.println("  Physical      " + inf[0]);
+            System.out.println("  Avail         " + inf[1]);
+            System.out.println("  Swap          " + inf[2]);
+            System.out.println("  Swap free     " + inf[3]);
+            System.out.println("  Shared        " + inf[4]);
+            System.out.println("  Buffers size  " + inf[5]);
+            System.out.println("  Load          " + inf[6]);
+
+            System.out.println("  Idle          " + inf[7]);
+            System.out.println("  Kernel        " + inf[8]);
+            System.out.println("  User          " + inf[9]);
+
+            System.out.println("  Proc creation " + inf[10]);
+            System.out.println("  Proc kernel   " + inf[11]);
+            System.out.println("  Proc user     " + inf[12]);
+            System.out.println("  Curr working  " + inf[13]);
+            System.out.println("  Peak working  " + inf[14]);
+            System.out.println("  Page faults   " + inf[15]);
+
+            SSL.initialize(null);
+            System.out.println("OpenSSL ...");
+            System.out.println("  version       " + SSL.versionString());
+            System.out.println("  number        " + SSL.version());
+
+            System.out.println("Starting Native Echo server example on port " +
+                               echoAddr + ":" + echoPort);
+            Echo echo = new Echo();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+    }
+}
diff --git a/connectors/jni/examples/org/apache/tomcat/jni/Echo.properties b/connectors/jni/examples/org/apache/tomcat/jni/Echo.properties
new file mode 100644
index 0000000..0918c5e
--- /dev/null
+++ b/connectors/jni/examples/org/apache/tomcat/jni/Echo.properties
@@ -0,0 +1,2 @@
+# Telnet properties
+echo.port=23
diff --git a/connectors/jni/examples/org/apache/tomcat/jni/Local.properties b/connectors/jni/examples/org/apache/tomcat/jni/Local.properties
new file mode 100644
index 0000000..869e283
--- /dev/null
+++ b/connectors/jni/examples/org/apache/tomcat/jni/Local.properties
@@ -0,0 +1,8 @@
+# Local properties
+local.max=10
+
+# For NT Pipes use something like
+local.path=\\\\.\\PIPE\\test
+
+# For Unix Sockets use
+# local.path=/tmp/testsock
diff --git a/connectors/jni/examples/org/apache/tomcat/jni/LocalServer.java b/connectors/jni/examples/org/apache/tomcat/jni/LocalServer.java
new file mode 100644
index 0000000..50ce7b4
--- /dev/null
+++ b/connectors/jni/examples/org/apache/tomcat/jni/LocalServer.java
@@ -0,0 +1,168 @@
+package org.apache.tomcat.jni;
+
+import java.io.InputStream;
+import java.util.Properties;
+
+/** Local Socket server example
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public class LocalServer {
+
+    public static String serverAddr = null;
+    public static int serverNmax    = 0;
+    public static int serverNrun    = 0;
+    public static long serverPool   = 0;
+
+    private static Acceptor serverAcceptor = null;
+
+    private static Object threadLock = new Object();
+
+    static {
+
+        try {
+            InputStream is = LocalServer.class.getResourceAsStream
+                ("/org/apache/tomcat/jni/Local.properties");
+            Properties props = new Properties();
+            props.load(is);
+            is.close();
+            serverAddr = props.getProperty("local.path", null);
+            serverNmax = Integer.decode(props.getProperty("local.max", "0")).intValue();
+        }
+        catch (Throwable t) {
+            ; // Nothing
+        }
+    }
+
+    public LocalServer()
+    {
+        serverPool = Pool.create(0);
+        try {
+            serverAcceptor = new Acceptor();
+            serverAcceptor.start();
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+    }
+    public static void incThreads() {
+        synchronized(threadLock) {
+            serverNrun++;
+        }
+    }
+
+    public static void decThreads() {
+        synchronized(threadLock) {
+            serverNrun--;
+        }
+    }
+
+    /* Acceptor thread. Listens for new connections */
+    private class Acceptor extends java.lang.Thread {
+        private long serverSock = 0;
+        private long inetAddress = 0;
+        private long pool = 0;
+        public Acceptor() throws Exception {
+            try {
+
+                pool = Pool.create(LocalServer.serverPool);
+                System.out.println("Accepting: " +  LocalServer.serverAddr);
+                serverSock = Local.create(LocalServer.serverAddr, pool);
+                int rc = Local.bind(serverSock, inetAddress);
+                if (rc != 0) {
+                  throw(new Exception("Can't create Acceptor: bind: " + Error.strerror(rc)));
+                }
+                Local.listen(serverSock, LocalServer.serverNmax);
+            }
+            catch( Exception ex ) {
+                ex.printStackTrace();
+                throw(new Exception("Can't create Acceptor"));
+            }
+        }
+
+        public void run() {
+            int i = 0;
+            try {
+                while (true) {
+                    long clientSock = Local.accept(serverSock);
+                    System.out.println("Accepted id: " +  i);
+
+                    Socket.timeoutSet(clientSock, 10000000);
+                    Worker worker = new Worker(clientSock, i++,
+                                               this.getClass().getName());
+                    LocalServer.incThreads();
+                    worker.start();
+                }
+            }
+            catch( Exception ex ) {
+                ex.printStackTrace();
+            }
+        }
+    }
+
+    private class Worker extends java.lang.Thread {
+        private int workerId = 0;
+        private long clientSock = 0;
+        private byte [] wellcomeMsg = null;
+
+        public Worker(long clientSocket, int workerId, String from) {
+            this.clientSock = clientSocket;
+            this.workerId = workerId;
+            wellcomeMsg = ("LocalServer server id: " + this.workerId + " from " +
+                           from).getBytes();
+        }
+
+        public void run() {
+            boolean doClose = false;
+            try {
+                Socket.send(clientSock, wellcomeMsg, 0, wellcomeMsg.length);
+                while (!doClose) {
+                    /* Do a blocking read byte at a time */
+                    byte [] buf = new byte[1];
+                    byte [] msg = new byte[256];
+                    int p = 0;
+                    while (Socket.recv(clientSock, buf, 0, 1) == 1) {
+                        if (buf[0] == '\n')
+                            break;
+                        else if (buf[0] == '!') {
+                            doClose = true;
+                            break;
+                        }
+                        if (p > 250)
+                           break;
+                        msg[p++] = buf[0];
+                    }
+                    if (doClose) {
+                        try {
+                            byte [] snd = ("Bye from worker: " + workerId).getBytes();
+                            Socket.send(clientSock, snd, 0, snd.length);
+                        } catch(Exception e) { }
+
+                        Socket.close(clientSock);
+                    }
+                    else
+                        Socket.send(clientSock, msg, 0, p);                
+                }
+            } catch (Exception e) {
+                Socket.destroy(clientSock);
+                e.printStackTrace();
+            }
+            LocalServer.decThreads();
+            System.out.println("Worker: " +  workerId + " finished");
+        }
+    }
+
+
+    public static void main(String [] args) {
+        try {
+            Library.initialize(null);
+
+            LocalServer server = new LocalServer();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+ }
diff --git a/connectors/jni/examples/org/apache/tomcat/jni/SSL.properties b/connectors/jni/examples/org/apache/tomcat/jni/SSL.properties
new file mode 100644
index 0000000..7f1bd62
--- /dev/null
+++ b/connectors/jni/examples/org/apache/tomcat/jni/SSL.properties
@@ -0,0 +1,7 @@
+# SSL Server and client properties
+server.port=4443
+server.cert=localhost.crt
+server.key=localhost.key
+server.password=secret
+server.ciphers=ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
+server.verify=none
\ No newline at end of file
diff --git a/connectors/jni/examples/org/apache/tomcat/jni/SSLServer.java b/connectors/jni/examples/org/apache/tomcat/jni/SSLServer.java
new file mode 100644
index 0000000..9cec843
--- /dev/null
+++ b/connectors/jni/examples/org/apache/tomcat/jni/SSLServer.java
@@ -0,0 +1,223 @@
+package org.apache.tomcat.jni;
+
+import java.io.InputStream;
+import java.util.Properties;
+
+/** SSL Server server example
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public class SSLServer {
+
+    public static String serverAddr = null;
+    public static int serverPort    = 0;
+    public static int serverNmax    = 0;
+    public static int serverNrun    = 0;
+    public static long serverCtx    = 0;
+    public static long serverPool   = 0;
+    public static String serverCert = null;
+    public static String serverKey  = null;
+    public static String serverCiphers  = null;
+    public static String serverPassword = null;
+    public static String serverCAFile   = null;
+
+    private static Acceptor serverAcceptor = null;
+
+    private static Object threadLock = new Object();
+
+    static {
+
+        try {
+            InputStream is = SSLServer.class.getResourceAsStream
+                ("/org/apache/tomcat/jni/SSL.properties");
+            Properties props = new Properties();
+            props.load(is);
+            is.close();
+            serverAddr = props.getProperty("server.ip", "127.0.0.1");
+            serverPort = Integer.decode(props.getProperty("server.port", "4443")).intValue();
+            serverNmax = Integer.decode(props.getProperty("server.max", "1")).intValue();
+            serverCert = props.getProperty("server.cert", "server.pem");
+            serverKey  = props.getProperty("server.key", null);
+            serverCAFile   = props.getProperty("server.cacertificate", null);
+            serverCiphers  = props.getProperty("server.ciphers", "ALL");
+            serverPassword = props.getProperty("server.password", null);
+        }
+        catch (Throwable t) {
+            ; // Nothing
+        }
+    }
+
+    public SSLServer()
+    {
+        serverPool = Pool.create(0);
+        try {
+            /* Create SSL Context, one for each Virtual Host */
+            serverCtx = SSLContext.make(serverPool, SSL.SSL_PROTOCOL_SSLV2 | SSL.SSL_PROTOCOL_SSLV3, SSL.SSL_MODE_SERVER);
+            /* List the ciphers that the client is permitted to negotiate. */
+            SSLContext.setCipherSuite(serverCtx, serverCiphers);
+            /* Load Server key and certificate */
+            SSLContext.setCertificate(serverCtx, serverCert, serverKey, serverPassword, SSL.SSL_AIDX_RSA);
+            SSLContext.setVerify(serverCtx, SSL.SSL_CVERIFY_NONE, 10);
+            serverAcceptor = new Acceptor();
+            serverAcceptor.start();
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+    }
+    public static void incThreads() {
+        synchronized(threadLock) {
+            serverNrun++;
+        }
+    }
+
+    public static void decThreads() {
+        synchronized(threadLock) {
+            serverNrun--;
+        }
+    }
+
+    /* Acceptor thread. Listens for new connections */
+    private class Acceptor extends java.lang.Thread {
+        private long serverSock = 0;
+        private long inetAddress = 0;
+        private long pool = 0;
+        public Acceptor() throws Exception {
+            try {
+
+                pool = Pool.create(SSLServer.serverPool);
+                System.out.println("Accepting: " +  SSLServer.serverAddr + ":" +
+                                   SSLServer.serverPort);
+                inetAddress = Address.info(SSLServer.serverAddr, Socket.APR_INET,
+                                           SSLServer.serverPort, 0,
+                                           pool);
+                serverSock = Socket.create(Socket.APR_INET, Socket.SOCK_STREAM,
+                                           Socket.APR_PROTO_TCP, pool);
+                int rc = Socket.bind(serverSock, inetAddress);
+                if (rc != 0) {
+                  throw(new Exception("Can't create Acceptor: bind: " + Error.strerror(rc)));
+                }
+                Socket.listen(serverSock, 5);
+            }
+            catch( Exception ex ) {
+                ex.printStackTrace();
+                throw(new Exception("Can't create Acceptor"));
+            }
+        }
+
+        public void run() {
+            int i = 0;
+            try {
+                while (true) {
+                    long clientSock = Socket.accept(serverSock);
+                    System.out.println("Accepted id: " +  i);
+
+                    try {
+                        long sa = Address.get(Socket.APR_REMOTE, clientSock);
+                        Sockaddr raddr = new Sockaddr();
+                        if (Address.fill(raddr, sa)) {
+                            System.out.println("Remote Host: " + Address.getnameinfo(sa, 0));
+                            System.out.println("Remote IP: " + Address.getip(sa) +
+                                               ":" + raddr.port);
+                        }
+                        sa = Address.get(Socket.APR_LOCAL, clientSock);
+                        Sockaddr laddr = new Sockaddr();
+                        if (Address.fill(laddr, sa)) {
+                            System.out.println("Local Host: " + laddr.hostname);
+                            System.out.println("Local Server: " + Address.getnameinfo(sa, 0));
+                            System.out.println("Local IP: " + Address.getip(sa) +
+                                               ":" + laddr.port);
+                        }
+
+                    } catch (Exception e) {
+                        // Ignore
+                        e.printStackTrace();
+                    }
+
+                    Socket.timeoutSet(clientSock, 10000000);
+                    SSLSocket.attach(SSLServer.serverCtx, clientSock);
+                    i = SSLSocket.handshake(clientSock);
+                    if (i == 0) {
+
+                        Worker worker = new Worker(clientSock, i++,
+                                                   this.getClass().getName());
+                        SSLServer.incThreads();
+                        worker.start();
+                        
+                    }
+                    else {
+                        System.out.println("Handshake error: " + SSL.getLastError());
+                        Socket.destroy(clientSock);
+                    }
+                }
+            }
+            catch( Exception ex ) {
+                ex.printStackTrace();
+            }
+        }
+    }
+
+    private class Worker extends java.lang.Thread {
+        private int workerId = 0;
+        private long clientSock = 0;
+        private byte [] wellcomeMsg = null;
+
+        public Worker(long clientSocket, int workerId, String from) {
+            this.clientSock = clientSocket;
+            this.workerId = workerId;
+            wellcomeMsg = ("SSLServer server id: " + this.workerId + " from " +
+                           from + "\r\n").getBytes();
+        }
+
+        public void run() {
+            boolean doClose = false;
+            try {
+                Socket.send(clientSock, wellcomeMsg, 0, wellcomeMsg.length);
+                while (!doClose) {
+                    /* Do a blocking read byte at a time */
+                    byte [] buf = new byte[1];
+                    int ret;
+                    ret = Socket.recv(clientSock, buf, 0, 1);
+                    if (ret != 1)
+                        throw(new Exception("Socket.recv failed"));
+
+                    if (buf[0] == '\n')
+                        continue;
+                    else if (buf[0] == '!') {
+                        doClose = true;
+                    }
+                    Socket.send(clientSock, buf, 0, 1);
+
+                    if (doClose) {
+                        try {
+                            byte [] msg = ("Bye from worker: " + workerId + "\r\n").getBytes();
+                            Socket.send(clientSock, msg, 0, msg.length);
+                        } catch(Exception e) { }
+
+                        Socket.close(clientSock);
+                    }
+                }
+            } catch (Exception e) {
+                Socket.destroy(clientSock);
+                e.printStackTrace();
+            }
+            Echo.decThreads();
+            System.out.println("Worker: " +  workerId + " finished");
+        }
+    }
+
+
+    public static void main(String [] args) {
+        try {
+            Library.initialize(null);
+            SSL.initialize(null);
+
+            SSLServer server = new SSLServer();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+ }
diff --git a/connectors/jni/java/org/apache/tomcat/Apr.java b/connectors/jni/java/org/apache/tomcat/Apr.java
new file mode 100644
index 0000000..53d5d06
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/Apr.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+import java.io.InputStream;
+import java.util.Properties;
+
+public class Apr {
+    private static String aprInfo = null;
+
+    static {
+
+        try {
+            InputStream is = Apr.class.getResourceAsStream
+                ("/org/apache/tomcat/apr.properties");
+            Properties props = new Properties();
+            props.load(is);
+            is.close();
+            aprInfo = props.getProperty("tcn.info");
+        }
+        catch (Throwable t) {
+            ; // Nothing
+        }
+    }
+}
diff --git a/connectors/jni/java/org/apache/tomcat/apr.properties b/connectors/jni/java/org/apache/tomcat/apr.properties
new file mode 100644
index 0000000..9dfb03d
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/apr.properties
@@ -0,0 +1 @@
+tcn.info=Tomcat Native/@VERSION@
diff --git a/connectors/jni/java/org/apache/tomcat/jni/Address.java b/connectors/jni/java/org/apache/tomcat/jni/Address.java
new file mode 100644
index 0000000..048db45
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/Address.java
@@ -0,0 +1,114 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+/** Address
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public class Address {
+
+    static public String APR_ANYADDR = "0.0.0.0";
+    /**
+     * Fill the Sockaddr class from apr_sockaddr_t
+     * @param info Sockaddr class to fill
+     * @param sa Structure pointer
+     */
+    public static native boolean fill(Sockaddr info, long sa);
+
+    /**
+     * Create the Sockaddr object from apr_sockaddr_t
+     * @param sa Structure pointer
+     */
+    public static native Sockaddr getInfo(long sa);
+
+    /**
+     * Create apr_sockaddr_t from hostname, address family, and port.
+     * @param hostname The hostname or numeric address string to resolve/parse, or
+     *               NULL to build an address that corresponds to 0.0.0.0 or ::
+     * @param family The address family to use, or APR_UNSPEC if the system should
+     *               decide.
+     * @param port The port number.
+     * @param flags Special processing flags:
+     * <PRE>
+     *       APR_IPV4_ADDR_OK          first query for IPv4 addresses; only look
+     *                                 for IPv6 addresses if the first query failed;
+     *                                 only valid if family is APR_UNSPEC and hostname
+     *                                 isn't NULL; mutually exclusive with
+     *                                 APR_IPV6_ADDR_OK
+     *       APR_IPV6_ADDR_OK          first query for IPv6 addresses; only look
+     *                                 for IPv4 addresses if the first query failed;
+     *                                 only valid if family is APR_UNSPEC and hostname
+     *                                 isn't NULL and APR_HAVE_IPV6; mutually exclusive
+     *                                 with APR_IPV4_ADDR_OK
+     * </PRE>
+     * @param p The pool for the apr_sockaddr_t and associated storage.
+     * @return The new apr_sockaddr_t.
+     */
+    public static native long info(String hostname, int family,
+                                   int port, int flags, long p)
+        throws Exception;
+    /**
+     * Look up the host name from an apr_sockaddr_t.
+     * @param sa The apr_sockaddr_t.
+     * @param flags Special processing flags.
+     * @return The hostname.
+     */
+    public static native String getnameinfo(long sa, int flags);
+
+    /**
+     * Return the IP address (in numeric address string format) in
+     * an APR socket address.  APR will allocate storage for the IP address
+     * string from the pool of the apr_sockaddr_t.
+     * @param sa The socket address to reference.
+     * @return The IP address.
+     */
+    public static native String getip(long sa);
+
+    /**
+     * Given an apr_sockaddr_t and a service name, set the port for the service
+     * @param sockaddr The apr_sockaddr_t that will have its port set
+     * @param servname The name of the service you wish to use
+     * @return APR status code.
+     */
+    public static native int getservbyname(long sockaddr, String servname);
+
+    /**
+     * Return an apr_sockaddr_t from an apr_socket_t
+     * @param which Which interface do we want the apr_sockaddr_t for?
+     * @param sock The socket to use
+     * @return The returned apr_sockaddr_t.
+     */
+    public static native long get(int which, long sock)
+        throws Exception;
+
+    /**
+     * See if the IP addresses in two APR socket addresses are
+     * equivalent.  Appropriate logic is present for comparing
+     * IPv4-mapped IPv6 addresses with IPv4 addresses.
+     *
+     * @param a One of the APR socket addresses.
+     * @param b The other APR socket address.
+     * The return value will be True if the addresses
+     * are equivalent.
+     */
+    public static native boolean equal(long a, long b);
+
+}
diff --git a/connectors/jni/java/org/apache/tomcat/jni/BIOCallback.java b/connectors/jni/java/org/apache/tomcat/jni/BIOCallback.java
new file mode 100644
index 0000000..3632244
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/BIOCallback.java
@@ -0,0 +1,56 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+/** Open SSL BIO Callback Interface
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public interface BIOCallback {
+
+    /**
+     * Write data
+     * @param buf containg the bytes to write.
+     * @return Number of characters written.
+     */
+    public int write(byte [] buf);
+
+    /**
+     * Read data
+     * @param buf buffer to store the read bytes.
+     * @return number of bytes read.
+     */
+    public int read(byte [] buf);
+
+    /**
+     * Puts string
+     * @param data String to write
+     * @return Number of characters written
+     */
+    public int puts(String data);
+
+    /**
+     * Read string up to the len or CLRLF
+     * @param len Maximum number of characters to read
+     * @return String with up to len bytes readed
+     */
+    public String gets(int len);
+
+}
diff --git a/connectors/jni/java/org/apache/tomcat/jni/Directory.java b/connectors/jni/java/org/apache/tomcat/jni/Directory.java
new file mode 100644
index 0000000..db31124
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/Directory.java
@@ -0,0 +1,97 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+/** Directory
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public class Directory {
+
+    /**
+     * Create a new directory on the file system.
+     * @param path the path for the directory to be created. (use / on all systems)
+     * @param perm Permissions for the new direcoty.
+     * @param pool the pool to use.
+     */
+    public static native int make(String path, int perm, long pool);
+
+    /** Creates a new directory on the file system, but behaves like
+     * 'mkdir -p'. Creates intermediate directories as required. No error
+     * will be reported if PATH already exists.
+     * @param path the path for the directory to be created. (use / on all systems)
+     * @param perm Permissions for the new direcoty.
+     * @param pool the pool to use.
+     */
+    public static native int makeRecursive(String path, int perm, long pool);
+
+    /**
+     * Remove directory from the file system.
+     * @param path the path for the directory to be removed. (use / on all systems)
+     * @param pool the pool to use.
+     */
+    public static native int remove(String path, long pool);
+
+    /**
+     * Find an existing directory suitable as a temporary storage location.
+     * @param pool The pool to use for any necessary allocations.
+     * @return The temp directory.
+     * 
+     * This function uses an algorithm to search for a directory that an
+     * an application can use for temporary storage.  Once such a
+     * directory is found, that location is cached by the library.  Thus,
+     * callers only pay the cost of this algorithm once if that one time
+     * is successful.
+     *
+     */
+    public static native String tempGet(long pool);
+
+    /**
+     * Open the specified directory.
+     * @param dirname The full path to the directory (use / on all systems)
+     * @param pool The pool to use.
+     * @return The opened directory descriptor.
+     */
+    public static native long open(String dirname, long pool)
+        throws Error;
+
+    /**
+     * close the specified directory.
+     * @param thedir the directory descriptor to close.
+     */
+    public static native int close(long thedir);
+
+    /**
+     * Rewind the directory to the first entry.
+     * @param thedir the directory descriptor to rewind.
+     */
+    public static native int rewind(long thedir);
+
+
+    /**
+     * Read the next entry from the specified directory.
+     * @param finfo the file info structure and filled in by apr_dir_read
+     * @param wanted The desired apr_finfo_t fields, as a bit flag of APR_FINFO_ values
+     * @param thedir the directory descriptor returned from apr_dir_open
+     * No ordering is guaranteed for the entries read.
+     */
+    public static native int read(FileInfo finfo, int wanted, long thedir);
+
+}
diff --git a/connectors/jni/java/org/apache/tomcat/jni/Error.java b/connectors/jni/java/org/apache/tomcat/jni/Error.java
new file mode 100644
index 0000000..9d56b89
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/Error.java
@@ -0,0 +1,96 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+/** Error
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public class Error extends Exception {
+
+    /**
+     * APR error type.
+     */
+    private int error;
+
+    /**
+     * A description of the problem.
+     */
+    private String description;
+
+    /**
+     * Construct an APRException.
+     *
+     * @param error one of the value in Error
+     * @param description error message
+     */
+    private Error(int error, String description)
+    {
+        super(description);
+        this.error = error;
+        this.description = description;
+    }
+
+    /**
+     * Get the APR error code of the exception.
+     *
+     * @return error of the Exception
+     */
+    public int getError()
+    {
+        return error;
+    }
+
+    /**
+     * Get the APR description of the exception.
+     *
+     * @return description of the Exception
+     */
+    public String getDescription()
+    {
+        return description;
+    }
+
+    /**
+     * Get the last platform error.
+     * @return apr_status_t the last platform error, folded into apr_status_t, on most platforms
+     * This retrieves errno, or calls a GetLastError() style function, and
+     *      folds it with APR_FROM_OS_ERROR.  Some platforms (such as OS2) have no
+     *      such mechanism, so this call may be unsupported.  Do NOT use this
+     *      call for socket errors from socket, send, recv etc!
+     */
+    public static native int osError();
+
+    /**
+     * Get the last platform socket error.
+     * @return the last socket error, folded into apr_status_t, on all platforms
+     * This retrieves errno or calls a GetLastSocketError() style function,
+     *      and folds it with APR_FROM_OS_ERROR.
+     */
+    public static native int netosError();
+
+    /**
+     * Return a human readable string describing the specified error.
+     * @param statcode The error code the get a string for.
+     * @return The error string.
+    */
+    public static native String strerror(int statcode);
+
+}
diff --git a/connectors/jni/java/org/apache/tomcat/jni/File.java b/connectors/jni/java/org/apache/tomcat/jni/File.java
new file mode 100644
index 0000000..7f72d55
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/File.java
@@ -0,0 +1,703 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+/* Import needed classes */
+import java.nio.ByteBuffer;
+/** File
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public class File {
+
+    /** Open the file for reading */
+    public static final int APR_FOPEN_READ       = 0x00001;
+    /** Open the file for writing */
+    public static final int APR_FOPEN_WRITE      = 0x00002;
+    /** Create the file if not there */
+    public static final int APR_FOPEN_CREATE     = 0x00004;
+    /** Append to the end of the file */
+    public static final int APR_FOPEN_APPEND     = 0x00008;
+    /** Open the file and truncate to 0 length */
+    public static final int APR_FOPEN_TRUNCATE   = 0x00010;
+    /** Open the file in binary mode */
+    public static final int APR_FOPEN_BINARY     = 0x00020;
+    /** Open should fail if APR_CREATE and file exists. */
+    public static final int APR_FOPEN_EXCL       = 0x00040;
+    /** Open the file for buffered I/O */
+    public static final int APR_FOPEN_BUFFERED   = 0x00080;
+    /** Delete the file after close */
+    public static final int APR_FOPEN_DELONCLOSE = 0x00100;
+    /** Platform dependent tag to open the file for
+     * use across multiple threads
+     */
+    public static final int APR_FOPEN_XTHREAD     = 0x00200;
+    /** Platform dependent support for higher level locked read/write
+     * access to support writes across process/machines
+     */
+    public static final int APR_FOPEN_SHARELOCK   = 0x00400;
+    /** Do not register a cleanup when the file is opened */
+    public static final int APR_FOPEN_NOCLEANUP   = 0x00800;
+    /** Advisory flag that this file should support
+     * apr_socket_sendfile operation
+     */
+    public static final int APR_FOPEN_SENDFILE_ENABLED = 0x01000;
+    /** Platform dependent flag to enable large file support;
+     * <br /><b>Warning :</b> The APR_LARGEFILE flag only has effect on some platforms
+     * where sizeof(apr_off_t) == 4.  Where implemented, it allows opening
+     * and writing to a file which exceeds the size which can be
+     * represented by apr_off_t (2 gigabytes).  When a file's size does
+     * exceed 2Gb, apr_file_info_get() will fail with an error on the
+     * descriptor, likewise apr_stat()/apr_lstat() will fail on the
+     * filename.  apr_dir_read() will fail with APR_INCOMPLETE on a
+     * directory entry for a large file depending on the particular
+     * APR_FINFO_* flags.  Generally, it is not recommended to use this
+     * flag.
+     */
+    public static final int APR_FOPEN_LARGEFILE      = 0x04000;
+
+    /** Set the file position */
+    public static final int APR_SET = 0;
+    /** Current */
+    public static final int APR_CUR = 1;
+    /** Go to end of file */
+    public static final int APR_END = 2;
+
+    /* flags for apr_file_attrs_set */
+
+    /** File is read-only */
+    public static final int APR_FILE_ATTR_READONLY   = 0x01;
+    /** File is executable */
+    public static final int APR_FILE_ATTR_EXECUTABLE = 0x02;
+    /** File is hidden */
+    public static final int APR_FILE_ATTR_HIDDEN     = 0x04;
+
+
+    /* File lock types/flags */
+
+    /** Shared lock. More than one process or thread can hold a shared lock
+     * at any given time. Essentially, this is a "read lock", preventing
+     * writers from establishing an exclusive lock.
+     */
+    public static final int APR_FLOCK_SHARED    = 1;
+
+    /** Exclusive lock. Only one process may hold an exclusive lock at any
+     * given time. This is analogous to a "write lock".
+     */
+    public static final int APR_FLOCK_EXCLUSIVE = 2;
+    /** mask to extract lock type */
+    public static final int APR_FLOCK_TYPEMASK  = 0x000F;
+    /** do not block while acquiring the file lock */
+    public static final int APR_FLOCK_NONBLOCK  = 0x0010;
+
+    /* apr_filetype_e values for the filetype member of the
+     * apr_file_info_t structure
+     * <br /><b>Warning :</b>: Not all of the filetypes below can be determined.
+     * For example, a given platform might not correctly report
+     * a socket descriptor as APR_SOCK if that type isn't
+     * well-identified on that platform.  In such cases where
+     * a filetype exists but cannot be described by the recognized
+     * flags below, the filetype will be APR_UNKFILE.  If the
+     * filetype member is not determined, the type will be APR_NOFILE.
+     */
+
+    /** no file type determined */
+    public static final int APR_NOFILE  = 0;
+    /** a regular file */
+    public static final int APR_REG     = 1;
+    /** a directory */
+    public static final int APR_DIR     = 2;
+    /** a character device */
+    public static final int APR_CHR     = 3;
+    /** a block device */
+    public static final int APR_BLK     = 4;
+    /** a FIFO / pipe */
+    public static final int APR_PIPE    = 5;
+    /** a symbolic link */
+    public static final int APR_LNK     = 6;
+    /** a [unix domain] socket */
+    public static final int APR_SOCK    = 7;
+    /** a file of some other unknown type */
+    public static final int APR_UNKFILE = 127;
+
+
+    /*
+     * apr_file_permissions File Permissions flags
+     */
+
+    public static final int APR_FPROT_USETID     = 0x8000; /** Set user id */
+    public static final int APR_FPROT_UREAD      = 0x0400; /** Read by user */
+    public static final int APR_FPROT_UWRITE     = 0x0200; /** Write by user */
+    public static final int APR_FPROT_UEXECUTE   = 0x0100; /** Execute by user */
+
+    public static final int APR_FPROT_GSETID     = 0x4000; /** Set group id */
+    public static final int APR_FPROT_GREAD      = 0x0040; /** Read by group */
+    public static final int APR_FPROT_GWRITE     = 0x0020; /** Write by group */
+    public static final int APR_FPROT_GEXECUTE   = 0x0010; /** Execute by group */
+
+    public static final int APR_FPROT_WSTICKY    = 0x2000; /** Sticky bit */
+    public static final int APR_FPROT_WREAD      = 0x0004; /** Read by others */
+    public static final int APR_FPROT_WWRITE     = 0x0002; /** Write by others */
+    public static final int APR_FPROT_WEXECUTE   = 0x0001; /** Execute by others */
+    public static final int APR_FPROT_OS_DEFAULT = 0x0FFF; /** use OS's default permissions */
+
+
+    public static final int APR_FINFO_LINK   = 0x00000001; /** Stat the link not the file itself if it is a link */
+    public static final int APR_FINFO_MTIME  = 0x00000010; /** Modification Time */
+    public static final int APR_FINFO_CTIME  = 0x00000020; /** Creation or inode-changed time */
+    public static final int APR_FINFO_ATIME  = 0x00000040; /** Access Time */
+    public static final int APR_FINFO_SIZE   = 0x00000100; /** Size of the file */
+    public static final int APR_FINFO_CSIZE  = 0x00000200; /** Storage size consumed by the file */
+    public static final int APR_FINFO_DEV    = 0x00001000; /** Device */
+    public static final int APR_FINFO_INODE  = 0x00002000; /** Inode */
+    public static final int APR_FINFO_NLINK  = 0x00004000; /** Number of links */
+    public static final int APR_FINFO_TYPE   = 0x00008000; /** Type */
+    public static final int APR_FINFO_USER   = 0x00010000; /** User */
+    public static final int APR_FINFO_GROUP  = 0x00020000; /** Group */
+    public static final int APR_FINFO_UPROT  = 0x00100000; /** User protection bits */
+    public static final int APR_FINFO_GPROT  = 0x00200000; /** Group protection bits */
+    public static final int APR_FINFO_WPROT  = 0x00400000; /** World protection bits */
+    public static final int APR_FINFO_ICASE  = 0x01000000; /** if dev is case insensitive */
+    public static final int APR_FINFO_NAME   = 0x02000000; /** ->name in proper case */
+
+    public static final int APR_FINFO_MIN    = 0x00008170; /** type, mtime, ctime, atime, size */
+    public static final int APR_FINFO_IDENT  = 0x00003000; /** dev and inode */
+    public static final int APR_FINFO_OWNER  = 0x00030000; /** user and group */
+    public static final int APR_FINFO_PROT   = 0x00700000; /**  all protections */
+    public static final int APR_FINFO_NORM   = 0x0073b170; /**  an atomic unix apr_stat() */
+    public static final int APR_FINFO_DIRENT = 0x02000000; /**  an atomic unix apr_dir_read() */
+
+
+
+    /**
+     * Open the specified file.
+     * @param fname The full path to the file (using / on all systems)
+     * @param flag Or'ed value of:
+     * <PRE>
+     * APR_FOPEN_READ              open for reading
+     * APR_FOPEN_WRITE             open for writing
+     * APR_FOPEN_CREATE            create the file if not there
+     * APR_FOPEN_APPEND            file ptr is set to end prior to all writes
+     * APR_FOPEN_TRUNCATE          set length to zero if file exists
+     * APR_FOPEN_BINARY            not a text file (This flag is ignored on
+     *                             UNIX because it has no meaning)
+     * APR_FOPEN_BUFFERED          buffer the data.  Default is non-buffered
+     * APR_FOPEN_EXCL              return error if APR_CREATE and file exists
+     * APR_FOPEN_DELONCLOSE        delete the file after closing.
+     * APR_FOPEN_XTHREAD           Platform dependent tag to open the file
+     *                             for use across multiple threads
+     * APR_FOPEN_SHARELOCK         Platform dependent support for higher
+     *                             level locked read/write access to support
+     *                             writes across process/machines
+     * APR_FOPEN_NOCLEANUP         Do not register a cleanup with the pool
+     *                             passed in on the <EM>pool</EM> argument (see below).
+     *                             The apr_os_file_t handle in apr_file_t will not
+     *                             be closed when the pool is destroyed.
+     * APR_FOPEN_SENDFILE_ENABLED  Open with appropriate platform semantics
+     *                             for sendfile operations.  Advisory only,
+     *                             apr_socket_sendfile does not check this flag.
+     * </PRE>
+     * @param perm Access permissions for file.
+     * @param pool The pool to use.
+     * If perm is APR_OS_DEFAULT and the file is being created,
+     * appropriate default permissions will be used.
+     * @return The opened file descriptor.
+     */
+    public static native long open(String fname, int flag, int perm, long pool)
+        throws Error;
+
+    /**
+     * Close the specified file.
+     * @param file The file descriptor to close.
+     */
+    public static native int close(long file);
+
+    /**
+     * Flush the file's buffer.
+     * @param thefile The file descriptor to flush
+     */
+    public static native int flush(long thefile);
+
+    /**
+     * Open a temporary file
+     * @param templ The template to use when creating a temp file.
+     * @param flags The flags to open the file with. If this is zero,
+     *              the file is opened with
+     *              APR_CREATE | APR_READ | APR_WRITE | APR_EXCL | APR_DELONCLOSE
+     * @param pool The pool to allocate the file out of.
+     * @return The apr file to use as a temporary file.
+     * 
+     * This function  generates  a unique temporary file name from template.
+     * The last six characters of template must be XXXXXX and these are replaced
+     * with a string that makes the filename unique. Since it will  be  modified,
+     * template must not be a string constant, but should be declared as a character
+     * array.
+     *
+     */
+    public static native long mktemp(String templ, int flags, long pool)
+        throws Error;
+
+    /**
+     * Delete the specified file.
+     * @param path The full path to the file (using / on all systems)
+     * @param pool The pool to use.
+     * If the file is open, it won't be removed until all
+     * instances are closed.
+     */
+    public static native int remove(String path, long pool);
+
+    /**
+     * Rename the specified file.
+     * <br /><b>Warning :</b> If a file exists at the new location, then it will be
+     * overwritten.  Moving files or directories across devices may not be
+     * possible.
+     * @param fromPath The full path to the original file (using / on all systems)
+     * @param toPath The full path to the new file (using / on all systems)
+     * @param pool The pool to use.
+     */
+    public static native int rename(String fromPath, String toPath, long pool);
+
+    /**
+     * Copy the specified file to another file.
+     * The new file does not need to exist, it will be created if required.
+     * <br /><b>Warning :</b> If the new file already exists, its contents will be overwritten.
+     * @param fromPath The full path to the original file (using / on all systems)
+     * @param toPath The full path to the new file (using / on all systems)
+     * @param perms Access permissions for the new file if it is created.
+     *     In place of the usual or'd combination of file permissions, the
+     *     value APR_FILE_SOURCE_PERMS may be given, in which case the source
+     *     file's permissions are copied.
+     * @param pool The pool to use.
+     */
+    public static native int copy(String fromPath, String toPath, int perms, long pool);
+
+    /**
+     * Append the specified file to another file.
+     * The new file does not need to exist, it will be created if required.
+     * @param fromPath The full path to the source file (use / on all systems)
+     * @param toPath The full path to the destination file (use / on all systems)
+     * @param perms Access permissions for the destination file if it is created.
+     *     In place of the usual or'd combination of file permissions, the
+     *     value APR_FILE_SOURCE_PERMS may be given, in which case the source
+     *     file's permissions are copied.
+     * @param pool The pool to use.
+     */
+    public static native int append(String fromPath, String toPath, int perms, long pool);
+
+    /**
+     * Write the string into the specified file.
+     * @param str The string to write. Must be NUL terminated!
+     * @param thefile The file descriptor to write to
+     */
+    public static native int puts(byte [] str, long thefile);
+
+    /**
+     * Move the read/write file offset to a specified byte within a file.
+     * @param thefile The file descriptor
+     * @param where How to move the pointer, one of:
+     * <PRE>
+     * APR_SET  --  set the offset to offset
+     * APR_CUR  --  add the offset to the current position
+     * APR_END  --  add the offset to the current file size
+     * </PRE>
+     * @param offset The offset to move the pointer to.
+     * @return Offset the pointer was actually moved to.
+     */
+    public static native long seek(long thefile, int where, long offset)
+        throws Error;
+
+    /**
+     * Write a character into the specified file.
+     * @param ch The character to write.
+     * @param thefile The file descriptor to write to
+     */
+    public static native int putc(byte ch, long thefile);
+
+    /**
+     * Put a character back onto a specified stream.
+     * @param ch The character to write.
+     * @param thefile The file descriptor to write to
+     */
+    public static native int ungetc(byte ch, long thefile);
+
+    /**
+     * Write data to the specified file.
+     *
+     * Write will write up to the specified number of
+     * bytes, but never more.  If the OS cannot write that many bytes, it
+     * will write as many as it can.  The third argument is modified to
+     * reflect the * number of bytes written.
+     *
+     * It is possible for both bytes to be written and an error to
+     * be returned.  APR_EINTR is never returned.
+     * @param thefile The file descriptor to write to.
+     * @param buf The buffer which contains the data.
+     * @param offset Start offset in buf
+     * @param nbytes The number of bytes to write; (-1) for full array.
+     * @return The number of bytes written.
+     */
+    public static native int write(long thefile, byte[] buf, int offset, int nbytes);
+
+    /**
+     * Write data to the specified file.
+     *
+     * Write will write up to the specified number of
+     * bytes, but never more.  If the OS cannot write that many bytes, it
+     * will write as many as it can.  The third argument is modified to
+     * reflect the * number of bytes written.
+     *
+     * It is possible for both bytes to be written and an error to
+     * be returned.  APR_EINTR is never returned.
+     * @param thefile The file descriptor to write to.
+     * @param buf The direct Byte buffer which contains the data.
+     * @param offset Start offset in buf
+     * @param nbytes The number of bytes to write
+     * @return The number of bytes written.
+     */
+    public static native int writeb(long thefile, ByteBuffer buf, int offset, int nbytes);
+
+    /**
+     * Write data to the specified file, ensuring that all of the data is
+     * written before returning.
+     *
+     * Write will write up to the specified number of
+     * bytes, but never more.  If the OS cannot write that many bytes, the
+     * process/thread will block until they can be written. Exceptional
+     * error such as "out of space" or "pipe closed" will terminate with
+     * an error.
+     *
+     * It is possible for both bytes to be written and an error to
+     * be returned.  And if *bytes_written is less than nbytes, an
+     * accompanying error is _always_ returned.
+     *
+     * APR_EINTR is never returned.
+     * @param thefile The file descriptor to write to.
+     * @param buf The buffer which contains the data.
+     * @param offset Start offset in buf
+     * @param nbytes The number of bytes to write; (-1) for full array.
+     * @return The number of bytes written.
+     */
+    public static native int writeFull(long thefile, byte[] buf, int offset, int nbytes);
+
+    /**
+     * Write data to the specified file, ensuring that all of the data is
+     * written before returning.
+     *
+     * Write will write up to the specified number of
+     * bytes, but never more.  If the OS cannot write that many bytes, the
+     * process/thread will block until they can be written. Exceptional
+     * error such as "out of space" or "pipe closed" will terminate with
+     * an error.
+     *
+     * It is possible for both bytes to be written and an error to
+     * be returned.  And if *bytes_written is less than nbytes, an
+     * accompanying error is _always_ returned.
+     *
+     * APR_EINTR is never returned.
+     * @param thefile The file descriptor to write to.
+     * @param buf The direct ByteBuffer which contains the data.
+     * @param offset Start offset in buf
+     * @param nbytes The number of bytes to write.
+     * @return The number of bytes written.
+     */
+    public static native int writeFullb(long thefile, ByteBuffer buf, int offset, int nbytes);
+
+    /**
+     * Write data from aray of byte arrays to the specified file.
+     *
+     * It is possible for both bytes to be written and an error to
+     * be returned.  APR_EINTR is never returned.
+     *
+     * apr_file_writev is available even if the underlying
+     * operating system doesn't provide writev().
+     * @param thefile The file descriptor to write to.
+     * @param vec The array from which to get the data to write to the file.
+     * @return The number of bytes written.
+     */
+    public static native int writev(long thefile, byte[][] vec);
+
+    /**
+     * Write data from aray of byte arrays to the specified file,
+     * ensuring that all of the data is written before returning.
+     *
+     * writevFull is available even if the underlying
+     * operating system doesn't provide writev().
+     * @param thefile The file descriptor to write to.
+     * @param vec The array from which to get the data to write to the file.
+     * @return The number of bytes written.
+     */
+    public static native int writevFull(long thefile, byte[][] vec);
+
+    /**
+     * Read data from the specified file.
+     *
+     * apr_file_read will read up to the specified number of
+     * bytes, but never more.  If there isn't enough data to fill that
+     * number of bytes, all of the available data is read.  The third
+     * argument is modified to reflect the number of bytes read.  If a
+     * char was put back into the stream via ungetc, it will be the first
+     * character returned.
+     *
+     * It is not possible for both bytes to be read and an APR_EOF
+     * or other error to be returned.  APR_EINTR is never returned.
+     * @param thefile The file descriptor to read from.
+     * @param buf The buffer to store the data to.
+     * @param offset Start offset in buf
+     * @param nbytes The number of bytes to read (-1) for full array.
+     * @return the number of bytes read.
+     */
+    public static native int read(long thefile, byte[] buf,  int offset, int nbytes);
+
+    /**
+     * Read data from the specified file.
+     *
+     * apr_file_read will read up to the specified number of
+     * bytes, but never more.  If there isn't enough data to fill that
+     * number of bytes, all of the available data is read.  The third
+     * argument is modified to reflect the number of bytes read.  If a
+     * char was put back into the stream via ungetc, it will be the first
+     * character returned.
+     *
+     * It is not possible for both bytes to be read and an APR_EOF
+     * or other error to be returned.  APR_EINTR is never returned.
+     * @param thefile The file descriptor to read from.
+     * @param buf The direct Byte buffer to store the data to.
+     * @param offset Start offset in buf
+     * @param nbytes The number of bytes to read.
+     * @return the number of bytes read.
+     */
+    public static native int readb(long thefile, ByteBuffer buf,  int offset, int nbytes);
+
+    /**
+     * Read data from the specified file, ensuring that the buffer is filled
+     * before returning.
+     *
+     * Read will read up to the specified number of
+     * bytes, but never more.  If there isn't enough data to fill that
+     * number of bytes, then the process/thread will block until it is
+     * available or EOF is reached.  If a char was put back into the
+     * stream via ungetc, it will be the first character returned.
+     *
+     * It is possible for both bytes to be read and an error to be
+     * returned.  And if *bytes_read is less than nbytes, an accompanying
+     * error is _always_ returned.
+     *
+     * APR_EINTR is never returned.
+     * @param thefile The file descriptor to read from.
+     * @param buf The buffer to store the data to.
+     * @param offset Start offset in buf
+     * @param nbytes The number of bytes to read (-1) for full array.
+     * @return the number of bytes read.
+     */
+    public static native int readFull(long thefile, byte[] buf,  int offset, int nbytes);
+
+    /**
+     * Read data from the specified file, ensuring that the buffer is filled
+     * before returning.
+     *
+     * Read will read up to the specified number of
+     * bytes, but never more.  If there isn't enough data to fill that
+     * number of bytes, then the process/thread will block until it is
+     * available or EOF is reached.  If a char was put back into the
+     * stream via ungetc, it will be the first character returned.
+     *
+     * It is possible for both bytes to be read and an error to be
+     * returned.  And if *bytes_read is less than nbytes, an accompanying
+     * error is _always_ returned.
+     *
+     * APR_EINTR is never returned.
+     * @param thefile The file descriptor to read from.
+     * @param buf The direct ByteBuffer to store the data to.
+     * @param offset Start offset in buf
+     * @param nbytes The number of bytes to read.
+     * @return the number of bytes read.
+     */
+    public static native int readFullb(long thefile, ByteBuffer buf,  int offset, int nbytes);
+
+    /**
+     * Read a string from the specified file.
+     * The buffer will be NUL-terminated if any characters are stored.
+     * @param buf The buffer to store the string in.
+     * @param offset Start offset in buf
+     * @param thefile The file descriptor to read from
+     */
+    public static native int gets(byte[] buf,  int offset, long thefile);
+
+
+    /**
+     * Read a character from the specified file.
+     * @param thefile The file descriptor to read from
+     * @return The readed character
+     */
+    public static native int getc(long thefile)
+        throws Error;
+
+    /**
+     * Are we at the end of the file
+     * @param fptr The apr file we are testing.
+     * @return Returns APR_EOF if we are at the end of file, APR_SUCCESS otherwise.
+     */
+    public static native int eof(long fptr);
+
+    /**
+     * return the file name of the current file.
+     * @param thefile The currently open file.
+     */
+    public static native String nameGet(long thefile);
+
+    /**
+     * Set the specified file's permission bits.
+     * <br /><b>Warning :</b> Some platforms may not be able to apply all of the
+     * available permission bits; APR_INCOMPLETE will be returned if some
+     * permissions are specified which could not be set.
+     * <br /><b>Warning :</b> Platforms which do not implement this feature will return
+     * APR_ENOTIMPL.
+     * @param fname The file (name) to apply the permissions to.
+     * @param perms The permission bits to apply to the file.
+     *
+     */
+    public static native int permsSet(String fname, int perms);
+
+    /**
+     * Set attributes of the specified file.
+     * This function should be used in preference to explict manipulation
+     *      of the file permissions, because the operations to provide these
+     *      attributes are platform specific and may involve more than simply
+     *      setting permission bits.
+     * <br /><b>Warning :</b> Platforms which do not implement this feature will return
+     *      APR_ENOTIMPL.
+     * @param fname The full path to the file (using / on all systems)
+     * @param attributes Or'd combination of
+     * <PRE>
+     *            APR_FILE_ATTR_READONLY   - make the file readonly
+     *            APR_FILE_ATTR_EXECUTABLE - make the file executable
+     *            APR_FILE_ATTR_HIDDEN     - make the file hidden
+     * </PRE>
+     * @param mask Mask of valid bits in attributes.
+     * @param pool the pool to use.
+     */
+    public static native int  attrsSet(String fname, int attributes, int mask, long pool);
+
+    /**
+     * Set the mtime of the specified file.
+     * <br /><b>Warning :</b> Platforms which do not implement this feature will return
+     *      APR_ENOTIMPL.
+     * @param fname The full path to the file (using / on all systems)
+     * @param mtime The mtime to apply to the file in microseconds
+     * @param pool The pool to use.
+     */
+    public static native int  mtimeSet(String fname, long mtime, long pool);
+
+    /**
+     * Establish a lock on the specified, open file. The lock may be advisory
+     * or mandatory, at the discretion of the platform. The lock applies to
+     * the file as a whole, rather than a specific range. Locks are established
+     * on a per-thread/process basis; a second lock by the same thread will not
+     * block.
+     * @param thefile The file to lock.
+     * @param type The type of lock to establish on the file.
+     */
+    public static native int lock(long thefile, int type);
+
+    /**
+     * Remove any outstanding locks on the file.
+     * @param thefile The file to unlock.
+     */
+    public static native int unlock(long thefile);
+
+    /**
+     * Retrieve the flags that were passed into apr_file_open()
+     * when the file was opened.
+     * @param file The file to retrive flags.
+     * @return the flags
+     */
+    public static native int flagsGet(long file);
+
+    /**
+     * Truncate the file's length to the specified offset
+     * @param fp The file to truncate
+     * @param offset The offset to truncate to.
+     */
+    public static native int trunc(long fp, long offset);
+
+    /**
+     * Create an anonymous pipe.
+     * @param io io[0] The file descriptors to use as input to the pipe.
+     *           io[1] The file descriptor to use as output from the pipe.
+     * @param pool The pool to operate on.
+     */
+    public static native int pipeCreate(long [] io, long pool);
+
+    /**
+     * Get the timeout value for a pipe or manipulate the blocking state.
+     * @param thepipe The pipe we are getting a timeout for.
+     * @return The current timeout value in microseconds.
+     */
+    public static native long pipeTimeoutGet(long thepipe)
+        throws Error;
+
+    /**
+     * Set the timeout value for a pipe or manipulate the blocking state.
+     * @param thepipe The pipe we are setting a timeout on.
+     * @param timeout The timeout value in microseconds.  Values < 0 mean wait
+     *        forever, 0 means do not wait at all.
+     */
+    public static native int pipeTimeoutSet(long thepipe, long timeout);
+
+    /**
+     * Duplicate the specified file descriptor.
+     * @param newFile The file to duplicate.
+     * newFile must point to a valid apr_file_t, or point to NULL.
+     * @param oldFile The file to duplicate.
+     * @param pool The pool to use for the new file.
+     * @return Duplicated file structure.
+     */
+    public static native long dup(long newFile, long oldFile, long pool)
+        throws Error;
+
+    /**
+     * Duplicate the specified file descriptor and close the original.
+     * @param newFile The old file that is to be closed and reused.
+     * newFile MUST point at a valid apr_file_t. It cannot be NULL.
+     * @param oldFile The file to duplicate.
+     * @param pool The pool to use for the new file.
+     * @return Status code.
+     */
+    public static native int dup2(long newFile, long oldFile, long pool);
+
+    /**
+     * Get the specified file's stats.  The file is specified by filename,
+     * instead of using a pre-opened file.
+     * @param finfo Where to store the information about the file, which is
+     * never touched if the call fails.
+     * @param fname The name of the file to stat.
+     * @param wanted The desired apr_finfo_t fields, as a bit flag of APR_FINFO_ values
+     * @param pool the pool to use to allocate the new file.
+     */
+    public static native int stat(FileInfo finfo, String fname, int wanted, long pool);
+
+    /**
+     * Get the specified file's stats.
+     * @param finfo Where to store the information about the file.
+     * @param wanted The desired apr_finfo_t fields, as a bit flag of APR_FINFO_ values
+     * @param thefile The file to get information about.
+     */
+    public static native int infoGet(FileInfo finfo, int wanted, long thefile);
+
+}
diff --git a/connectors/jni/java/org/apache/tomcat/jni/FileInfo.java b/connectors/jni/java/org/apache/tomcat/jni/FileInfo.java
new file mode 100644
index 0000000..59a1cbd
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/FileInfo.java
@@ -0,0 +1,67 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+/** Fileinfo
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public class FileInfo {
+
+    /** Allocates memory and closes lingering handles in the specified pool */
+    public long pool;
+    /** The bitmask describing valid fields of this apr_finfo_t structure
+     *  including all available 'wanted' fields and potentially more */
+    public int valid;
+    /** The access permissions of the file.  Mimics Unix access rights. */
+    public int protection;
+    /** The type of file.  One of APR_REG, APR_DIR, APR_CHR, APR_BLK, APR_PIPE,
+     * APR_LNK or APR_SOCK.  If the type is undetermined, the value is APR_NOFILE.
+     * If the type cannot be determined, the value is APR_UNKFILE.
+     */
+    public int filetype;
+    /** The user id that owns the file */
+    public int user;
+    /** The group id that owns the file */
+    public int group;
+    /** The inode of the file. */
+    public int inode;
+    /** The id of the device the file is on. */
+    public int device;
+    /** The number of hard links to the file. */
+    public int nlink;
+    /** The size of the file */
+    public long size;
+    /** The storage size consumed by the file */
+    public long csize;
+    /** The time the file was last accessed */
+    public long atime;
+    /** The time the file was last modified */
+    public long mtime;
+    /** The time the file was created, or the inode was last changed */
+    public long ctime;
+    /** The pathname of the file (possibly unrooted) */
+    public String fname;
+    /** The file's name (no path) in filesystem case */
+    public String name;
+    /** The file's handle, if accessed (can be submitted to apr_duphandle) */
+    public long filehand;
+
+}
diff --git a/connectors/jni/java/org/apache/tomcat/jni/Global.java b/connectors/jni/java/org/apache/tomcat/jni/Global.java
new file mode 100644
index 0000000..47638e4
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/Global.java
@@ -0,0 +1,96 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+/** Global
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public class Global {
+
+    /**
+     * Create and initialize a mutex that can be used to synchronize both
+     * processes and threads. Note: There is considerable overhead in using
+     * this API if only cross-process or cross-thread mutual exclusion is
+     * required. See apr_proc_mutex.h and apr_thread_mutex.h for more
+     * specialized lock routines.
+     * <br /><b>Warning :</b> Check APR_HAS_foo_SERIALIZE defines to see if the platform supports
+     *          APR_LOCK_foo.  Only APR_LOCK_DEFAULT is portable.
+     * @param fname A file name to use if the lock mechanism requires one.  This
+     *        argument should always be provided.  The lock code itself will
+     *        determine if it should be used.
+     * @param mech The mechanism to use for the interprocess lock, if any; one of
+     * <PRE>
+     *            APR_LOCK_FCNTL
+     *            APR_LOCK_FLOCK
+     *            APR_LOCK_SYSVSEM
+     *            APR_LOCK_POSIXSEM
+     *            APR_LOCK_PROC_PTHREAD
+     *            APR_LOCK_DEFAULT     pick the default mechanism for the platform
+     * </PRE>
+     * @param pool the pool from which to allocate the mutex.
+     * @return Newly created mutex.
+     */
+    public static native long create(String fname, int mech, long pool)
+        throws Error;
+
+    /**
+     * Re-open a mutex in a child process.
+     * @param fname A file name to use if the mutex mechanism requires one.  This
+     *              argument should always be provided.  The mutex code itself will
+     *              determine if it should be used.  This filename should be the
+     *              same one that was passed to apr_proc_mutex_create().
+     * @param pool The pool to operate on.
+     * This function must be called to maintain portability, even
+     *         if the underlying lock mechanism does not require it.
+     * @return Newly opened mutex.
+     */
+    public static native long childInit(String fname, long pool)
+        throws Error;
+
+    /**
+     * Acquire the lock for the given mutex. If the mutex is already locked,
+     * the current thread will be put to sleep until the lock becomes available.
+     * @param mutex the mutex on which to acquire the lock.
+     */
+    public static native int lock(long mutex);
+
+    /**
+     * Attempt to acquire the lock for the given mutex. If the mutex has already
+     * been acquired, the call returns immediately with APR_EBUSY. Note: it
+     * is important that the APR_STATUS_IS_EBUSY(s) macro be used to determine
+     * if the return value was APR_EBUSY, for portability reasons.
+     * @param mutex the mutex on which to attempt the lock acquiring.
+     */
+    public static native int trylock(long mutex);
+
+    /**
+     * Release the lock for the given mutex.
+     * @param mutex the mutex from which to release the lock.
+     */
+    public static native int unlock(long mutex);
+
+    /**
+     * Destroy the mutex and free the memory associated with the lock.
+     * @param mutex the mutex to destroy.
+     */
+    public static native int destroy(long mutex);
+
+}
diff --git a/connectors/jni/java/org/apache/tomcat/jni/Library.java b/connectors/jni/java/org/apache/tomcat/jni/Library.java
new file mode 100644
index 0000000..d178c78
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/Library.java
@@ -0,0 +1,208 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+/** Library
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public final class Library {
+
+    /* Default library names */
+    private static String [] NAMES = {"tcnative-1", "libtcnative-1"};
+    /*
+     * A handle to the unique Library singleton instance.
+     */
+    static private Library _instance = null;
+
+    private Library()
+    {
+        boolean loaded = false;
+        String err = "";
+        for (int i = 0; i < NAMES.length; i++) {
+            try {
+                System.loadLibrary(NAMES[i]);
+                loaded = true;
+            }
+            catch (Throwable e) {
+                if ( i > 0)
+                    err += ", ";
+                err +=  e.getMessage();
+            }
+            if (loaded)
+                break;
+        }
+        if (!loaded) {
+            err += "(";
+            err += System.getProperty("java.library.path");
+            err += ")";
+            throw new UnsatisfiedLinkError(err);
+        }
+    }
+
+    private Library(String libraryName)
+    {
+        System.loadLibrary(libraryName);
+    }
+
+    /* create global TCN's APR pool
+     * This has to be the first call to TCN library.
+     */
+    private static native boolean initialize();
+    /* destroy global TCN's APR pool
+     * This has to be the last call to TCN library.
+     */
+    public static native void terminate();
+    /* Internal function for loading APR Features */
+    private static native boolean has(int what);
+    /* Internal function for loading APR Features */
+    private static native int version(int what);
+    /* Internal function for loading APR sizes */
+    private static native int size(int what);
+
+    /* TCN_MAJOR_VERSION */
+    public static int TCN_MAJOR_VERSION  = 0;
+    /* TCN_MINOR_VERSION */
+    public static int TCN_MINOR_VERSION  = 0;
+    /* TCN_PATCH_VERSION */
+    public static int TCN_PATCH_VERSION  = 0;
+    /* TCN_IS_DEV_VERSION */
+    public static int TCN_IS_DEV_VERSION = 0;
+    /* APR_MAJOR_VERSION */
+    public static int APR_MAJOR_VERSION  = 0;
+    /* APR_MINOR_VERSION */
+    public static int APR_MINOR_VERSION  = 0;
+    /* APR_PATCH_VERSION */
+    public static int APR_PATCH_VERSION  = 0;
+    /* APR_IS_DEV_VERSION */
+    public static int APR_IS_DEV_VERSION = 0;
+
+    /* TCN_VERSION_STRING */
+    public static native String versionString();
+    /* APR_VERSION_STRING */
+    public static native String aprVersionString();
+
+    /*  APR Feature Macros */
+    public static boolean APR_HAVE_IPV6           = false;
+    public static boolean APR_HAS_SHARED_MEMORY   = false;
+    public static boolean APR_HAS_THREADS         = false;
+    public static boolean APR_HAS_SENDFILE        = false;
+    public static boolean APR_HAS_MMAP            = false;
+    public static boolean APR_HAS_FORK            = false;
+    public static boolean APR_HAS_RANDOM          = false;
+    public static boolean APR_HAS_OTHER_CHILD     = false;
+    public static boolean APR_HAS_DSO             = false;
+    public static boolean APR_HAS_SO_ACCEPTFILTER = false;
+    public static boolean APR_HAS_UNICODE_FS      = false;
+    public static boolean APR_HAS_PROC_INVOKED    = false;
+    public static boolean APR_HAS_USER            = false;
+    public static boolean APR_HAS_LARGE_FILES     = false;
+    public static boolean APR_HAS_XTHREAD_FILES   = false;
+    public static boolean APR_HAS_OS_UUID         = false;
+    /* Are we big endian? */
+    public static boolean APR_IS_BIGENDIAN        = false;
+    /* APR sets APR_FILES_AS_SOCKETS to 1 on systems where it is possible
+     * to poll on files/pipes.
+     */
+    public static boolean APR_FILES_AS_SOCKETS    = false;
+    /* This macro indicates whether or not EBCDIC is the native character set.
+     */
+    public static boolean APR_CHARSET_EBCDIC      = false;
+    /* Is the TCP_NODELAY socket option inherited from listening sockets?
+     */
+    public static boolean APR_TCP_NODELAY_INHERITED = false;
+    /* Is the O_NONBLOCK flag inherited from listening sockets?
+     */
+    public static boolean APR_O_NONBLOCK_INHERITED  = false;
+
+
+    public static int APR_SIZEOF_VOIDP;
+    public static int APR_PATH_MAX;
+    public static int APRMAXHOSTLEN;
+    public static int APR_MAX_IOVEC_SIZE;
+    public static int APR_MAX_SECS_TO_LINGER;
+    public static int APR_MMAP_THRESHOLD;
+    public static int APR_MMAP_LIMIT;
+
+    /* return global TCN's APR pool */
+    public static native long globalPool();
+
+    /**
+     * Setup any APR internal data structures.  This MUST be the first function
+     * called for any APR library.
+     * @param libraryName the name of the library to load
+     */
+    static public boolean initialize(String libraryName)
+        throws Exception
+    {
+        if (_instance == null) {
+            if (libraryName == null)
+                _instance = new Library();
+            else
+                _instance = new Library(libraryName);
+            TCN_MAJOR_VERSION  = version(0x01);
+            TCN_MINOR_VERSION  = version(0x02);
+            TCN_PATCH_VERSION  = version(0x03);
+            TCN_IS_DEV_VERSION = version(0x04);
+            APR_MAJOR_VERSION  = version(0x11);
+            APR_MINOR_VERSION  = version(0x12);
+            APR_PATCH_VERSION  = version(0x13);
+            APR_IS_DEV_VERSION = version(0x14);
+
+            APR_SIZEOF_VOIDP        = size(1);
+            APR_PATH_MAX            = size(2);
+            APRMAXHOSTLEN           = size(3);
+            APR_MAX_IOVEC_SIZE      = size(4);
+            APR_MAX_SECS_TO_LINGER  = size(5);
+            APR_MMAP_THRESHOLD      = size(6);
+            APR_MMAP_LIMIT          = size(7);
+
+            APR_HAVE_IPV6           = has(0);
+            APR_HAS_SHARED_MEMORY   = has(1);
+            APR_HAS_THREADS         = has(2);
+            APR_HAS_SENDFILE        = has(3);
+            APR_HAS_MMAP            = has(4);
+            APR_HAS_FORK            = has(5);
+            APR_HAS_RANDOM          = has(6);
+            APR_HAS_OTHER_CHILD     = has(7);
+            APR_HAS_DSO             = has(8);
+            APR_HAS_SO_ACCEPTFILTER = has(9);
+            APR_HAS_UNICODE_FS      = has(10);
+            APR_HAS_PROC_INVOKED    = has(11);
+            APR_HAS_USER            = has(12);
+            APR_HAS_LARGE_FILES     = has(13);
+            APR_HAS_XTHREAD_FILES   = has(14);
+            APR_HAS_OS_UUID         = has(15);
+            APR_IS_BIGENDIAN        = has(16);
+            APR_FILES_AS_SOCKETS    = has(17);
+            APR_CHARSET_EBCDIC      = has(18);
+            APR_TCP_NODELAY_INHERITED = has(19);
+            APR_O_NONBLOCK_INHERITED  = has(20);
+            if (APR_MAJOR_VERSION < 1) {
+                throw new UnsatisfiedLinkError("Unsupported APR Version (" +
+                                               aprVersionString() + ")");
+            }
+            if (!APR_HAS_THREADS) {
+                throw new UnsatisfiedLinkError("Missing APR_HAS_THREADS");
+            }
+        }
+        return initialize();
+    }
+}
diff --git a/connectors/jni/java/org/apache/tomcat/jni/Local.java b/connectors/jni/java/org/apache/tomcat/jni/Local.java
new file mode 100644
index 0000000..fb5a7a6
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/Local.java
@@ -0,0 +1,76 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+/** Local socket
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public class Local {
+
+    /**
+     * Create a socket.
+     * @param path The address of the new socket.
+     * @param cont The parent pool to use
+     * @return The new socket that has been set up.
+     */
+    public static native long create(String path, long cont)
+        throws Exception;
+
+    /**
+     * Bind the socket to its associated port
+     * @param sock The socket to bind
+     * @param sa The socket address to bind to
+     * This may be where we will find out if there is any other process
+     *      using the selected port.
+     */
+    public static native int bind(long sock, long sa);
+
+    /**
+     * Listen to a bound socket for connections.
+     * @param sock The socket to listen on
+     * @param backlog The number of outstanding connections allowed in the sockets
+     *                listen queue.  If this value is less than zero, for NT pipes
+     *                the number of instances is unlimite.
+     *
+     */
+    public static native int listen(long sock, int backlog);
+
+    /**
+     * Accept a new connection request
+     * @param sock The socket we are listening on.
+     * @param pool The pool for the new socket.
+     * @return  A copy of the socket that is connected to the socket that
+     *          made the connection request.  This is the socket which should
+     *          be used for all future communication.
+     */
+    public static native long accept(long sock)
+        throws Exception;
+
+    /**
+     * Issue a connection request to a socket either on the same machine
+     * or a different one.
+     * @param sock The socket we wish to use for our side of the connection
+     * @param sa The address of the machine we wish to connect to.
+     *           Unused for NT Pipes.
+     */
+    public static native int connect(long sock, long sa);
+
+}
diff --git a/connectors/jni/java/org/apache/tomcat/jni/Lock.java b/connectors/jni/java/org/apache/tomcat/jni/Lock.java
new file mode 100644
index 0000000..df6e549
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/Lock.java
@@ -0,0 +1,123 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+/** Lock
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public class Lock {
+
+    /**
+     * Enumerated potential types for APR process locking methods
+     * <br /><b>Warning :</b> Check APR_HAS_foo_SERIALIZE defines to see if the platform supports
+     *          APR_LOCK_foo.  Only APR_LOCK_DEFAULT is portable.
+     */
+
+    public static final int APR_LOCK_FCNTL        = 0; /** fcntl() */
+    public static final int APR_LOCK_FLOCK        = 1; /** flock() */
+    public static final int APR_LOCK_SYSVSEM      = 2; /** System V Semaphores */
+    public static final int APR_LOCK_PROC_PTHREAD = 3; /** POSIX pthread process-based locking */
+    public static final int APR_LOCK_POSIXSEM     = 4; /** POSIX semaphore process-based locking */
+    public static final int APR_LOCK_DEFAULT      = 5; /** Use the default process lock */
+
+    /**
+     * Create and initialize a mutex that can be used to synchronize processes.
+     * <br /><b>Warning :</b> Check APR_HAS_foo_SERIALIZE defines to see if the platform supports
+     *          APR_LOCK_foo.  Only APR_LOCK_DEFAULT is portable.
+     * @param fname A file name to use if the lock mechanism requires one.  This
+     *        argument should always be provided.  The lock code itself will
+     *        determine if it should be used.
+     * @param mech The mechanism to use for the interprocess lock, if any; one of
+     * <PRE>
+     *            APR_LOCK_FCNTL
+     *            APR_LOCK_FLOCK
+     *            APR_LOCK_SYSVSEM
+     *            APR_LOCK_POSIXSEM
+     *            APR_LOCK_PROC_PTHREAD
+     *            APR_LOCK_DEFAULT     pick the default mechanism for the platform
+     * </PRE>
+     * @param pool the pool from which to allocate the mutex.
+     * @return Newly created mutex.
+     */
+    public static native long create(String fname, int mech, long pool)
+        throws Error;
+
+    /**
+     * Re-open a mutex in a child process.
+     * This function must be called to maintain portability, even
+     * if the underlying lock mechanism does not require it.
+     * @param fname A file name to use if the mutex mechanism requires one.  This
+     *              argument should always be provided.  The mutex code itself will
+     *              determine if it should be used.  This filename should be the
+     *              same one that was passed to apr_proc_mutex_create().
+     * @param pool The pool to operate on.
+     * @return Newly opened mutex.
+     */
+    public static native long childInit(String fname, long pool)
+        throws Error;
+
+    /**
+     * Acquire the lock for the given mutex. If the mutex is already locked,
+     * the current thread will be put to sleep until the lock becomes available.
+     * @param mutex the mutex on which to acquire the lock.
+     */
+    public static native int lock(long mutex);
+
+    /**
+     * Attempt to acquire the lock for the given mutex. If the mutex has already
+     * been acquired, the call returns immediately with APR_EBUSY. Note: it
+     * is important that the APR_STATUS_IS_EBUSY(s) macro be used to determine
+     * if the return value was APR_EBUSY, for portability reasons.
+     * @param mutex the mutex on which to attempt the lock acquiring.
+     */
+    public static native int trylock(long mutex);
+
+    /**
+     * Release the lock for the given mutex.
+     * @param mutex the mutex from which to release the lock.
+     */
+    public static native int unlock(long mutex);
+
+    /**
+     * Destroy the mutex and free the memory associated with the lock.
+     * @param mutex the mutex to destroy.
+     */
+    public static native int destroy(long mutex);
+
+    /**
+     * Return the name of the lockfile for the mutex, or NULL
+     * if the mutex doesn't use a lock file
+     */
+    public static native String lockfile(long mutex);
+
+    /**
+     * Display the name of the mutex, as it relates to the actual method used.
+     * This matches the valid options for Apache's AcceptMutex directive
+     * @param mutex the name of the mutex
+     */
+    public static native String name(long mutex);
+
+    /**
+     * Display the name of the default mutex: APR_LOCK_DEFAULT
+     */
+    public static native String defname();
+
+}
diff --git a/connectors/jni/java/org/apache/tomcat/jni/Mmap.java b/connectors/jni/java/org/apache/tomcat/jni/Mmap.java
new file mode 100644
index 0000000..f42fd37
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/Mmap.java
@@ -0,0 +1,73 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+/** Mmap
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public class Mmap {
+    /** MMap opened for reading */
+    public static final int APR_MMAP_READ  = 1;
+    /** MMap opened for writing */
+    public static final int APR_MMAP_WRITE = 2;
+
+
+    /**
+     * Create a new mmap'ed file out of an existing APR file.
+     * @param file The file turn into an mmap.
+     * @param offset The offset into the file to start the data pointer at.
+     * @param size The size of the file
+     * @param flag bit-wise or of:
+     * <PRE>
+     * APR_MMAP_READ       MMap opened for reading
+     * APR_MMAP_WRITE      MMap opened for writing
+     * </PRE>
+     * @param pool The pool to use when creating the mmap.
+     * @return The newly created mmap'ed file.
+     */
+    public static native long create(long file, long offset, long size, int flag, long pool)
+        throws Error;
+
+    /**
+     * Duplicate the specified MMAP.
+     * @param mmap The mmap to duplicate.
+     * @param pool The pool to use for new_mmap.
+     * @return Duplicated mmap'ed file.
+     */
+    public static native long dup(long mmap, long pool)
+        throws Error;
+
+    /**
+     * Remove a mmap'ed.
+     * @param mm The mmap'ed file.
+     */
+    public static native int delete(long mm);
+
+    /**
+     * Move the pointer into the mmap'ed file to the specified offset.
+     * @param mm The mmap'ed file.
+     * @param offset The offset to move to.
+     * @return The pointer to the offset specified.
+     */
+    public static native long offset(long mm, long offset)
+        throws Error;
+
+}
diff --git a/connectors/jni/java/org/apache/tomcat/jni/Multicast.java b/connectors/jni/java/org/apache/tomcat/jni/Multicast.java
new file mode 100644
index 0000000..4e2e680
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/Multicast.java
@@ -0,0 +1,78 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+/** Multicast
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public class Multicast {
+
+    /**
+     * Join a Multicast Group
+     * @param sock The socket to join a multicast group
+     * @param join The address of the multicast group to join
+     * @param iface Address of the interface to use.  If NULL is passed, the
+     *              default multicast interface will be used. (OS Dependent)
+     * @param source Source Address to accept transmissions from (non-NULL
+     *               implies Source-Specific Multicast)
+     */
+    public static native int join(long sock, long join,
+                                  long iface, long source);
+
+    /**
+     * Leave a Multicast Group.  All arguments must be the same as
+     * apr_mcast_join.
+     * @param sock The socket to leave a multicast group
+     * @param addr The address of the multicast group to leave
+     * @param iface Address of the interface to use.  If NULL is passed, the
+     *              default multicast interface will be used. (OS Dependent)
+     * @param source Source Address to accept transmissions from (non-NULL
+     *               implies Source-Specific Multicast)
+     */
+    public static native int leave(long sock, long addr,
+                                   long iface, long source);
+
+    /**
+     * Set the Multicast Time to Live (ttl) for a multicast transmission.
+     * @param sock The socket to set the multicast ttl
+     * @param ttl Time to live to Assign. 0-255, default=1
+     * <br /><b>Remark :</b> If the TTL is 0, packets will only be seen
+     * by sockets on the local machine,
+     * and only when multicast loopback is enabled.
+     */
+    public static native int hops(long sock, int ttl);
+
+    /**
+     * Toggle IP Multicast Loopback
+     * @param sock The socket to set multicast loopback
+     * @param opt false=disable, true=enable
+     */
+    public static native int loopback(long sock, boolean opt);
+
+
+    /**
+     * Set the Interface to be used for outgoing Multicast Transmissions.
+     * @param sock The socket to set the multicast interface on
+     * @param iface Address of the interface to use for Multicast
+     */
+    public static native int ointerface(long sock, long iface);
+
+}
diff --git a/connectors/jni/java/org/apache/tomcat/jni/OS.java b/connectors/jni/java/org/apache/tomcat/jni/OS.java
new file mode 100644
index 0000000..0e7c8a6
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/OS.java
@@ -0,0 +1,128 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+/** OS
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public class OS {
+
+    /* OS Enums */
+    private static final int UNIX      = 1;
+    private static final int NETWARE   = 2;
+    private static final int WIN32     = 3;
+    private static final int WIN64     = 4;
+    private static final int LINUX     = 5;
+    private static final int SOLARIS   = 6;
+    private static final int BSD       = 7;
+
+    public static final int LOG_EMERG  = 1;
+    public static final int LOG_ERROR  = 2;
+    public static final int LOG_NOTICE = 3;
+    public static final int LOG_WARN   = 4;
+    public static final int LOG_INFO   = 5;
+    public static final int LOG_DEBUG  = 6;
+
+    /**
+     * Check for OS type.
+     * @param type OS type to test.
+     */
+    private static native boolean is(int type);
+
+    public static final boolean IS_UNIX    = is(UNIX);
+    public static final boolean IS_NETWARE = is(NETWARE);
+    public static final boolean IS_WIN32   = is(WIN32);
+    public static final boolean IS_WIN64   = is(WIN64);
+    public static final boolean IS_LINUX   = is(LINUX);
+    public static final boolean IS_SOLARIS = is(SOLARIS);
+    public static final boolean IS_BSD     = is(BSD);
+
+    /**
+     * Get the name of the system default characer set.
+     * @param pool the pool to allocate the name from, if needed
+     */
+    public static native String defaultEncoding(long pool);
+
+    /**
+     * Get the name of the current locale character set.
+     * Defers to apr_os_default_encoding if the current locale's
+     * data can't be retreved on this system.
+     * @param pool the pool to allocate the name from, if needed
+     */
+    public static native String localeEncoding(long pool);
+
+    /**
+     * Generate random bytes.
+     * @param buf Buffer to fill with random bytes
+     * @param len Length of buffer in bytes
+     */
+    public static native int random(byte [] buf, int len);
+
+    /**
+     * Gather system info.
+     * <PRE>
+     * On exit the inf array will be filled with:
+     * inf[0]  - Total usable main memory size
+     * inf[1]  - Available memory size
+     * inf[2]  - Total page file/swap space size
+     * inf[3]  - Page file/swap space still available
+     * inf[4]  - Amount of shared memory
+     * inf[5]  - Memory used by buffers
+     * inf[6]  - Memory Load
+     *
+     * inf[7]  - Idle Time in microseconds
+     * inf[8]  - Kernel Time in microseconds
+     * inf[9]  - User Time in microseconds
+     *
+     * inf[10] - Process creation time (apr_time_t)
+     * inf[11] - Process Kernel Time in microseconds
+     * inf[12] - Process User Time in microseconds
+     *
+     * inf[13] - Current working set size.
+     * inf[14] - Peak working set size.
+     * inf[15] - Number of page faults.
+     * </PRE>
+     * @param inf array that will be filled with system information.
+     *            Array length must be at least 16.
+     */
+    public static native int info(long [] inf);
+
+    /**
+     * Expand environment variables.
+     * @param str String to expand
+     * @return Expanded string with replaced environment variables.
+     */
+    public static native String expand(String str);
+
+    /**
+     * Initialize system logging.
+     * @param domain String that will be prepended to every message
+     */
+    public static native void sysloginit(String domain);
+
+    /**
+     * Log message.
+     * @param level Log message severity. See LOG_XXX enums.
+     * @param message Message to log
+     */
+    public static native void syslog(int level, String message);
+
+}
diff --git a/connectors/jni/java/org/apache/tomcat/jni/PasswordCallback.java b/connectors/jni/java/org/apache/tomcat/jni/PasswordCallback.java
new file mode 100644
index 0000000..d085ae3
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/PasswordCallback.java
@@ -0,0 +1,34 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+/** PasswordCallback Interface
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public interface PasswordCallback {
+
+    /**
+     * Called when the password is required
+     * @param prompt Password prompt
+     * @return Valid password or null
+     */
+    public String callback(String prompt);
+}
diff --git a/connectors/jni/java/org/apache/tomcat/jni/Poll.java b/connectors/jni/java/org/apache/tomcat/jni/Poll.java
new file mode 100644
index 0000000..8eb0ee8
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/Poll.java
@@ -0,0 +1,158 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+/** Poll
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public class Poll {
+
+    /**
+     * Poll options
+     */
+    public static final int APR_POLLIN   = 0x001; /** Can read without blocking */
+    public static final int APR_POLLPRI  = 0x002; /** Priority data available */
+    public static final int APR_POLLOUT  = 0x004; /** Can write without blocking */
+    public static final int APR_POLLERR  = 0x010; /** Pending error */
+    public static final int APR_POLLHUP  = 0x020; /** Hangup occurred */
+    public static final int APR_POLLNVAL = 0x040; /** Descriptior invalid */
+
+    /**
+     * Pollset Flags
+     */
+    /** Adding or Removing a Descriptor is thread safe */
+    public static final int APR_POLLSET_THREADSAFE = 0x001;
+
+
+    /** Used in apr_pollfd_t to determine what the apr_descriptor is
+     * apr_datatype_e enum
+     */
+    public static final int APR_NO_DESC       = 0; /** nothing here */
+    public static final int APR_POLL_SOCKET   = 1; /** descriptor refers to a socket */
+    public static final int APR_POLL_FILE     = 2; /** descriptor refers to a file */
+    public static final int APR_POLL_LASTDESC = 3; /** descriptor is the last one in the list */
+
+    /**
+     * Setup a pollset object.
+     * If flags equals APR_POLLSET_THREADSAFE, then a pollset is
+     * created on which it is safe to make concurrent calls to
+     * apr_pollset_add(), apr_pollset_remove() and apr_pollset_poll() from
+     * separate threads.  This feature is only supported on some
+     * platforms; the apr_pollset_create() call will fail with
+     * APR_ENOTIMPL on platforms where it is not supported.
+     * @param size The maximum number of descriptors that this pollset can hold
+     * @param p The pool from which to allocate the pollset
+     * @param flags Optional flags to modify the operation of the pollset.
+     * @param ttl Maximum time to live for a particular socket.
+     * @return  The pointer in which to return the newly created object
+     */
+    public static native long create(int size, long p, int flags, long ttl)
+        throws Error;
+    /**
+     * Destroy a pollset object
+     * @param pollset The pollset to destroy
+     */
+    public static native int destroy(long pollset);
+
+    /**
+     * Add a socket or to a pollset
+     * If you set client_data in the descriptor, that value
+     * will be returned in the client_data field whenever this
+     * descriptor is signalled in apr_pollset_poll().
+     * @param pollset The pollset to which to add the descriptor
+     * @param sock The sockets to add
+     * @param data Client data to add
+     * @param reqevents requested events
+     */
+    public static native int add(long pollset, long sock,
+                                 int reqevents);
+
+    /**
+     * Remove a descriptor from a pollset
+     * @param pollset The pollset from which to remove the descriptor
+     * @param sock The socket to remove
+     */
+    public static native int remove(long pollset, long sock);
+
+    /**
+     * Block for activity on the descriptor(s) in a pollset
+     * @param pollset The pollset to use
+     * @param timeout Timeout in microseconds
+     * @param descriptors Array of signalled descriptors (output parameter)
+     *        The desctiptor array must be two times the size of pollset.
+     *        and are populated as follows:
+     * <PRE>
+     * descriptors[n + 0] -> returned events
+     * descriptors[n + 1] -> socket
+     * </PRE>
+     * @param remove Remove signaled descriptors from pollset
+     * @return Number of signalled descriptors (output parameter)
+     *         or negative APR error code.
+     */
+    public static native int poll(long pollset, long timeout,
+                                  long [] descriptors, boolean remove);
+
+    /**
+     * Maintain on the descriptor(s) in a pollset
+     * @param pollset The pollset to use
+     * @param descriptors Array of signalled descriptors (output parameter)
+     *        The desctiptor array must be the size of pollset.
+     *        and are populated as follows:
+     * <PRE>
+     * descriptors[n] -> socket
+     * </PRE>
+     * @param remove Remove signaled descriptors from pollset
+     * @return Number of signalled descriptors (output parameter)
+     *         or negative APR error code.
+     */
+    public static native int maintain(long pollset, long [] descriptors,
+                                      boolean remove);
+
+    /**
+     * Set the socket time to live.
+     * @param pollset The pollset to use
+     * @param ttl Timeout in microseconds
+     */
+    public static native void setTtl(long pollset, long ttl);
+
+    /**
+     * Get the socket time to live.
+     * @param pollset The pollset to use
+     * @return Timeout in microseconds
+     */
+    public static native long getTtl(long pollset);
+
+    /**
+     * Return all descriptor(s) in a pollset
+     * @param pollset The pollset to use
+     * @param descriptors Array of descriptors (output parameter)
+     *        The desctiptor array must be two times the size of pollset.
+     *        and are populated as follows:
+     * <PRE>
+     * descriptors[n + 0] -> returned events
+     * descriptors[n + 1] -> socket
+     * </PRE>
+     * @return Number of descriptors (output parameter) in the Poll
+     *         or negative APR error code.
+     */
+    public static native int pollset(long pollset, long [] descriptors);
+
+}
diff --git a/connectors/jni/java/org/apache/tomcat/jni/Pool.java b/connectors/jni/java/org/apache/tomcat/jni/Pool.java
new file mode 100644
index 0000000..408c9cb
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/Pool.java
@@ -0,0 +1,164 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+import java.nio.ByteBuffer;
+
+/** Pool
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public class Pool {
+
+    /**
+     * Create a new pool.
+     * @param parent The parent pool.  If this is 0, the new pool is a root
+     * pool.  If it is non-zero, the new pool will inherit all
+     * of its parent pool's attributes, except the apr_pool_t will
+     * be a sub-pool.
+     * @return The pool we have just created.
+    */
+    public static native long create(long parent);
+
+    /**
+     * Clear all memory in the pool and run all the cleanups. This also destroys all
+     * subpools.
+     * @param pool The pool to clear
+     * This does not actually free the memory, it just allows the pool
+     *         to re-use this memory for the next allocation.
+     */
+    public static native void clear(long pool);
+
+    /**
+     * Destroy the pool. This takes similar action as apr_pool_clear() and then
+     * frees all the memory.
+     * This will actually free the memory
+     * @param pool The pool to destroy
+     */
+    public static native void destroy(long pool);
+
+    /**
+     * Get the parent pool of the specified pool.
+     * @param pool The pool for retrieving the parent pool.
+     * @return The parent of the given pool.
+     */
+    public static native long parentGet(long pool);
+
+    /**
+     * Determine if pool a is an ancestor of pool b
+     * @param a The pool to search
+     * @param b The pool to search for
+     * @return True if a is an ancestor of b, NULL is considered an ancestor
+     * of all pools.
+     */
+    public static native boolean isAncestor(long a, long b);
+
+
+    /*
+     * Cleanup
+     *
+     * Cleanups are performed in the reverse order they were registered.  That is:
+     * Last In, First Out.  A cleanup function can safely allocate memory from
+     * the pool that is being cleaned up. It can also safely register additional
+     * cleanups which will be run LIFO, directly after the current cleanup
+     * terminates.  Cleanups have to take caution in calling functions that
+     * create subpools. Subpools, created during cleanup will NOT automatically
+     * be cleaned up.  In other words, cleanups are to clean up after themselves.
+     */
+
+    /**
+     * Register a function to be called when a pool is cleared or destroyed
+     * @param pool The pool register the cleanup with
+     * @param o The object to call when the pool is cleared
+     *                      or destroyed
+     * @return The cleanup handler.
+     */
+    public static native long cleanupRegister(long pool, Object o);
+
+    /**
+     * Remove a previously registered cleanup function
+     * @param pool The pool remove the cleanup from
+     * @param data The cleanup handler to remove from cleanup
+     */
+    public static native void cleanupKill(long pool, long data);
+
+    /**
+     * Register a process to be killed when a pool dies.
+     * @param a The pool to use to define the processes lifetime
+     * @param proc The process to register
+     * @param how How to kill the process, one of:
+     * <PRE>
+     * APR_KILL_NEVER         -- process is never sent any signals
+     * APR_KILL_ALWAYS        -- process is sent SIGKILL on apr_pool_t cleanup
+     * APR_KILL_AFTER_TIMEOUT -- SIGTERM, wait 3 seconds, SIGKILL
+     * APR_JUST_WAIT          -- wait forever for the process to complete
+     * APR_KILL_ONLY_ONCE     -- send SIGTERM and then wait
+     * </PRE>
+     */
+    public static native void noteSubprocess(long a, long proc, int how);
+
+    /**
+     * Allocate a block of memory from a pool
+     * @param p The pool to allocate from
+     * @param size The amount of memory to allocate
+     * @return The ByteBuffer with allocated memory
+     */
+    public static native ByteBuffer alloc(long p, int size);
+
+    /**
+     * Allocate a block of memory from a pool and set all of the memory to 0
+     * @param p The pool to allocate from
+     * @param size The amount of memory to allocate
+     * @return The ByteBuffer with allocated memory
+     */
+    public static native ByteBuffer calloc(long p, int size);
+
+    /*
+     * User data management
+     */
+
+    /**
+     * Set the data associated with the current pool
+     * @param data The user data associated with the pool.
+     * @param key The key to use for association
+     * @param pool The current pool
+     * <br /><b>Warning :</b>
+     * The data to be attached to the pool should have a life span
+     * at least as long as the pool it is being attached to.
+     * Object attached to the pool will be globaly referenced
+     * untill the pool is cleared or dataSet is called with the null data.
+     * @return APR Status code.
+     */
+     public static native int dataSet(long pool, String key, Object data);
+
+    /**
+     * Return the data associated with the current pool.
+     * @param key The key for the data to retrieve
+     * @param pool The current pool.
+     */
+     public static native Object dataGet(long pool, String key);
+
+    /**
+     * Run all of the child_cleanups, so that any unnecessary files are
+     * closed because we are about to exec a new program
+     */
+    public static native void cleanupForExec();
+
+}
diff --git a/connectors/jni/java/org/apache/tomcat/jni/PoolCallback.java b/connectors/jni/java/org/apache/tomcat/jni/PoolCallback.java
new file mode 100644
index 0000000..fa93ec4
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/PoolCallback.java
@@ -0,0 +1,33 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+/** PoolCallback Interface
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public interface PoolCallback {
+
+    /**
+     * Called when the pool is destroyed or cleared
+     * @return Function must return APR_SUCCESS
+     */
+    public int callback();
+}
diff --git a/connectors/jni/java/org/apache/tomcat/jni/Proc.java b/connectors/jni/java/org/apache/tomcat/jni/Proc.java
new file mode 100644
index 0000000..7f037c7
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/Proc.java
@@ -0,0 +1,210 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+/** Proc
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public class Proc {
+
+    /*
+     * apr_cmdtype_e enum
+     */
+    public static final int APR_SHELLCM      = 0; /** use the shell to invoke the program */
+    public static final int APR_PROGRAM      = 1; /** invoke the program directly, no copied env */
+    public static final int APR_PROGRAM_ENV  = 2; /** invoke the program, replicating our environment */
+    public static final int APR_PROGRAM_PATH = 3; /** find program on PATH, use our environment */
+    public static final int APR_SHELLCMD_ENV = 4; /** use the shell to invoke the program,
+                                                   *   replicating our environment
+                                                   */
+
+    /*
+     * apr_wait_how_e enum
+     */
+    public static final int APR_WAIT   = 0; /** wait for the specified process to finish */
+    public static final int APR_NOWAIT = 1; /** do not wait -- just see if it has finished */
+
+    /*
+     * apr_exit_why_e enum
+     */
+    public static final int APR_PROC_EXIT        = 1; /** process exited normally */
+    public static final int APR_PROC_SIGNAL      = 2; /** process exited due to a signal */
+    public static final int APR_PROC_SIGNAL_CORE = 4; /** process exited and dumped a core file */
+
+    public static final int APR_NO_PIPE       = 0;
+    public static final int APR_FULL_BLOCK    = 1;
+    public static final int APR_FULL_NONBLOCK = 2;
+    public static final int APR_PARENT_BLOCK  = 3;
+    public static final int APR_CHILD_BLOCK   = 4;
+
+    public static final int APR_LIMIT_CPU     = 0;
+    public static final int APR_LIMIT_MEM     = 1;
+    public static final int APR_LIMIT_NPROC   = 2;
+    public static final int APR_LIMIT_NOFILE  = 3;
+
+
+    /** child has died, caller must call unregister still */
+    public static final int APR_OC_REASON_DEATH      = 0;
+    /** write_fd is unwritable */
+    public static final int APR_OC_REASON_UNWRITABLE = 1;
+    /** a restart is occuring, perform any necessary cleanup (including
+     * sending a special signal to child)
+     */
+    public static final int APR_OC_REASON_RESTART    = 2;
+    /** unregister has been called, do whatever is necessary (including
+     * kill the child)
+     */
+    public static final int APR_OC_REASON_UNREGISTER = 3;
+    /** somehow the child exited without us knowing ... buggy os? */
+    public static final int APR_OC_REASON_LOST       = 4;
+    /** a health check is occuring, for most maintainence functions
+     * this is a no-op.
+     */
+    public static final int APR_OC_REASON_RUNNING    = 5;
+
+    /* apr_kill_conditions_e enumeration */
+    /** process is never sent any signals */
+    public static final int APR_KILL_NEVER         = 0;
+    /** process is sent SIGKILL on apr_pool_t cleanup */
+    public static final int APR_KILL_ALWAYS        = 1;
+    /** SIGTERM, wait 3 seconds, SIGKILL */
+    public static final int APR_KILL_AFTER_TIMEOUT = 2;
+    /** wait forever for the process to complete */
+    public static final int APR_JUST_WAIT          = 3;
+    /** send SIGTERM and then wait */
+    public static final int APR_KILL_ONLY_ONCE     = 4;
+
+    public static final int APR_PROC_DETACH_FOREGROUND = 0; /** Do not detach */
+    public static final int APR_PROC_DETACH_DAEMONIZE  = 1; /** Detach */
+
+    /* Maximum number of arguments for create process call */
+    public static final int MAX_ARGS_SIZE          = 1024;
+    /* Maximum number of environment variables for create process call */
+    public static final int MAX_ENV_SIZE           = 1024;
+
+    /**
+     * Allocate apr_proc_t stucture from pool
+     * This is not an apr function.
+     * @param cont The pool to use.
+     */
+    public static native long alloc(long cont);
+
+    /**
+     * This is currently the only non-portable call in APR.  This executes
+     * a standard unix fork.
+     * @param proc The resulting process handle.
+     * @param cont The pool to use.
+     * @return APR_INCHILD for the child, and APR_INPARENT for the parent
+     * or an error.
+     */
+    public static native int fork(long [] proc, long cont);
+
+    /**
+     * Create a new process and execute a new program within that process.
+     * This function returns without waiting for the new process to terminate;
+     * use apr_proc_wait for that.
+     * @param progname The program to run
+     * @param args The arguments to pass to the new program.  The first
+     *             one should be the program name.
+     * @param env The new environment table for the new process.  This
+     *            should be a list of NULL-terminated strings. This argument
+     *            is ignored for APR_PROGRAM_ENV, APR_PROGRAM_PATH, and
+     *            APR_SHELLCMD_ENV types of commands.
+     * @param attr The procattr we should use to determine how to create the new
+     * process
+     * @param pool The pool to use.
+     * @return The resulting process handle.
+     */
+    public static native int create(long proc, String progname,
+                                    String [] args, String [] env,
+                                    long attr, long pool);
+
+    /**
+     * Wait for a child process to die
+     * @param proc The process handle that corresponds to the desired child process
+     * @param exit exit[0] The returned exit status of the child, if a child process
+     *                dies, or the signal that caused the child to die.
+     *                On platforms that don't support obtaining this information,
+     *                the status parameter will be returned as APR_ENOTIMPL.
+     * exit[1] Why the child died, the bitwise or of:
+     * <PRE>
+     * APR_PROC_EXIT         -- process terminated normally
+     * APR_PROC_SIGNAL       -- process was killed by a signal
+     * APR_PROC_SIGNAL_CORE  -- process was killed by a signal, and
+     *                          generated a core dump.
+     * </PRE>
+     * @param waithow How should we wait.  One of:
+     * <PRE>
+     * APR_WAIT   -- block until the child process dies.
+     * APR_NOWAIT -- return immediately regardless of if the
+     *               child is dead or not.
+     * </PRE>
+     * @return The childs status is in the return code to this process.  It is one of:
+     * <PRE>
+     * APR_CHILD_DONE     -- child is no longer running.
+     * APR_CHILD_NOTDONE  -- child is still running.
+     * </PRE>
+     */
+    public static native int wait(long proc, int [] exit, int waithow);
+
+    /**
+     * Wait for any current child process to die and return information
+     * about that child.
+     * @param proc Pointer to NULL on entry, will be filled out with child's
+     *             information
+     * @param exit exit[0] The returned exit status of the child, if a child process
+     *                dies, or the signal that caused the child to die.
+     *                On platforms that don't support obtaining this information,
+     *                the status parameter will be returned as APR_ENOTIMPL.
+     * exit[1] Why the child died, the bitwise or of:
+     * <PRE>
+     * APR_PROC_EXIT         -- process terminated normally
+     * APR_PROC_SIGNAL       -- process was killed by a signal
+     * APR_PROC_SIGNAL_CORE  -- process was killed by a signal, and
+     *                          generated a core dump.
+     * </PRE>
+     * @param waithow How should we wait.  One of:
+     * <PRE>
+     * APR_WAIT   -- block until the child process dies.
+     * APR_NOWAIT -- return immediately regardless of if the
+     *               child is dead or not.
+     * </PRE>
+     * @param pool Pool to allocate child information out of.
+     */
+    public static native int waitAllProcs(long proc, int [] exit,
+                                          int waithow, long pool);
+
+     /**
+     * Detach the process from the controlling terminal.
+     * @param daemonize set to non-zero if the process should daemonize
+     *                  and become a background process, else it will
+     *                  stay in the foreground.
+     */
+    public static native int detach(int daemonize);
+
+    /**
+     * Terminate a process.
+     * @param proc The process to terminate.
+     * @param sig How to kill the process.
+     */
+    public static native int kill(long proc, int sig);
+
+}
diff --git a/connectors/jni/java/org/apache/tomcat/jni/ProcErrorCallback.java b/connectors/jni/java/org/apache/tomcat/jni/ProcErrorCallback.java
new file mode 100644
index 0000000..f359823
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/ProcErrorCallback.java
@@ -0,0 +1,38 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+/** ProcErrorCallback Interface
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public interface ProcErrorCallback {
+
+    /**
+     * Called in the child process if APR encounters an error
+     * in the child prior to running the specified program.
+     * @param pool Pool associated with the apr_proc_t.  If your child
+     *             error function needs user data, associate it with this
+     *             pool.
+     * @param err APR error code describing the error
+     * @param description Text description of type of processing which failed
+     */
+    public void callback(long pool, int err, String description);
+}
diff --git a/connectors/jni/java/org/apache/tomcat/jni/Procattr.java b/connectors/jni/java/org/apache/tomcat/jni/Procattr.java
new file mode 100644
index 0000000..f66f642
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/Procattr.java
@@ -0,0 +1,172 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+/** Procattr
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public class Procattr {
+
+    /**
+     * Create and initialize a new procattr variable
+     * @param cont The pool to use
+     * @return The newly created procattr.
+     */
+    public static native long create(long cont)
+        throws Error;
+
+    /**
+     * Determine if any of stdin, stdout, or stderr should be linked to pipes
+     * when starting a child process.
+     * @param attr The procattr we care about.
+     * @param in Should stdin be a pipe back to the parent?
+     * @param out Should stdout be a pipe back to the parent?
+     * @param err Should stderr be a pipe back to the parent?
+     */
+    public static native int ioSet(long attr, int in, int out, int err);
+    /**
+     * Set the child_in and/or parent_in values to existing apr_file_t values.
+     * <br />
+     * This is NOT a required initializer function. This is
+     * useful if you have already opened a pipe (or multiple files)
+     * that you wish to use, perhaps persistently across multiple
+     * process invocations - such as a log file. You can save some
+     * extra function calls by not creating your own pipe since this
+     * creates one in the process space for you.
+     * @param attr The procattr we care about.
+     * @param in apr_file_t value to use as child_in. Must be a valid file.
+     * @param parent apr_file_t value to use as parent_in. Must be a valid file.
+     */
+    public static native int childInSet(long attr, long in, long parent);
+
+    /**
+     * Set the child_out and parent_out values to existing apr_file_t values.
+     * <br />
+     * This is NOT a required initializer function. This is
+     * useful if you have already opened a pipe (or multiple files)
+     * that you wish to use, perhaps persistently across multiple
+     * process invocations - such as a log file.
+     * @param attr The procattr we care about.
+     * @param out apr_file_t value to use as child_out. Must be a valid file.
+     * @param parent apr_file_t value to use as parent_out. Must be a valid file.
+     */
+    public static native int childOutSet(long attr, long out, long parent);
+
+    /**
+     * Set the child_err and parent_err values to existing apr_file_t values.
+     * <br />
+     * This is NOT a required initializer function. This is
+     * useful if you have already opened a pipe (or multiple files)
+     * that you wish to use, perhaps persistently across multiple
+     * process invocations - such as a log file.
+     * @param attr The procattr we care about.
+     * @param err apr_file_t value to use as child_err. Must be a valid file.
+     * @param parent apr_file_t value to use as parent_err. Must be a valid file.
+     */
+    public static native int childErrSet(long attr, long err, long parent);
+
+    /**
+     * Set which directory the child process should start executing in.
+     * @param attr The procattr we care about.
+     * @param dir Which dir to start in.  By default, this is the same dir as
+     *            the parent currently resides in, when the createprocess call
+     *            is made.
+     */
+    public static native int dirSet(long attr, String dir);
+
+    /**
+     * Set what type of command the child process will call.
+     * @param attr The procattr we care about.
+     * @param cmd The type of command.  One of:
+     * <PRE>
+     * APR_SHELLCMD     --  Anything that the shell can handle
+     * APR_PROGRAM      --  Executable program   (default)
+     * APR_PROGRAM_ENV  --  Executable program, copy environment
+     * APR_PROGRAM_PATH --  Executable program on PATH, copy env
+     * </PRE>
+     */
+    public static native int cmdtypeSet(long attr, int cmd);
+
+    /**
+     * Determine if the child should start in detached state.
+     * @param attr The procattr we care about.
+     * @param detach Should the child start in detached state?  Default is no.
+     */
+    public static native int detachSet(long attr, int detach);
+
+    /**
+     * Specify that apr_proc_create() should do whatever it can to report
+     * failures to the caller of apr_proc_create(), rather than find out in
+     * the child.
+     * @param attr The procattr describing the child process to be created.
+     * @param chk Flag to indicate whether or not extra work should be done
+     *            to try to report failures to the caller.
+     * <br />
+     * This flag only affects apr_proc_create() on platforms where
+     * fork() is used.  This leads to extra overhead in the calling
+     * process, but that may help the application handle such
+     * errors more gracefully.
+     */
+    public static native int errorCheckSet(long attr, int chk);
+
+    /**
+     * Determine if the child should start in its own address space or using the
+     * current one from its parent
+     * @param attr The procattr we care about.
+     * @param addrspace Should the child start in its own address space?  Default
+     * is no on NetWare and yes on other platforms.
+     */
+    public static native int addrspaceSet(long attr, int addrspace);
+
+    /**
+     * Specify an error function to be called in the child process if APR
+     * encounters an error in the child prior to running the specified program.
+     * @param attr The procattr describing the child process to be created.
+     * @param pool The the pool to use.
+     * @param o The Object to call in the child process.
+     * <br />
+     * At the present time, it will only be called from apr_proc_create()
+     * on platforms where fork() is used.  It will never be called on other
+     * platforms, on those platforms apr_proc_create() will return the error
+     * in the parent process rather than invoke the callback in the now-forked
+     * child process.
+     */
+    public static native void errfnSet(long attr, long pool, Object o);
+
+    /**
+     * Set the username used for running process
+     * @param attr The procattr we care about.
+     * @param username The username used
+     * @param password User password if needed. Password is needed on WIN32
+     *                 or any other platform having
+     *                 APR_PROCATTR_USER_SET_REQUIRES_PASSWORD set.
+     */
+    public static native int userSet(long attr, String username, String password);
+
+    /**
+     * Set the group used for running process
+     * @param attr The procattr we care about.
+     * @param groupname The group name  used
+     */
+    public static native int groupSet(long attr, String groupname);
+
+
+}
diff --git a/connectors/jni/java/org/apache/tomcat/jni/Registry.java b/connectors/jni/java/org/apache/tomcat/jni/Registry.java
new file mode 100644
index 0000000..724151f
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/Registry.java
@@ -0,0 +1,235 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+/** Windows Registy support
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public class Registry {
+
+    /* Registry Enums */
+    public static final int HKEY_CLASSES_ROOT       = 1;
+    public static final int HKEY_CURRENT_CONFIG     = 2;
+    public static final int HKEY_CURRENT_USER       = 3;
+    public static final int HKEY_LOCAL_MACHINE      = 4;
+    public static final int HKEY_USERS              = 5;
+
+    public static final int KEY_ALL_ACCESS          = 0x0001;
+    public static final int KEY_CREATE_LINK         = 0x0002;
+    public static final int KEY_CREATE_SUB_KEY      = 0x0004;
+    public static final int KEY_ENUMERATE_SUB_KEYS  = 0x0008;
+    public static final int KEY_EXECUTE             = 0x0010;
+    public static final int KEY_NOTIFY              = 0x0020;
+    public static final int KEY_QUERY_VALUE         = 0x0040;
+    public static final int KEY_READ                = 0x0080;
+    public static final int KEY_SET_VALUE           = 0x0100;
+    public static final int KEY_WOW64_64KEY         = 0x0200;
+    public static final int KEY_WOW64_32KEY         = 0x0400;
+    public static final int KEY_WRITE               = 0x0800;
+
+    public static final int REG_BINARY              = 1;
+    public static final int REG_DWORD               = 2;
+    public static final int REG_EXPAND_SZ           = 3;
+    public static final int REG_MULTI_SZ            = 4;
+    public static final int REG_QWORD               = 5;
+    public static final int REG_SZ                  = 6;
+
+     /**
+     * Create or open a Registry Key.
+     * @param name Registry Subkey to open
+     * @param root Root key, one of HKEY_*
+     * @param som Access mask that specifies the access rights for the key.
+     * @return Opened Registry key
+     */
+    public static native long create(int root, String name, int sam, long pool)
+        throws Error;
+
+     /**
+     * Opens the specified Registry Key.
+     * @param name Registry Subkey to open
+     * @param root Root key, one of HKEY_*
+     * @param som Access mask that specifies the access rights for the key.
+     * @return Opened Registry key
+     */
+    public static native long open(int root, String name, int sam, long pool)
+        throws Error;
+
+    /**
+     * Close the specified Registry key.
+     * @param key The Registry key descriptor to close.
+     */
+    public static native int close(long key);
+
+    /**
+     * Get the Registry key type.
+     * @param key The Registry key descriptor to use.
+     * @param name The name of the value to query
+     * @return Value type or negative error value
+     */
+    public static native int getType(long key, String name);
+
+    /**
+     * Get the Registry value for REG_DWORD
+     * @param key The Registry key descriptor to use.
+     * @param name The name of the value to query
+     * @return Registry key value
+     */
+    public static native int getValueI(long key, String name)
+        throws Error;
+
+    /**
+     * Get the Registry value for REG_QWORD or REG_DWORD
+     * @param key The Registry key descriptor to use.
+     * @param name The name of the value to query
+     * @return Registry key value
+     */
+    public static native long getValueJ(long key, String name)
+        throws Error;
+
+    /**
+     * Get the Registry key length.
+     * @param key The Registry key descriptor to use.
+     * @param name The name of the value to query
+     * @return Value size or negative error value
+     */
+    public static native int getSize(long key, String name);
+
+    /**
+     * Get the Registry value for REG_SZ or REG_EXPAND_SZ
+     * @param key The Registry key descriptor to use.
+     * @param name The name of the value to query
+     * @return Registry key value
+     */
+    public static native String getValueS(long key, String name)
+        throws Error;
+
+    /**
+     * Get the Registry value for REG_MULTI_SZ
+     * @param key The Registry key descriptor to use.
+     * @param name The name of the value to query
+     * @return Registry key value
+     */
+    public static native String[] getValueA(long key, String name)
+        throws Error;
+
+    /**
+     * Get the Registry value for REG_BINARY
+     * @param key The Registry key descriptor to use.
+     * @param name The name of the value to query
+     * @return Registry key value
+     */
+    public static native byte[] getValueB(long key, String name)
+        throws Error;
+
+
+    /**
+     * Set the Registry value for REG_DWORD
+     * @param key The Registry key descriptor to use.
+     * @param name The name of the value to set
+     * @param val The the value to set
+     * @return If the function succeeds, the return value is 0
+     */
+    public static native int setValueI(long key, String name, int val);
+
+    /**
+     * Set the Registry value for REG_QWORD
+     * @param key The Registry key descriptor to use.
+     * @param name The name of the value to set
+     * @param val The the value to set
+     * @return If the function succeeds, the return value is 0
+     */
+    public static native int setValueJ(long key, String name, int val);
+
+    /**
+     * Set the Registry value for REG_SZ
+     * @param key The Registry key descriptor to use.
+     * @param name The name of the value to set
+     * @param val The the value to set
+     * @return If the function succeeds, the return value is 0
+     */
+    public static native int setValueS(long key, String name, String val);
+
+    /**
+     * Set the Registry value for REG_EXPAND_SZ
+     * @param key The Registry key descriptor to use.
+     * @param name The name of the value to set
+     * @param val The the value to set
+     * @return If the function succeeds, the return value is 0
+     */
+    public static native int setValueE(long key, String name, String val);
+
+     /**
+     * Set the Registry value for REG_MULTI_SZ
+     * @param key The Registry key descriptor to use.
+     * @param name The name of the value to set
+     * @param val The the value to set
+     * @return If the function succeeds, the return value is 0
+     */
+    public static native int setValueA(long key, String name, String[] val);
+
+     /**
+     * Set the Registry value for REG_BINARY
+     * @param key The Registry key descriptor to use.
+     * @param name The name of the value to set
+     * @param val The the value to set
+     * @return If the function succeeds, the return value is 0
+     */
+    public static native int setValueB(long key, String name, byte[] val);
+
+    /**
+     * Enumerate the Registry subkeys
+     * @param key The Registry key descriptor to use.
+     * @param name The name of the value to query
+     * @return Array of all subkey names
+     */
+    public static native String[] enumKeys(long key)
+        throws Error;
+
+    /**
+     * Enumerate the Registry values
+     * @param key The Registry key descriptor to use.
+     * @param name The name of the value to query
+     * @return Array of all value names
+     */
+    public static native String[] enumValues(long key)
+        throws Error;
+
+     /**
+     * Delete the Registry value
+     * @param key The Registry key descriptor to use.
+     * @param name The name of the value to delete
+     * @return If the function succeeds, the return value is 0
+     */
+    public static native int deleteValue(long key, String name);
+
+     /**
+     * Delete the Registry subkey
+     * @param root Root key, one of HKEY_*
+     * @param name Subkey to delete
+     * @param onlyIfEmpty If true will not delete a key if
+     *                    it contains any subkeys or values
+     * @return If the function succeeds, the return value is 0
+     */
+    public static native int deleteKey(int root, String name,
+                                       boolean onlyIfEmpty);
+
+
+}
diff --git a/connectors/jni/java/org/apache/tomcat/jni/SSL.java b/connectors/jni/java/org/apache/tomcat/jni/SSL.java
new file mode 100644
index 0000000..8cdfc1c
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/SSL.java
@@ -0,0 +1,328 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+/** SSL
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public final class SSL {
+
+    /*
+     * Type definitions mostly from mod_ssl
+     */
+    public static final int UNSET            = -1;
+    /*
+     * Define the certificate algorithm types
+     */
+    public static final int SSL_ALGO_UNKNOWN = 0;
+    public static final int SSL_ALGO_RSA     = (1<<0);
+    public static final int SSL_ALGO_DSA     = (1<<1);
+    public static final int SSL_ALGO_ALL     = (SSL_ALGO_RSA|SSL_ALGO_DSA);
+
+    public static final int SSL_AIDX_RSA     = 0;
+    public static final int SSL_AIDX_DSA     = 1;
+    public static final int SSL_AIDX_MAX     = 2;
+    /*
+     * Define IDs for the temporary RSA keys and DH params
+     */
+
+    public static final int SSL_TMP_KEY_RSA_512  = 0;
+    public static final int SSL_TMP_KEY_RSA_1024 = 1;
+    public static final int SSL_TMP_KEY_RSA_2048 = 2;
+    public static final int SSL_TMP_KEY_RSA_4096 = 3;
+    public static final int SSL_TMP_KEY_DH_512   = 4;
+    public static final int SSL_TMP_KEY_DH_1024  = 5;
+    public static final int SSL_TMP_KEY_DH_2048  = 6;
+    public static final int SSL_TMP_KEY_DH_4096  = 7;
+    public static final int SSL_TMP_KEY_MAX      = 8;
+
+    /*
+     * Define the SSL options
+     */
+    public static final int SSL_OPT_NONE           = 0;
+    public static final int SSL_OPT_RELSET         = (1<<0);
+    public static final int SSL_OPT_STDENVVARS     = (1<<1);
+    public static final int SSL_OPT_EXPORTCERTDATA = (1<<3);
+    public static final int SSL_OPT_FAKEBASICAUTH  = (1<<4);
+    public static final int SSL_OPT_STRICTREQUIRE  = (1<<5);
+    public static final int SSL_OPT_OPTRENEGOTIATE = (1<<6);
+    public static final int SSL_OPT_ALL            = (SSL_OPT_STDENVVARS|SSL_OPT_EXPORTCERTDATA|SSL_OPT_FAKEBASICAUTH|SSL_OPT_STRICTREQUIRE|SSL_OPT_OPTRENEGOTIATE);
+
+    /*
+     * Define the SSL Protocol options
+     */
+    public static final int SSL_PROTOCOL_NONE  = 0;
+    public static final int SSL_PROTOCOL_SSLV2 = (1<<0);
+    public static final int SSL_PROTOCOL_SSLV3 = (1<<1);
+    public static final int SSL_PROTOCOL_TLSV1 = (1<<2);
+    public static final int SSL_PROTOCOL_ALL   = (SSL_PROTOCOL_SSLV2|SSL_PROTOCOL_SSLV3|SSL_PROTOCOL_TLSV1);
+
+    /*
+     * Define the SSL verify levels
+     */
+    public static final int SSL_CVERIFY_UNSET          = UNSET;
+    public static final int SSL_CVERIFY_NONE           = 0;
+    public static final int SSL_CVERIFY_OPTIONAL       = 1;
+    public static final int SSL_CVERIFY_REQUIRE        = 2;
+    public static final int SSL_CVERIFY_OPTIONAL_NO_CA = 3;
+
+    /* Use either SSL_VERIFY_NONE or SSL_VERIFY_PEER, the last 2 options
+     * are 'ored' with SSL_VERIFY_PEER if they are desired
+     */
+    public static final int SSL_VERIFY_NONE                 = 0;
+    public static final int SSL_VERIFY_PEER                 = 1;
+    public static final int SSL_VERIFY_FAIL_IF_NO_PEER_CERT = 2;
+    public static final int SSL_VERIFY_CLIENT_ONCE          = 4;
+    public static final int SSL_VERIFY_PEER_STRICT          = (SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
+
+    public static final int SSL_OP_MICROSOFT_SESS_ID_BUG            = 0x00000001;
+    public static final int SSL_OP_NETSCAPE_CHALLENGE_BUG           = 0x00000002;
+    public static final int SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG = 0x00000008;
+    public static final int SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG      = 0x00000010;
+    public static final int SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER       = 0x00000020;
+    public static final int SSL_OP_MSIE_SSLV2_RSA_PADDING           = 0x00000040;
+    public static final int SSL_OP_SSLEAY_080_CLIENT_DH_BUG         = 0x00000080;
+    public static final int SSL_OP_TLS_D5_BUG                       = 0x00000100;
+    public static final int SSL_OP_TLS_BLOCK_PADDING_BUG            = 0x00000200;
+
+    /* Disable SSL 3.0/TLS 1.0 CBC vulnerability workaround that was added
+     * in OpenSSL 0.9.6d.  Usually (depending on the application protocol)
+     * the workaround is not needed.  Unfortunately some broken SSL/TLS
+     * implementations cannot handle it at all, which is why we include
+     * it in SSL_OP_ALL. */
+    public static final int SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS      = 0x00000800;
+
+    /* SSL_OP_ALL: various bug workarounds that should be rather harmless.
+     *             This used to be 0x000FFFFFL before 0.9.7. */
+    public static final int SSL_OP_ALL                              = 0x00000FFF;
+
+    /* As server, disallow session resumption on renegotiation */
+    public static final int SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION = 0x00010000;
+    /* If set, always create a new key when using tmp_dh parameters */
+    public static final int SSL_OP_SINGLE_DH_USE                    = 0x00100000;
+    /* Set to always use the tmp_rsa key when doing RSA operations,
+     * even when this violates protocol specs */
+    public static final int SSL_OP_EPHEMERAL_RSA                    = 0x00200000;
+    /* Set on servers to choose the cipher according to the server's
+     * preferences */
+    public static final int SSL_OP_CIPHER_SERVER_PREFERENCE         = 0x00400000;
+    /* If set, a server will allow a client to issue a SSLv3.0 version number
+     * as latest version supported in the premaster secret, even when TLSv1.0
+     * (version 3.1) was announced in the client hello. Normally this is
+     * forbidden to prevent version rollback attacks. */
+    public static final int SSL_OP_TLS_ROLLBACK_BUG                 = 0x00800000;
+
+    public static final int SSL_OP_NO_SSLv2                         = 0x01000000;
+    public static final int SSL_OP_NO_SSLv3                         = 0x02000000;
+    public static final int SSL_OP_NO_TLSv1                         = 0x04000000;
+
+    /* The next flag deliberately changes the ciphertest, this is a check
+     * for the PKCS#1 attack */
+    public static final int SSL_OP_PKCS1_CHECK_1                    = 0x08000000;
+    public static final int SSL_OP_PKCS1_CHECK_2                    = 0x10000000;
+    public static final int SSL_OP_NETSCAPE_CA_DN_BUG               = 0x20000000;
+    public static final int SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG  = 0x40000000;
+
+    public static final int SSL_CRT_FORMAT_UNDEF    = 0;
+    public static final int SSL_CRT_FORMAT_ASN1     = 1;
+    public static final int SSL_CRT_FORMAT_TEXT     = 2;
+    public static final int SSL_CRT_FORMAT_PEM      = 3;
+    public static final int SSL_CRT_FORMAT_NETSCAPE = 4;
+    public static final int SSL_CRT_FORMAT_PKCS12   = 5;
+    public static final int SSL_CRT_FORMAT_SMIME    = 6;
+    public static final int SSL_CRT_FORMAT_ENGINE   = 7;
+
+    public static final int SSL_MODE_CLIENT         = 0;
+    public static final int SSL_MODE_SERVER         = 1;
+    public static final int SSL_MODE_COMBINED       = 2;
+
+    public static final int SSL_SHUTDOWN_TYPE_UNSET    = 0;
+    public static final int SSL_SHUTDOWN_TYPE_STANDARD = 1;
+    public static final int SSL_SHUTDOWN_TYPE_UNCLEAN  = 2;
+    public static final int SSL_SHUTDOWN_TYPE_ACCURATE = 3;
+
+    public static final int SSL_INFO_SESSION_ID                = 0x0001;
+    public static final int SSL_INFO_CIPHER                    = 0x0002;
+    public static final int SSL_INFO_CIPHER_USEKEYSIZE         = 0x0003;
+    public static final int SSL_INFO_CIPHER_ALGKEYSIZE         = 0x0004;
+    public static final int SSL_INFO_CIPHER_VERSION            = 0x0005;
+    public static final int SSL_INFO_CIPHER_DESCRIPTION        = 0x0006;
+    public static final int SSL_INFO_PROTOCOL                  = 0x0007;
+
+    /* To obtain the CountryName of the Client Certificate Issuer
+     * use the SSL_INFO_CLIENT_I_DN + SSL_INFO_DN_COUNTRYNAME
+     */
+    public static final int SSL_INFO_CLIENT_S_DN               = 0x0010;
+    public static final int SSL_INFO_CLIENT_I_DN               = 0x0020;
+    public static final int SSL_INFO_SERVER_S_DN               = 0x0040;
+    public static final int SSL_INFO_SERVER_I_DN               = 0x0080;
+
+    public static final int SSL_INFO_DN_COUNTRYNAME            = 0x0001;
+    public static final int SSL_INFO_DN_STATEORPROVINCENAME    = 0x0002;
+    public static final int SSL_INFO_DN_LOCALITYNAME           = 0x0003;
+    public static final int SSL_INFO_DN_ORGANIZATIONNAME       = 0x0004;
+    public static final int SSL_INFO_DN_ORGANIZATIONALUNITNAME = 0x0005;
+    public static final int SSL_INFO_DN_COMMONNAME             = 0x0006;
+    public static final int SSL_INFO_DN_TITLE                  = 0x0007;
+    public static final int SSL_INFO_DN_INITIALS               = 0x0008;
+    public static final int SSL_INFO_DN_GIVENNAME              = 0x0009;
+    public static final int SSL_INFO_DN_SURNAME                = 0x000A;
+    public static final int SSL_INFO_DN_DESCRIPTION            = 0x000B;
+    public static final int SSL_INFO_DN_UNIQUEIDENTIFIER       = 0x000C;
+    public static final int SSL_INFO_DN_EMAILADDRESS           = 0x000D;
+
+    public static final int SSL_INFO_CLIENT_M_VERSION          = 0x0101;
+    public static final int SSL_INFO_CLIENT_M_SERIAL           = 0x0102;
+    public static final int SSL_INFO_CLIENT_V_START            = 0x0103;
+    public static final int SSL_INFO_CLIENT_V_END              = 0x0104;
+    public static final int SSL_INFO_CLIENT_A_SIG              = 0x0105;
+    public static final int SSL_INFO_CLIENT_A_KEY              = 0x0106;
+    public static final int SSL_INFO_CLIENT_CERT               = 0x0107;
+    public static final int SSL_INFO_CLIENT_V_REMAIN           = 0x0108;
+
+    public static final int SSL_INFO_SERVER_M_VERSION          = 0x0201;
+    public static final int SSL_INFO_SERVER_M_SERIAL           = 0x0202;
+    public static final int SSL_INFO_SERVER_V_START            = 0x0203;
+    public static final int SSL_INFO_SERVER_V_END              = 0x0204;
+    public static final int SSL_INFO_SERVER_A_SIG              = 0x0205;
+    public static final int SSL_INFO_SERVER_A_KEY              = 0x0206;
+    public static final int SSL_INFO_SERVER_CERT               = 0x0207;
+    /* Return client certificate chain.
+     * Add certificate chain number to that flag (0 ... verify depth)
+     */
+    public static final int SSL_INFO_CLIENT_CERT_CHAIN         = 0x0400;
+    /* Return OpenSSL version number */
+    public static native int version();
+
+    /* Return OpenSSL version string */
+    public static native String versionString();
+
+    /**
+     * Initialize OpenSSL support.
+     * This function needs to be called once for the
+     * lifetime of JVM. Library.init() has to be called before.
+     * @param engine Support for external a Crypto Device ("engine"),
+     *                usually
+     * a hardware accellerator card for crypto operations.
+     * @return APR status code
+     */
+    public static native int initialize(String engine);
+
+    /**
+     * Add content of the file to the PRNG
+     * @param filename Filename containing random data.
+     *        If null the default file will be tested.
+     *        The seed file is $RANDFILE if that environment variable is
+     *        set, $HOME/.rnd otherwise.
+     *        In case both files are unavailable builtin
+     *        random seed generator is used.
+     */
+    public static native boolean randLoad(String filename);
+
+    /**
+     * Writes a number of random bytes (currently 1024) to
+     * file <code>filename</code> which can be used to initialize the PRNG
+     * by calling randLoad in a later session.
+     * @param filename Filename to save the data
+     */
+    public static native boolean randSave(String filename);
+
+    /**
+     * Creates random data to filename
+     * @param filename Filename to save the data
+     * @param len The length of random sequence in bytes
+     * @param base64 Output the data in Base64 encoded format
+     */
+    public static native boolean randMake(String filename, int len,
+                                          boolean base64);
+
+    /**
+     * Sets global random filename.
+     * @param filename Filename to use.
+     *        If set it will be used for SSL initialization
+     *        and all contexts where explicitly not set.
+     */
+    public static native void randSet(String filename);
+
+    /**
+     * Initialize new BIO
+     * @param pool The pool to use.
+     * @param callback BIOCallback to use
+     * @return New BIO handle
+     */
+     public static native long newBIO(long pool, BIOCallback callback)
+            throws Exception;
+
+    /**
+     * Close BIO and derefrence callback object
+     * @param bio BIO to close and destroy.
+     * @return APR Status code
+     */
+     public static native int closeBIO(long bio);
+
+    /**
+     * Set global Password callback for obtaining passwords.
+     * @param callback PasswordCallback implementation to use.
+     */
+     public static native void setPasswordCallback(PasswordCallback callback);
+
+    /**
+     * Set global Password for decrypting certificates and keys.
+     * @param password Password to use.
+     */
+     public static native void setPassword(String password);
+
+    /**
+     * Generate temporary RSA key.
+     * <br />
+     * Index can be one of:
+     * <PRE>
+     * SSL_TMP_KEY_RSA_512
+     * SSL_TMP_KEY_RSA_1024
+     * SSL_TMP_KEY_RSA_2048
+     * SSL_TMP_KEY_RSA_4096
+     * </PRE>
+     * By default 512 and 1024 keys are generated on startup.
+     * You can use a low priority thread to generate them on the fly.
+     * @param idx temporary key index.
+     */
+    public static native boolean generateRSATempKey(int idx);
+
+    /**
+     * Load temporary DSA key from file
+     * <br />
+     * Index can be one of:
+     * <PRE>
+     * SSL_TMP_KEY_DH_512
+     * SSL_TMP_KEY_DH_1024
+     * SSL_TMP_KEY_DH_2048
+     * SSL_TMP_KEY_DH_4096
+     * </PRE>
+     * @param idx temporary key index.
+     * @param file File contatining DH params.
+     */
+    public static native boolean loadDSATempKey(int idx, String file);
+
+    /**
+     * Return last SSL error string
+     */
+    public static native String getLastError();
+}
diff --git a/connectors/jni/java/org/apache/tomcat/jni/SSLContext.java b/connectors/jni/java/org/apache/tomcat/jni/SSLContext.java
new file mode 100644
index 0000000..90472f8
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/SSLContext.java
@@ -0,0 +1,277 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+/** SSL Context
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public final class SSLContext {
+
+
+    /**
+     * Initialize new SSL context
+     * @param pool The pool to use.
+     * @param protocol The SSL protocol to use. It can be one of:
+     * <PRE>
+     * SSL_PROTOCOL_SSLV2
+     * SSL_PROTOCOL_SSLV3
+     * SSL_PROTOCOL_SSLV2 | SSL_PROTOCOL_SSLV3
+     * SSL_PROTOCOL_TLSV1
+     * SSL_PROTOCOL_ALL
+     * </PRE>
+     * @param mode SSL mode to use
+     * <PRE>
+     * SSL_MODE_CLIENT
+     * SSL_MODE_SERVER
+     * SSL_MODE_COMBINED
+     * </PRE>
+     */
+    public static native long make(long pool, int protocol, int mode)
+        throws Exception;
+
+    /**
+     * Free the resources used by the Context
+     * @param ctx Server or Client context to free.
+     * @return APR Status code.
+     */
+    public static native int free(long ctx);
+
+    /**
+     * Set Session context id. Usually host:port combination.
+     * @param ctx Context to use.
+     * @param id  String that uniquely identifies this context.
+     */
+    public static native void setContextId(long ctx, String id);
+
+    /**
+     * Asssociate BIOCallback for input or output data capture.
+     * <br />
+     * First word in the output string will contain error
+     * level in the form:
+     * <PRE>
+     * [ERROR]  -- Critical error messages
+     * [WARN]   -- Varning messages
+     * [INFO]   -- Informational messages
+     * [DEBUG]  -- Debugging messaged
+     * </PRE>
+     * Callback can use that word to determine application logging level
+     * by intercepting <b>write</b> call.
+     * If the <b>bio</b> is set to 0 no error messages will be displayed.
+     * Default is to use the stderr output stream.
+     * @param ctx Server or Client context to use.
+     * @param bio BIO handle to use, created with SSL.newBIO
+     * @param dir BIO direction (1 for input 0 for output).
+     */
+    public static native void setBIO(long ctx, long bio, int dir);
+
+    /**
+     * Set OpenSSL Option.
+     * @param ctx Server or Client context to use.
+     * @param options  See SSL.SSL_OP_* for option flags.
+     */
+    public static native void setOptions(long ctx, int options);
+
+    /**
+     * Sets the "quiet shutdown" flag for <b>ctx</b> to be
+     * <b>mode</b>. SSL objects created from <b>ctx</b> inherit the
+     * <b>mode</b> valid at the time and may be 0 or 1.
+     * <br />
+     * Normally when a SSL connection is finished, the parties must send out
+     * "close notify" alert messages using L<SSL_shutdown(3)|SSL_shutdown(3)>
+     * for a clean shutdown.
+     * <br />
+     * When setting the "quiet shutdown" flag to 1, <b>SSL.shutdown</b>
+     * will set the internal flags to SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN.
+     * (<b>SSL_shutdown</b> then behaves like called with
+     * SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN.)
+     * The session is thus considered to be shutdown, but no "close notify" alert
+     * is sent to the peer. This behaviour violates the TLS standard.
+     * The default is normal shutdown behaviour as described by the TLS standard.
+     * @param ctx Server or Client context to use.
+     * @param mode True to set the quiet shutdown.
+     */
+    public static native void setQuietShutdown(long ctx, boolean mode);
+
+    /**
+     * Cipher Suite available for negotiation in SSL handshake.
+     * <br />
+     * This complex directive uses a colon-separated cipher-spec string consisting
+     * of OpenSSL cipher specifications to configure the Cipher Suite the client
+     * is permitted to negotiate in the SSL handshake phase. Notice that this
+     * directive can be used both in per-server and per-directory context.
+     * In per-server context it applies to the standard SSL handshake when a
+     * connection is established. In per-directory context it forces a SSL
+     * renegotation with the reconfigured Cipher Suite after the HTTP request
+     * was read but before the HTTP response is sent.
+     * @param ctx Server or Client context to use.
+     * @param ciphers An SSL cipher specification.
+     */
+    public static native boolean setCipherSuite(long ctx, String ciphers)
+        throws Exception;
+
+    /**
+     * Set File of concatenated PEM-encoded CA CRLs or
+     * directory of PEM-encoded CA Certificates for Client Auth
+     * <br />
+     * This directive sets the all-in-one file where you can assemble the
+     * Certificate Revocation Lists (CRL) of Certification Authorities (CA)
+     * whose clients you deal with. These are used for Client Authentication.
+     * Such a file is simply the concatenation of the various PEM-encoded CRL
+     * files, in order of preference.
+     * <br />
+     * The files in this directory have to be PEM-encoded and are accessed through
+     * hash filenames. So usually you can't just place the Certificate files there:
+     * you also have to create symbolic links named hash-value.N. And you should
+     * always make sure this directory contains the appropriate symbolic links.
+     * Use the Makefile which comes with mod_ssl to accomplish this task.
+     * @param ctx Server or Client context to use.
+     * @param file File of concatenated PEM-encoded CA CRLs for Client Auth.
+     * @param path Directory of PEM-encoded CA Certificates for Client Auth.
+     */
+    public static native boolean setCARevocation(long ctx, String file,
+                                                 String path)
+        throws Exception;
+
+    /**
+     * Set File of PEM-encoded Server CA Certificates
+     * <br />
+     * This directive sets the optional all-in-one file where you can assemble the
+     * certificates of Certification Authorities (CA) which form the certificate
+     * chain of the server certificate. This starts with the issuing CA certificate
+     * of of the server certificate and can range up to the root CA certificate.
+     * Such a file is simply the concatenation of the various PEM-encoded CA
+     * Certificate files, usually in certificate chain order.
+     * <br />
+     * But be careful: Providing the certificate chain works only if you are using
+     * a single (either RSA or DSA) based server certificate. If you are using a
+     * coupled RSA+DSA certificate pair, this will work only if actually both
+     * certificates use the same certificate chain. Else the browsers will be
+     * confused in this situation.
+     * @param ctx Server or Client context to use.
+     * @param file File of PEM-encoded Server CA Certificates.
+     * @param skipfirst Skip first certificate if chain file is inside
+     *                  certificate file.
+     */
+    public static native boolean setCertificateChainFile(long ctx, String file,
+                                                         boolean skipfirst);
+
+    /**
+     * Set Certificate
+     * <br />
+     * Point setCertificateFile at a PEM encoded certificate.  If
+     * the certificate is encrypted, then you will be prompted for a
+     * pass phrase.  Note that a kill -HUP will prompt again. A test
+     * certificate can be generated with `make certificate' under
+     * built time. Keep in mind that if you've both a RSA and a DSA
+     * certificate you can configure both in parallel (to also allow
+     * the use of DSA ciphers, etc.)
+     * <br />
+     * If the key is not combined with the certificate, use key param
+     * to point at the key file.  Keep in mind that if
+     * you've both a RSA and a DSA private key you can configure
+     * both in parallel (to also allow the use of DSA ciphers, etc.)
+     * @param ctx Server or Client context to use.
+     * @param cert Certificate file.
+     * @param key Private Key file to use if not in cert.
+     * @param password Certificate password. If null and certificate
+     *                 is encrypted, password prompt will be dispayed.
+     * @param idx Certificate index SSL_AIDX_RSA or SSL_AIDX_DSA.
+     */
+    public static native boolean setCertificate(long ctx, String cert,
+                                                String key, String password,
+                                                int idx)
+        throws Exception;
+
+    /**
+     * Set File and Directory of concatenated PEM-encoded CA Certificates
+     * for Client Auth
+     * <br />
+     * This directive sets the all-in-one file where you can assemble the
+     * Certificates of Certification Authorities (CA) whose clients you deal with.
+     * These are used for Client Authentication. Such a file is simply the
+     * concatenation of the various PEM-encoded Certificate files, in order of
+     * preference. This can be used alternatively and/or additionally to
+     * path.
+     * <br />
+     * The files in this directory have to be PEM-encoded and are accessed through
+     * hash filenames. So usually you can't just place the Certificate files there:
+     * you also have to create symbolic links named hash-value.N. And you should
+     * always make sure this directory contains the appropriate symbolic links.
+     * Use the Makefile which comes with mod_ssl to accomplish this task.
+     * @param ctx Server or Client context to use.
+     * @param file File of concatenated PEM-encoded CA Certificates for
+     *             Client Auth.
+     * @param path Directory of PEM-encoded CA Certificates for Client Auth.
+     */
+    public static native boolean setCACertificate(long ctx, String file,
+                                                  String path)
+        throws Exception;
+
+    /**
+     * Set SSL connection shutdown type
+     * <br />
+     * The following levels are available for level:
+     * <PRE>
+     * SSL_SHUTDOWN_TYPE_STANDARD
+     * SSL_SHUTDOWN_TYPE_UNCLEAN
+     * SSL_SHUTDOWN_TYPE_ACCURATE
+     * </PRE>
+     * @param ctx Server or Client context to use.
+     * @param type Shutdown type to use.
+     */
+    public static native void setShutdowType(long ctx, int type);
+
+    /**
+     * Set Type of Client Certificate verification and Maximum depth of CA Certificates
+     * in Client Certificate verification.
+     * <br />
+     * This directive sets the Certificate verification level for the Client
+     * Authentication. Notice that this directive can be used both in per-server
+     * and per-directory context. In per-server context it applies to the client
+     * authentication process used in the standard SSL handshake when a connection
+     * is established. In per-directory context it forces a SSL renegotation with
+     * the reconfigured client verification level after the HTTP request was read
+     * but before the HTTP response is sent.
+     * <br />
+     * The following levels are available for level:
+     * <PRE>
+     * SSL_CVERIFY_NONE           - No client Certificate is required at all
+     * SSL_CVERIFY_OPTIONAL       - The client may present a valid Certificate
+     * SSL_CVERIFY_REQUIRE        - The client has to present a valid Certificate
+     * SSL_CVERIFY_OPTIONAL_NO_CA - The client may present a valid Certificate
+     *                              but it need not to be (successfully) verifiable
+     * </PRE>
+     * <br />
+     * The depth actually is the maximum number of intermediate certificate issuers,
+     * i.e. the number of CA certificates which are max allowed to be followed while
+     * verifying the client certificate. A depth of 0 means that self-signed client
+     * certificates are accepted only, the default depth of 1 means the client
+     * certificate can be self-signed or has to be signed by a CA which is directly
+     * known to the server (i.e. the CA's certificate is under
+     * <code>setCACertificatePath</code>), etc.
+     * @param ctx Server or Client context to use.
+     * @param level Type of Client Certificate verification.
+     * @param depth Maximum depth of CA Certificates in Client Certificate
+     *              verification.
+     */
+    public static native void setVerify(long ctx, int level, int depth);
+
+}
diff --git a/connectors/jni/java/org/apache/tomcat/jni/SSLSocket.java b/connectors/jni/java/org/apache/tomcat/jni/SSLSocket.java
new file mode 100644
index 0000000..fd408ff
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/SSLSocket.java
@@ -0,0 +1,89 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+/** SSL Socket
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public class SSLSocket {
+
+    /**
+     * Attach APR socket on a SSL connection.
+     * @param ctx SSLContext to use.
+     * @param sock APR Socket that already did physical connect or accept.
+     * @return APR_STATUS code.
+     */
+    public static native int attach(long ctx, long sock)
+        throws Exception;
+
+    /**
+     * Do a SSL handshake.
+     * @param thesocket The socket to use
+     */
+    public static native int handshake(long thesocket);
+
+    /**
+     * Do a SSL renegotiation.
+     * SSL supports per-directory re-configuration of SSL parameters.
+     * This is implemented by performing an SSL renegotiation of the
+     * re-configured parameters after the request is read, but before the
+     * response is sent. In more detail: the renegotiation happens after the
+     * request line and MIME headers were read, but _before_ the attached
+     * request body is read. The reason simply is that in the HTTP protocol
+     * usually there is no acknowledgment step between the headers and the
+     * body (there is the 100-continue feature and the chunking facility
+     * only), so Apache has no API hook for this step.
+     *
+     * @param thesocket The socket to use
+     */
+    public static native int renegotiate(long thesocket);
+
+    /**
+     * Retrun SSL Info parameter as byte array.
+     *
+     * @param sock The socket to read the data from.
+     * @param id Parameter id.
+     * @return Byte array containing info id value.
+     */
+    public static native byte[] getInfoB(long sock, int id)
+        throws Exception;
+
+    /**
+     * Retrun SSL Info parameter as String.
+     *
+     * @param sock The socket to read the data from.
+     * @param id Parameter id.
+     * @return String containing info id value.
+     */
+    public static native String getInfoS(long sock, int id)
+        throws Exception;
+
+    /**
+     * Retrun SSL Info parameter as integer.
+     *
+     * @param sock The socket to read the data from.
+     * @param id Parameter id.
+     * @return Integer containing info id value or -1 on error.
+     */
+    public static native int getInfoI(long sock, int id)
+        throws Exception;
+
+}
diff --git a/connectors/jni/java/org/apache/tomcat/jni/Shm.java b/connectors/jni/java/org/apache/tomcat/jni/Shm.java
new file mode 100644
index 0000000..8384405
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/Shm.java
@@ -0,0 +1,124 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+import java.nio.ByteBuffer;
+
+/** Shm
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public class Shm {
+
+    /**
+     * Create and make accessable a shared memory segment.
+     * <br />
+     * A note about Anonymous vs. Named shared memory segments:<br />
+     *         Not all plaforms support anonymous shared memory segments, but in
+     *         some cases it is prefered over other types of shared memory
+     *         implementations. Passing a NULL 'file' parameter to this function
+     *         will cause the subsystem to use anonymous shared memory segments.
+     *         If such a system is not available, APR_ENOTIMPL is returned.
+     * <br />
+     * A note about allocation sizes:<br />
+     *         On some platforms it is necessary to store some metainformation
+     *         about the segment within the actual segment. In order to supply
+     *         the caller with the requested size it may be necessary for the
+     *         implementation to request a slightly greater segment length
+     *         from the subsystem. In all cases, the apr_shm_baseaddr_get()
+     *         function will return the first usable byte of memory.
+     * @param reqsize The desired size of the segment.
+     * @param filename The file to use for shared memory on platforms that
+     *        require it.
+     * @param pool the pool from which to allocate the shared memory
+     *        structure.
+     * @return The created shared memory structure.
+     *
+     */
+    public static native long create(long reqsize, String filename, long pool)
+        throws Error;
+
+    /**
+     * Remove shared memory segment associated with a filename.
+     * <br />
+     * This function is only supported on platforms which support
+     * name-based shared memory segments, and will return APR_ENOTIMPL on
+     * platforms without such support.
+     * @param filename The filename associated with shared-memory segment which
+     *        needs to be removed
+     * @param pool The pool used for file operations
+     */
+    public static native int remove(String filename, long pool);
+
+    /**
+     * Destroy a shared memory segment and associated memory.
+     * @param m The shared memory segment structure to destroy.
+     */
+    public static native int destroy(long m);
+
+    /**
+     * Attach to a shared memory segment that was created
+     * by another process.
+     * @param filename The file used to create the original segment.
+     *        (This MUST match the original filename.)
+     * @param pool the pool from which to allocate the shared memory
+     *        structure for this process.
+     * @return The created shared memory structure.
+     */
+    public static native long attach(String filename, long pool)
+        throws Error;
+
+    /**
+     * Detach from a shared memory segment without destroying it.
+     * @param m The shared memory structure representing the segment
+     *        to detach from.
+     */
+    public static native int detach(long m);
+
+    /**
+     * Retrieve the base address of the shared memory segment.
+     * NOTE: This address is only usable within the callers address
+     * space, since this API does not guarantee that other attaching
+     * processes will maintain the same address mapping.
+     * @param m The shared memory segment from which to retrieve
+     *        the base address.
+     * @return address, aligned by APR_ALIGN_DEFAULT.
+     */
+    public static native long baseaddr(long m);
+
+    /**
+     * Retrieve the length of a shared memory segment in bytes.
+     * @param m The shared memory segment from which to retrieve
+     *        the segment length.
+     */
+    public static native long size(long m);
+
+    /**
+     * Retrieve new ByteBuffer base address of the shared memory segment.
+     * NOTE: This address is only usable within the callers address
+     * space, since this API does not guarantee that other attaching
+     * processes will maintain the same address mapping.
+     * @param m The shared memory segment from which to retrieve
+     *        the base address.
+     * @return address, aligned by APR_ALIGN_DEFAULT.
+     */
+    public static native ByteBuffer buffer(long m);
+
+}
diff --git a/connectors/jni/java/org/apache/tomcat/jni/Sockaddr.java b/connectors/jni/java/org/apache/tomcat/jni/Sockaddr.java
new file mode 100644
index 0000000..3f2b90c
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/Sockaddr.java
@@ -0,0 +1,42 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+/** Sockaddr
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public class Sockaddr {
+
+   /** The pool to use... */
+    public long pool;
+    /** The hostname */
+    public String hostname;
+    /** Either a string of the port number or the service name for the port */
+    public String servname;
+    /** The numeric port */
+    public int port;
+    /** The family */
+    public int family;
+    /** If multiple addresses were found by apr_sockaddr_info_get(), this
+     *  points to a representation of the next address. */
+    public long next;
+
+}
diff --git a/connectors/jni/java/org/apache/tomcat/jni/Socket.java b/connectors/jni/java/org/apache/tomcat/jni/Socket.java
new file mode 100644
index 0000000..161edb0
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/Socket.java
@@ -0,0 +1,547 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+/* Import needed classes */
+import java.nio.ByteBuffer;
+
+/** Socket
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public class Socket {
+
+    /* Standard socket defines */
+    public static final int SOCK_STREAM = 0;
+    public static final int SOCK_DGRAM  = 1;
+    /*
+     * apr_sockopt Socket option definitions
+     */
+    public static final int APR_SO_LINGER       = 1;    /** Linger */
+    public static final int APR_SO_KEEPALIVE    = 2;    /** Keepalive */
+    public static final int APR_SO_DEBUG        = 4;    /** Debug */
+    public static final int APR_SO_NONBLOCK     = 8;    /** Non-blocking IO */
+    public static final int APR_SO_REUSEADDR    = 16;   /** Reuse addresses */
+    public static final int APR_SO_SNDBUF       = 64;   /** Send buffer */
+    public static final int APR_SO_RCVBUF       = 128;  /** Receive buffer */
+    public static final int APR_SO_DISCONNECTED = 256;  /** Disconnected */
+    /** For SCTP sockets, this is mapped to STCP_NODELAY internally. */
+    public static final int APR_TCP_NODELAY     = 512;
+    public static final int APR_TCP_NOPUSH      = 1024; /** No push */
+    /** This flag is ONLY set internally when we set APR_TCP_NOPUSH with
+     * APR_TCP_NODELAY set to tell us that APR_TCP_NODELAY should be turned on
+     * again when NOPUSH is turned off
+     */
+    public static final int APR_RESET_NODELAY   = 2048;
+    /** Set on non-blocking sockets (timeout != 0) on which the
+     * previous read() did not fill a buffer completely.  the next
+     * apr_socket_recv()  will first call select()/poll() rather than
+     * going straight into read().  (Can also be set by an application to
+     * force a select()/poll() call before the next read, in cases where
+     * the app expects that an immediate read would fail.)
+     */
+    public static final int APR_INCOMPLETE_READ = 4096;
+    /** like APR_INCOMPLETE_READ, but for write
+     */
+    public static final int APR_INCOMPLETE_WRITE = 8192;
+    /** Don't accept IPv4 connections on an IPv6 listening socket.
+     */
+    public static final int APR_IPV6_V6ONLY      = 16384;
+    /** Delay accepting of new connections until data is available.
+     */
+    public static final int APR_TCP_DEFER_ACCEPT = 32768;
+
+    /** Define what type of socket shutdown should occur.
+     * apr_shutdown_how_e enum
+     */
+    public static final int APR_SHUTDOWN_READ      = 0; /** no longer allow read request */
+    public static final int APR_SHUTDOWN_WRITE     = 1; /** no longer allow write requests */
+    public static final int APR_SHUTDOWN_READWRITE = 2; /** no longer allow read or write requests */
+
+    public static final int APR_IPV4_ADDR_OK = 0x01;
+    public static final int APR_IPV6_ADDR_OK = 0x02;
+
+    /* TODO: Missing:
+     * APR_INET
+     * APR_UNSPEC
+     * APR_INET6
+     */
+    public static final int APR_UNSPEC = 0;
+    public static final int APR_INET   = 1;
+    public static final int APR_INET6  = 2;
+
+    public static final int APR_PROTO_TCP  =   6; /** TCP  */
+    public static final int APR_PROTO_UDP  =  17; /** UDP  */
+    public static final int APR_PROTO_SCTP = 132; /** SCTP */
+
+    /**
+     * Enum to tell us if we're interested in remote or local socket
+     * apr_interface_e
+     */
+    public static final int APR_LOCAL  = 0;
+    public static final int APR_REMOTE = 1;
+
+    /* Socket.get types */
+    public static final int SOCKET_GET_POOL = 0;
+    public static final int SOCKET_GET_IMPL = 1;
+    public static final int SOCKET_GET_APRS = 2;
+    public static final int SOCKET_GET_TYPE = 3;
+
+    /**
+     * Create a socket.
+     * @param family The address family of the socket (e.g., APR_INET).
+     * @param type The type of the socket (e.g., SOCK_STREAM).
+     * @param protocol The protocol of the socket (e.g., APR_PROTO_TCP).
+     * @param cont The parent pool to use
+     * @return The new socket that has been set up.
+     */
+    public static native long create(int family, int type,
+                                     int protocol, long cont)
+        throws Exception;
+
+
+    /**
+     * Shutdown either reading, writing, or both sides of a socket.
+     * <br />
+     * This does not actually close the socket descriptor, it just
+     *      controls which calls are still valid on the socket.
+     * @param thesocket The socket to close
+     * @param how How to shutdown the socket.  One of:
+     * <PRE>
+     * APR_SHUTDOWN_READ         no longer allow read requests
+     * APR_SHUTDOWN_WRITE        no longer allow write requests
+     * APR_SHUTDOWN_READWRITE    no longer allow read or write requests
+     * </PRE>
+     */
+    public static native int shutdown(long thesocket, int how);
+
+    /**
+     * Close a socket.
+     * @param thesocket The socket to close
+     */
+    public static native int close(long thesocket);
+
+    /**
+     * Destroy a pool associated with socket
+     * @param thesocket The destroy
+     */
+    public static native void destroy(long thesocket);
+
+    /**
+     * Bind the socket to its associated port
+     * @param sock The socket to bind
+     * @param sa The socket address to bind to
+     * This may be where we will find out if there is any other process
+     *      using the selected port.
+     */
+    public static native int bind(long sock, long sa);
+
+    /**
+     * Listen to a bound socket for connections.
+     * @param sock The socket to listen on
+     * @param backlog The number of outstanding connections allowed in the sockets
+     *                listen queue.  If this value is less than zero, the listen
+     *                queue size is set to zero.
+     */
+    public static native int listen(long sock, int backlog);
+
+    /**
+     * Accept a new connection request
+     * @param sock The socket we are listening on.
+     * @param pool The pool for the new socket.
+     * @return  A copy of the socket that is connected to the socket that
+     *          made the connection request.  This is the socket which should
+     *          be used for all future communication.
+     */
+    public static native long accept(long sock)
+        throws Exception;
+
+    /**
+     * Set an OS level accept filter.
+     * @param sock The socket to put the accept filter on.
+     * @param name The accept filter
+     * @param args Any extra args to the accept filter.  Passing NULL here removes
+     *             the accept filter.
+     */
+    public static native int acceptfilter(long sock, String name, String args);
+
+    /**
+     * Query the specified socket if at the OOB/Urgent data mark
+     * @param sock The socket to query
+     * @return True if socket is at the OOB/urgent mark,
+     *         otherwise return false.
+     */
+    public static native boolean atmark(long sock);
+
+    /**
+     * Issue a connection request to a socket either on the same machine
+     * or a different one.
+     * @param sock The socket we wish to use for our side of the connection
+     * @param sa The address of the machine we wish to connect to.
+     */
+    public static native int connect(long sock, long sa);
+
+    /**
+     * Send data over a network.
+     * <PRE>
+     * This functions acts like a blocking write by default.  To change
+     * this behavior, use apr_socket_timeout_set() or the APR_SO_NONBLOCK
+     * socket option.
+     *
+     * It is possible for both bytes to be sent and an error to be returned.
+     *
+     * APR_EINTR is never returned.
+     * </PRE>
+     * @param sock The socket to send the data over.
+     * @param buf The buffer which contains the data to be sent.
+     * @param offset Offset in the byte buffer.
+     * @param len The number of bytes to write; (-1) for full array.
+     * @return The number of bytes send.
+     *
+     */
+    public static native int send(long sock, byte[] buf, int offset, int len);
+
+    /**
+     * Send data over a network.
+     * <PRE>
+     * This functions acts like a blocking write by default.  To change
+     * this behavior, use apr_socket_timeout_set() or the APR_SO_NONBLOCK
+     * socket option.
+     *
+     * It is possible for both bytes to be sent and an error to be returned.
+     *
+     * APR_EINTR is never returned.
+     * </PRE>
+     * @param sock The socket to send the data over.
+     * @param buf The Byte buffer which contains the data to be sent.
+     * @param offset The offset within the buffer array of the first buffer from
+     *               which bytes are to be retrieved; must be non-negative
+     *               and no larger than buf.length
+     * @param len The maximum number of buffers to be accessed; must be non-negative
+     *            and no larger than buf.length - offset
+     * @return The number of bytes send.
+     *
+     */
+    public static native int sendb(long sock, ByteBuffer buf,
+                                   int offset, int len);
+    /**
+     * Send data over a network using internally set ByteBuffer
+     */
+    public static native int sendbb(long sock,
+                                   int offset, int len);
+
+    /**
+     * Send multiple packets of data over a network.
+     * <PRE>
+     * This functions acts like a blocking write by default.  To change
+     * this behavior, use apr_socket_timeout_set() or the APR_SO_NONBLOCK
+     * socket option.
+     * The number of bytes actually sent is stored in argument 3.
+     *
+     * It is possible for both bytes to be sent and an error to be returned.
+     *
+     * APR_EINTR is never returned.
+     * </PRE>
+     * @param sock The socket to send the data over.
+     * @param vec The array from which to get the data to send.
+     *
+     */
+    public static native int sendv(long sock, byte[][] vec);
+
+    /**
+     * @param sock The socket to send from
+     * @param where The apr_sockaddr_t describing where to send the data
+     * @param flags The flags to use
+     * @param buf  The data to send
+     * @param offset Offset in the byte buffer.
+     * @param len  The length of the data to send
+     */
+    public static native int sendto(long sock, long where, int flags,
+                                    byte[] buf, int offset, int len);
+
+    /**
+     * Read data from a network.
+     *
+     * <PRE>
+     * This functions acts like a blocking read by default.  To change
+     * this behavior, use apr_socket_timeout_set() or the APR_SO_NONBLOCK
+     * socket option.
+     * The number of bytes actually received is stored in argument 3.
+     *
+     * It is possible for both bytes to be received and an APR_EOF or
+     * other error to be returned.
+     *
+     * APR_EINTR is never returned.
+     * </PRE>
+     * @param sock The socket to read the data from.
+     * @param buf The buffer to store the data in.
+     * @param offset Offset in the byte buffer.
+     * @param nbytes The number of bytes to read (-1) for full array.
+     * @return the number of bytes received.
+     */
+    public static native int recv(long sock, byte[] buf, int offset, int nbytes);
+
+    /**
+     * Read data from a network with timeout.
+     *
+     * <PRE>
+     * This functions acts like a blocking read by default.  To change
+     * this behavior, use apr_socket_timeout_set() or the APR_SO_NONBLOCK
+     * socket option.
+     * The number of bytes actually received is stored in argument 3.
+     *
+     * It is possible for both bytes to be received and an APR_EOF or
+     * other error to be returned.
+     *
+     * APR_EINTR is never returned.
+     * </PRE>
+     * @param sock The socket to read the data from.
+     * @param buf The buffer to store the data in.
+     * @param offset Offset in the byte buffer.
+     * @param nbytes The number of bytes to read (-1) for full array.
+     * @param timeout The socket timeout in microseconds.
+     * @return the number of bytes received.
+     */
+    public static native int recvt(long sock, byte[] buf, int offset,
+                                   int nbytes, long timeout);
+
+    /**
+     * Read data from a network.
+     *
+     * <PRE>
+     * This functions acts like a blocking read by default.  To change
+     * this behavior, use apr_socket_timeout_set() or the APR_SO_NONBLOCK
+     * socket option.
+     * The number of bytes actually received is stored in argument 3.
+     *
+     * It is possible for both bytes to be received and an APR_EOF or
+     * other error to be returned.
+     *
+     * APR_EINTR is never returned.
+     * </PRE>
+     * @param sock The socket to read the data from.
+     * @param buf The buffer to store the data in.
+     * @param offset Offset in the byte buffer.
+     * @param nbytes The number of bytes to read (-1) for full array.
+     * @return the number of bytes received.
+     */
+    public static native int recvb(long sock, ByteBuffer buf,
+                                   int offset, int nbytes);
+    /**
+     * Read data from a network using internally set ByteBuffer
+     */
+    public static native int recvbb(long sock,
+                                    int offset, int nbytes);
+    /**
+     * Read data from a network with timeout.
+     *
+     * <PRE>
+     * This functions acts like a blocking read by default.  To change
+     * this behavior, use apr_socket_timeout_set() or the APR_SO_NONBLOCK
+     * socket option.
+     * The number of bytes actually received is stored in argument 3.
+     *
+     * It is possible for both bytes to be received and an APR_EOF or
+     * other error to be returned.
+     *
+     * APR_EINTR is never returned.
+     * </PRE>
+     * @param sock The socket to read the data from.
+     * @param buf The buffer to store the data in.
+     * @param offset Offset in the byte buffer.
+     * @param nbytes The number of bytes to read (-1) for full array.
+     * @param timeout The socket timeout in microseconds.
+     * @return the number of bytes received.
+     */
+    public static native int recvbt(long sock, ByteBuffer buf,
+                                    int offset, int nbytes, long timeout);
+    /**
+     * Read data from a network with timeout using internally set ByteBuffer
+     */
+    public static native int recvbbt(long sock,
+                                     int offset, int nbytes, long timeout);
+
+    /**
+     * @param from The apr_sockaddr_t to fill in the recipient info
+     * @param sock The socket to use
+     * @param flags The flags to use
+     * @param buf  The buffer to use
+     * @param offset Offset in the byte buffer.
+     * @param nbytes The number of bytes to read (-1) for full array.
+     * @return the number of bytes received.
+     */
+    public static native int recvFrom(long from, long sock, int flags,
+                                      byte[] buf, int offset, int nbytes);
+
+    /**
+     * Setup socket options for the specified socket
+     * @param sock The socket to set up.
+     * @param opt The option we would like to configure.  One of:
+     * <PRE>
+     * APR_SO_DEBUG      --  turn on debugging information
+     * APR_SO_KEEPALIVE  --  keep connections active
+     * APR_SO_LINGER     --  lingers on close if data is present
+     * APR_SO_NONBLOCK   --  Turns blocking on/off for socket
+     *                       When this option is enabled, use
+     *                       the APR_STATUS_IS_EAGAIN() macro to
+     *                       see if a send or receive function
+     *                       could not transfer data without
+     *                       blocking.
+     * APR_SO_REUSEADDR  --  The rules used in validating addresses
+     *                       supplied to bind should allow reuse
+     *                       of local addresses.
+     * APR_SO_SNDBUF     --  Set the SendBufferSize
+     * APR_SO_RCVBUF     --  Set the ReceiveBufferSize
+     * </PRE>
+     * @param on Value for the option.
+     */
+    public static native int optSet(long sock, int opt, int on);
+
+    /**
+     * Query socket options for the specified socket
+     * @param sock The socket to query
+     * @param opt The option we would like to query.  One of:
+     * <PRE>
+     * APR_SO_DEBUG      --  turn on debugging information
+     * APR_SO_KEEPALIVE  --  keep connections active
+     * APR_SO_LINGER     --  lingers on close if data is present
+     * APR_SO_NONBLOCK   --  Turns blocking on/off for socket
+     * APR_SO_REUSEADDR  --  The rules used in validating addresses
+     *                       supplied to bind should allow reuse
+     *                       of local addresses.
+     * APR_SO_SNDBUF     --  Set the SendBufferSize
+     * APR_SO_RCVBUF     --  Set the ReceiveBufferSize
+     * APR_SO_DISCONNECTED -- Query the disconnected state of the socket.
+     *                       (Currently only used on Windows)
+     * </PRE>
+     * @return Socket option returned on the call.
+     */
+    public static native int optGet(long sock, int opt)
+        throws Exception;
+
+    /**
+     * Setup socket timeout for the specified socket
+     * @param sock The socket to set up.
+     * @param t Value for the timeout in microseconds.
+     * <PRE>
+     * t > 0  -- read and write calls return APR_TIMEUP if specified time
+     *           elapsess with no data read or written
+     * t == 0 -- read and write calls never block
+     * t < 0  -- read and write calls block
+     * </PRE>
+     */
+    public static native int timeoutSet(long sock, long t);
+
+    /**
+     * Query socket timeout for the specified socket
+     * @param sock The socket to query
+     * @return Socket timeout returned from the query.
+     */
+    public static native long timeoutGet(long sock)
+        throws Exception;
+
+    /**
+     * Send a file from an open file descriptor to a socket, along with
+     * optional headers and trailers.
+     * <br />
+     * This functions acts like a blocking write by default.  To change
+     *         this behavior, use apr_socket_timeout_set() or the
+     *         APR_SO_NONBLOCK socket option.
+     * The number of bytes actually sent is stored in the len parameter.
+     * The offset parameter is passed by reference for no reason; its
+     * value will never be modified by the apr_socket_sendfile() function.
+     * @param sock The socket to which we're writing
+     * @param file The open file from which to read
+     * @param headers Array containing the headers to send
+     * @param trailers Array containing the trailers to send
+     * @param offset Offset into the file where we should begin writing
+     * @param len Number of bytes to send from the file
+     * @param flags APR flags that are mapped to OS specific flags
+     * @return Number of bytes actually sent, including headers,
+     *         file, and trailers
+     *
+     */
+    public static native long sendfile(long sock, long file, byte [][] headers,
+                                       byte[][] trailers, long offset,
+                                       long len, int flags);
+
+    /**
+     * Send a file without header and trailer arrays.
+     */
+    public static native long sendfilen(long sock, long file, long offset,
+                                        long len, int flags);
+
+    /**
+     * Create a child pool from associated socket pool.
+     * @param thesocket The socket to use
+     */
+    public static native long pool(long thesocket)
+        throws Exception;
+
+    /**
+     * Private method for geting the socket struct members
+     * @param socket The soocket to use
+     * @param what Struct member to obtain
+     * <PRE>
+     * SOCKET_GET_POOL  - The socket pool
+     * SOCKET_GET_IMPL  - The socket implementation object
+     * SOCKET_GET_APRS  - APR socket
+     * SOCKET_GET_TYPE  - Socket type
+     * </PRE>
+     * @return The stucture member address
+     */
+    private static native long get(long socket, int what);
+
+    /**
+     * Set internal send ByteBuffer.
+     * This function will preset internal Java ByteBuffer for
+     * consecutive sendbb calls.
+     * @param thesocket The socket to use
+     * @param buf The ByteBuffer
+     */
+    public static native void setsbb(long sock, ByteBuffer buf);
+
+    /**
+     * Set internal receive ByteBuffer.
+     * This function will preset internal Java ByteBuffer for
+     * consecutive revcvbb/recvbbt calls.
+     * @param thesocket The socket to use
+     * @param buf The ByteBuffer
+     */
+    public static native void setrbb(long sock, ByteBuffer buf);
+
+    /**
+     * Set the data associated with the current socket.
+     * @param sock The currently open socket.
+     * @param data The user data to associate with the socket.
+     * @param key The key to associate with the data.
+     * @param cleanup The cleanup to call when the socket is destroyed.
+     */
+      public static native int dataSet(long sock, String key, Object data);
+
+    /**
+     * Return the data associated with the current socket
+     * @param data The user data associated with the socket.
+     * @param key The key to associate with the user data.
+     * @param sock The currently open socket.
+     * @return Data or null in case of error.
+     */
+     public static native Object dataGet(long sock, String key);
+}
diff --git a/connectors/jni/java/org/apache/tomcat/jni/Status.java b/connectors/jni/java/org/apache/tomcat/jni/Status.java
new file mode 100644
index 0000000..9f883ea
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/Status.java
@@ -0,0 +1,265 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+/** Status
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public class Status {
+
+    /**
+     * APR_OS_START_ERROR is where the APR specific error values start.
+     */
+     public static final int APR_OS_START_ERROR   = 20000;
+    /**
+     * APR_OS_ERRSPACE_SIZE is the maximum number of errors you can fit
+     *    into one of the error/status ranges below -- except for
+     *    APR_OS_START_USERERR, which see.
+     */
+     public static final int APR_OS_ERRSPACE_SIZE = 50000;
+    /**
+     * APR_OS_START_STATUS is where the APR specific status codes start.
+     */
+     public static final int APR_OS_START_STATUS  = (APR_OS_START_ERROR + APR_OS_ERRSPACE_SIZE);
+
+    /**
+     * APR_OS_START_USERERR are reserved for applications that use APR that
+     *     layer their own error codes along with APR's.  Note that the
+     *     error immediately following this one is set ten times farther
+     *     away than usual, so that users of apr have a lot of room in
+     *     which to declare custom error codes.
+     */
+    public static final int APR_OS_START_USERERR  = (APR_OS_START_STATUS + APR_OS_ERRSPACE_SIZE);
+    /**
+     * APR_OS_START_USEERR is obsolete, defined for compatibility only.
+     * Use APR_OS_START_USERERR instead.
+     */
+    public static final int APR_OS_START_USEERR    = APR_OS_START_USERERR;
+    /**
+     * APR_OS_START_CANONERR is where APR versions of errno values are defined
+     *     on systems which don't have the corresponding errno.
+     */
+    public static final int APR_OS_START_CANONERR  = (APR_OS_START_USERERR + (APR_OS_ERRSPACE_SIZE * 10));
+
+    /**
+     * APR_OS_START_EAIERR folds EAI_ error codes from getaddrinfo() into
+     *     apr_status_t values.
+     */
+    public static final int APR_OS_START_EAIERR  = (APR_OS_START_CANONERR + APR_OS_ERRSPACE_SIZE);
+    /**
+     * APR_OS_START_SYSERR folds platform-specific system error values into
+     *     apr_status_t values.
+     */
+    public static final int APR_OS_START_SYSERR  = (APR_OS_START_EAIERR + APR_OS_ERRSPACE_SIZE);
+
+    /** no error. */
+    public static final int APR_SUCCESS = 0;
+
+    /**
+     * APR Error Values
+     * <PRE>
+     * <b>APR ERROR VALUES</b>
+     * APR_ENOSTAT      APR was unable to perform a stat on the file
+     * APR_ENOPOOL      APR was not provided a pool with which to allocate memory
+     * APR_EBADDATE     APR was given an invalid date
+     * APR_EINVALSOCK   APR was given an invalid socket
+     * APR_ENOPROC      APR was not given a process structure
+     * APR_ENOTIME      APR was not given a time structure
+     * APR_ENODIR       APR was not given a directory structure
+     * APR_ENOLOCK      APR was not given a lock structure
+     * APR_ENOPOLL      APR was not given a poll structure
+     * APR_ENOSOCKET    APR was not given a socket
+     * APR_ENOTHREAD    APR was not given a thread structure
+     * APR_ENOTHDKEY    APR was not given a thread key structure
+     * APR_ENOSHMAVAIL  There is no more shared memory available
+     * APR_EDSOOPEN     APR was unable to open the dso object.  For more
+     *                  information call apr_dso_error().
+     * APR_EGENERAL     General failure (specific information not available)
+     * APR_EBADIP       The specified IP address is invalid
+     * APR_EBADMASK     The specified netmask is invalid
+     * APR_ESYMNOTFOUND Could not find the requested symbol
+     * </PRE>
+     *
+     */
+    public static final int APR_ENOSTAT       = (APR_OS_START_ERROR + 1);
+    public static final int APR_ENOPOOL       = (APR_OS_START_ERROR + 2);
+    public static final int APR_EBADDATE      = (APR_OS_START_ERROR + 4);
+    public static final int APR_EINVALSOCK    = (APR_OS_START_ERROR + 5);
+    public static final int APR_ENOPROC       = (APR_OS_START_ERROR + 6);
+    public static final int APR_ENOTIME       = (APR_OS_START_ERROR + 7);
+    public static final int APR_ENODIR        = (APR_OS_START_ERROR + 8);
+    public static final int APR_ENOLOCK       = (APR_OS_START_ERROR + 9);
+    public static final int APR_ENOPOLL       = (APR_OS_START_ERROR + 10);
+    public static final int APR_ENOSOCKET     = (APR_OS_START_ERROR + 11);
+    public static final int APR_ENOTHREAD     = (APR_OS_START_ERROR + 12);
+    public static final int APR_ENOTHDKEY     = (APR_OS_START_ERROR + 13);
+    public static final int APR_EGENERAL      = (APR_OS_START_ERROR + 14);
+    public static final int APR_ENOSHMAVAIL   = (APR_OS_START_ERROR + 15);
+    public static final int APR_EBADIP        = (APR_OS_START_ERROR + 16);
+    public static final int APR_EBADMASK      = (APR_OS_START_ERROR + 17);
+    public static final int APR_EDSOOPEN      = (APR_OS_START_ERROR + 19);
+    public static final int APR_EABSOLUTE     = (APR_OS_START_ERROR + 20);
+    public static final int APR_ERELATIVE     = (APR_OS_START_ERROR + 21);
+    public static final int APR_EINCOMPLETE   = (APR_OS_START_ERROR + 22);
+    public static final int APR_EABOVEROOT    = (APR_OS_START_ERROR + 23);
+    public static final int APR_EBADPATH      = (APR_OS_START_ERROR + 24);
+    public static final int APR_EPATHWILD     = (APR_OS_START_ERROR + 25);
+    public static final int APR_ESYMNOTFOUND  = (APR_OS_START_ERROR + 26);
+    public static final int APR_EPROC_UNKNOWN = (APR_OS_START_ERROR + 27);
+    public static final int APR_ENOTENOUGHENTROPY = (APR_OS_START_ERROR + 28);
+
+    /** APR Status Values
+     * <PRE>
+     * <b>APR STATUS VALUES</b>
+     * APR_INCHILD        Program is currently executing in the child
+     * APR_INPARENT       Program is currently executing in the parent
+     * APR_DETACH         The thread is detached
+     * APR_NOTDETACH      The thread is not detached
+     * APR_CHILD_DONE     The child has finished executing
+     * APR_CHILD_NOTDONE  The child has not finished executing
+     * APR_TIMEUP         The operation did not finish before the timeout
+     * APR_INCOMPLETE     The operation was incomplete although some processing
+     *                    was performed and the results are partially valid
+     * APR_BADCH          Getopt found an option not in the option string
+     * APR_BADARG         Getopt found an option that is missing an argument
+     *                    and an argument was specified in the option string
+     * APR_EOF            APR has encountered the end of the file
+     * APR_NOTFOUND       APR was unable to find the socket in the poll structure
+     * APR_ANONYMOUS      APR is using anonymous shared memory
+     * APR_FILEBASED      APR is using a file name as the key to the shared memory
+     * APR_KEYBASED       APR is using a shared key as the key to the shared memory
+     * APR_EINIT          Ininitalizer value.  If no option has been found, but
+     *                    the status variable requires a value, this should be used
+     * APR_ENOTIMPL       The APR function has not been implemented on this
+     *                    platform, either because nobody has gotten to it yet,
+     *                    or the function is impossible on this platform.
+     * APR_EMISMATCH      Two passwords do not match.
+     * APR_EBUSY          The given lock was busy.
+     * </PRE>
+     *
+     */
+    public static final int APR_INCHILD       = (APR_OS_START_STATUS + 1);
+    public static final int APR_INPARENT      = (APR_OS_START_STATUS + 2);
+    public static final int APR_DETACH        = (APR_OS_START_STATUS + 3);
+    public static final int APR_NOTDETACH     = (APR_OS_START_STATUS + 4);
+    public static final int APR_CHILD_DONE    = (APR_OS_START_STATUS + 5);
+    public static final int APR_CHILD_NOTDONE = (APR_OS_START_STATUS + 6);
+    public static final int APR_TIMEUP        = (APR_OS_START_STATUS + 7);
+    public static final int APR_INCOMPLETE    = (APR_OS_START_STATUS + 8);
+    public static final int APR_BADCH         = (APR_OS_START_STATUS + 12);
+    public static final int APR_BADARG        = (APR_OS_START_STATUS + 13);
+    public static final int APR_EOF           = (APR_OS_START_STATUS + 14);
+    public static final int APR_NOTFOUND      = (APR_OS_START_STATUS + 15);
+    public static final int APR_ANONYMOUS     = (APR_OS_START_STATUS + 19);
+    public static final int APR_FILEBASED     = (APR_OS_START_STATUS + 20);
+    public static final int APR_KEYBASED      = (APR_OS_START_STATUS + 21);
+    public static final int APR_EINIT         = (APR_OS_START_STATUS + 22);
+    public static final int APR_ENOTIMPL      = (APR_OS_START_STATUS + 23);
+    public static final int APR_EMISMATCH     = (APR_OS_START_STATUS + 24);
+    public static final int APR_EBUSY         = (APR_OS_START_STATUS + 25);
+
+    public static final int TIMEUP            = (APR_OS_START_USERERR + 1);
+    public static final int EAGAIN            = (APR_OS_START_USERERR + 2);
+    public static final int EINTR             = (APR_OS_START_USERERR + 3);
+    public static final int EINPROGRESS       = (APR_OS_START_USERERR + 4);
+    public static final int ETIMEDOUT         = (APR_OS_START_USERERR + 5);
+
+    private static native boolean is(int err, int idx);
+    /**
+     * APR_STATUS_IS Status Value Tests
+     * <br /><b>Warning :</b> For any particular error condition, more than one of these tests
+     *      may match. This is because platform-specific error codes may not
+     *      always match the semantics of the POSIX codes these tests (and the
+     *      corresponding APR error codes) are named after. A notable example
+     *      are the APR_STATUS_IS_ENOENT and APR_STATUS_IS_ENOTDIR tests on
+     *      Win32 platforms. The programmer should always be aware of this and
+     *      adjust the order of the tests accordingly.
+     *
+     */
+    public static final boolean APR_STATUS_IS_ENOSTAT(int s)    { return is(s, 1); }
+    public static final boolean APR_STATUS_IS_ENOPOOL(int s)    { return is(s, 2); }
+    /* empty slot: +3 */
+    public static final boolean APR_STATUS_IS_EBADDATE(int s)   { return is(s, 4); }
+    public static final boolean APR_STATUS_IS_EINVALSOCK(int s) { return is(s, 5); }
+    public static final boolean APR_STATUS_IS_ENOPROC(int s)    { return is(s, 6); }
+    public static final boolean APR_STATUS_IS_ENOTIME(int s)    { return is(s, 7); }
+    public static final boolean APR_STATUS_IS_ENODIR(int s)     { return is(s, 8); }
+    public static final boolean APR_STATUS_IS_ENOLOCK(int s)    { return is(s, 9); }
+    public static final boolean APR_STATUS_IS_ENOPOLL(int s)    { return is(s, 10); }
+    public static final boolean APR_STATUS_IS_ENOSOCKET(int s)  { return is(s, 11); }
+    public static final boolean APR_STATUS_IS_ENOTHREAD(int s)  { return is(s, 12); }
+    public static final boolean APR_STATUS_IS_ENOTHDKEY(int s)  { return is(s, 13); }
+    public static final boolean APR_STATUS_IS_EGENERAL(int s)   { return is(s, 14); }
+    public static final boolean APR_STATUS_IS_ENOSHMAVAIL(int s){ return is(s, 15); }
+    public static final boolean APR_STATUS_IS_EBADIP(int s)     { return is(s, 16); }
+    public static final boolean APR_STATUS_IS_EBADMASK(int s)   { return is(s, 17); }
+    /* empty slot: +18 */
+    public static final boolean APR_STATUS_IS_EDSOPEN(int s)    { return is(s, 19); }
+    public static final boolean APR_STATUS_IS_EABSOLUTE(int s)  { return is(s, 20); }
+    public static final boolean APR_STATUS_IS_ERELATIVE(int s)  { return is(s, 21); }
+    public static final boolean APR_STATUS_IS_EINCOMPLETE(int s){ return is(s, 22); }
+    public static final boolean APR_STATUS_IS_EABOVEROOT(int s) { return is(s, 23); }
+    public static final boolean APR_STATUS_IS_EBADPATH(int s)   { return is(s, 24); }
+    public static final boolean APR_STATUS_IS_EPATHWILD(int s)  { return is(s, 25); }
+    public static final boolean APR_STATUS_IS_ESYMNOTFOUND(int s)      { return is(s, 26); }
+    public static final boolean APR_STATUS_IS_EPROC_UNKNOWN(int s)     { return is(s, 27); }
+    public static final boolean APR_STATUS_IS_ENOTENOUGHENTROPY(int s) { return is(s, 28); }
+
+    /*
+     * APR_Error
+     */
+    public static final boolean APR_STATUS_IS_INCHILD(int s)    { return is(s, 51); }
+    public static final boolean APR_STATUS_IS_INPARENT(int s)   { return is(s, 52); }
+    public static final boolean APR_STATUS_IS_DETACH(int s)     { return is(s, 53); }
+    public static final boolean APR_STATUS_IS_NOTDETACH(int s)  { return is(s, 54); }
+    public static final boolean APR_STATUS_IS_CHILD_DONE(int s) { return is(s, 55); }
+    public static final boolean APR_STATUS_IS_CHILD_NOTDONE(int s)  { return is(s, 56); }
+    public static final boolean APR_STATUS_IS_TIMEUP(int s)     { return is(s, 57); }
+    public static final boolean APR_STATUS_IS_INCOMPLETE(int s) { return is(s, 58); }
+    /* empty slot: +9 */
+    /* empty slot: +10 */
+    /* empty slot: +11 */
+    public static final boolean APR_STATUS_IS_BADCH(int s)      { return is(s, 62); }
+    public static final boolean APR_STATUS_IS_BADARG(int s)     { return is(s, 63); }
+    public static final boolean APR_STATUS_IS_EOF(int s)        { return is(s, 64); }
+    public static final boolean APR_STATUS_IS_NOTFOUND(int s)   { return is(s, 65); }
+    /* empty slot: +16 */
+    /* empty slot: +17 */
+    /* empty slot: +18 */
+    public static final boolean APR_STATUS_IS_ANONYMOUS(int s)  { return is(s, 69); }
+    public static final boolean APR_STATUS_IS_FILEBASED(int s)  { return is(s, 70); }
+    public static final boolean APR_STATUS_IS_KEYBASED(int s)   { return is(s, 71); }
+    public static final boolean APR_STATUS_IS_EINIT(int s)      { return is(s, 72); }
+    public static final boolean APR_STATUS_IS_ENOTIMPL(int s)   { return is(s, 73); }
+    public static final boolean APR_STATUS_IS_EMISMATCH(int s)  { return is(s, 74); }
+    public static final boolean APR_STATUS_IS_EBUSY(int s)      { return is(s, 75); }
+
+    /* Socket errors */
+    public static final boolean APR_STATUS_IS_EAGAIN(int s)     { return is(s, 90); }
+    public static final boolean APR_STATUS_IS_ETIMEDOUT(int s)  { return is(s, 91); }
+    public static final boolean APR_STATUS_IS_ECONNABORTED(int s) { return is(s, 92); }
+    public static final boolean APR_STATUS_IS_ECONNRESET(int s)   { return is(s, 93); }
+    public static final boolean APR_STATUS_IS_EINPROGRESS(int s)  { return is(s, 94); }
+    public static final boolean APR_STATUS_IS_EINTR(int s)      { return is(s, 95); }
+    public static final boolean APR_STATUS_IS_ENOTSOCK(int s)   { return is(s, 96); }
+    public static final boolean APR_STATUS_IS_EINVAL(int s)     { return is(s, 97); }
+
+}
diff --git a/connectors/jni/java/org/apache/tomcat/jni/Stdlib.java b/connectors/jni/java/org/apache/tomcat/jni/Stdlib.java
new file mode 100644
index 0000000..1be544b
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/Stdlib.java
@@ -0,0 +1,90 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+/** Stdlib
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public class Stdlib {
+
+    /**
+     * Read from plain memory
+     * @param dst Destination byte array
+     * @param src Source memory address
+     * @param sz Number of bytes to copy.
+     */
+    public static native boolean memread(byte [] dst, long src, int sz);
+
+    /**
+     * Write to plain memory
+     * @param dst Destination memory address
+     * @param src Source byte array
+     * @param sz Number of bytes to copy.
+     */
+    public static native boolean memwrite(long dst, byte [] src, int sz);
+
+    /**
+     * Sets buffers to a specified character
+     * @param dst Destination memory address
+     * @param c Character to set.
+     * @param sz Number of characters.
+     */
+    public static native boolean memset(long dst, int c, int sz);
+
+    /**
+     * Allocates memory blocks.
+     * @param sz Bytes to allocate.
+     */
+    public static native long malloc(int sz);
+
+    /**
+     * Reallocate memory blocks.
+     * @param mem Pointer to previously allocated memory block.
+     * @param sz New size in bytes.
+     */
+    public static native long realloc(long mem, int sz);
+
+    /**
+     * Allocates an array in memory with elements initialized to 0.
+     * @param num Number of elements.
+     * @param sz Length in bytes of each element.
+     */
+    public static native long calloc(long num, int sz);
+
+    /**
+     * Deallocates or frees a memory block.
+     * @param mem Previously allocated memory block to be freed.
+     */
+    public static native void free(long mem);
+
+    /**
+     * Get current process pid.
+     * @return current pid or < 1 in case of error.
+     */
+    public static native int getpid();
+
+    /**
+     * Get current process parent pid.
+     * @return parent pid or < 1 in case of error.
+     */
+    public static native int getppid();
+
+}
diff --git a/connectors/jni/java/org/apache/tomcat/jni/Thread.java b/connectors/jni/java/org/apache/tomcat/jni/Thread.java
new file mode 100644
index 0000000..9990348
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/Thread.java
@@ -0,0 +1,33 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+/** Thread
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public class Thread {
+    
+    /**
+     * Get the current thread ID handle.
+     */
+    public static native long current();    
+
+}
diff --git a/connectors/jni/java/org/apache/tomcat/jni/Time.java b/connectors/jni/java/org/apache/tomcat/jni/Time.java
new file mode 100644
index 0000000..dc41535
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/Time.java
@@ -0,0 +1,74 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+/** Time
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public class Time {
+
+    /** number of microseconds per second */
+    public static final long APR_USEC_PER_SEC  = 1000000L;
+    /** number of miliseconds per microsecond */
+    public static final long APR_MSEC_PER_USEC = 1000L;
+
+    /** @return apr_time_t as a second */
+    public static long sec(long t)
+    {
+        return t / APR_USEC_PER_SEC;
+    }
+
+    /** @return apr_time_t as a msec */
+    public static long msec(long t)
+    {
+        return t / APR_MSEC_PER_USEC;
+    }
+
+    /**
+     * number of microseconds since 00:00:00 january 1, 1970 UTC
+     * @return the current time
+     */
+    public static native long now();
+
+    /**
+     * Formats dates in the RFC822
+     * format in an efficient manner.
+     * @param t the time to convert
+     */
+    public static native String rfc822(long t);
+
+    /**
+     * Formats dates in the ctime() format
+     * in an efficient manner.
+     * Unlike ANSI/ISO C ctime(), apr_ctime() does not include
+     * a \n at the end of the string.
+     * @param t the time to convert
+     */
+    public static native String ctime(long t);
+
+    /**
+     * Sleep for the specified number of micro-seconds.
+     * <br /><b>Warning :</b> May sleep for longer than the specified time.
+     * @param t desired amount of time to sleep.
+     */
+    public static native void sleep(long t);
+
+}
diff --git a/connectors/jni/java/org/apache/tomcat/jni/User.java b/connectors/jni/java/org/apache/tomcat/jni/User.java
new file mode 100644
index 0000000..69723f9
--- /dev/null
+++ b/connectors/jni/java/org/apache/tomcat/jni/User.java
@@ -0,0 +1,127 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+/** User
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+public class User {
+
+    /**
+     * Get the userid (and groupid) of the calling process
+     * This function is available only if APR_HAS_USER is defined.
+     * @param p The pool from which to allocate working space
+     * @return Returns the user id
+     */
+     public static native long uidCurrent(long p)
+        throws Error;
+
+    /**
+     * Get the groupid of the calling process
+     * This function is available only if APR_HAS_USER is defined.
+     * @param p The pool from which to allocate working space
+     * @return Returns the group id
+     */
+     public static native long gidCurrent(long p)
+        throws Error;
+
+
+    /**
+     * Get the userid for the specified username
+     * This function is available only if APR_HAS_USER is defined.
+     * @param username The username to lookup
+     * @param p The pool from which to allocate working space
+     * @return Returns the user id
+     */
+     public static native long uid(String username, long p)
+        throws Error;
+
+    /**
+     * Get the groupid for the specified username
+     * This function is available only if APR_HAS_USER is defined.
+     * @param username The username to lookup
+     * @param p The pool from which to allocate working space
+     * @return  Returns the user's group id
+     */
+     public static native long usergid(String username, long p)
+        throws Error;
+
+    /**
+     * Get the groupid for a specified group name
+     * This function is available only if APR_HAS_USER is defined.
+     * @param groupname The group name to look up
+     * @param p The pool from which to allocate working space
+     * @return  Returns the user's group id
+     */
+     public static native long gid(String groupname, long p)
+        throws Error;
+
+    /**
+     * Get the user name for a specified userid
+     * This function is available only if APR_HAS_USER is defined.
+     * @param userid The userid
+     * @param p The pool from which to allocate the string
+     * @return New string containing user name
+     */
+     public static native String username(long userid, long p)
+        throws Error;
+
+    /**
+     * Get the group name for a specified groupid
+     * This function is available only if APR_HAS_USER is defined.
+     * @param groupid The groupid
+     * @param p The pool from which to allocate the string
+     * @return New string containing group name
+     */
+     public static native String groupname(long groupid, long p)
+        throws Error;
+
+    /**
+     * Compare two user identifiers for equality.
+     * This function is available only if APR_HAS_USER is defined.
+     * @param left One uid to test
+     * @param right Another uid to test
+     * @return APR_SUCCESS if the apr_uid_t strutures identify the same user,
+     * APR_EMISMATCH if not, APR_BADARG if an apr_uid_t is invalid.
+     */
+     public static native int uidcompare(long left, long right);
+
+    /**
+     * Compare two group identifiers for equality.
+     * This function is available only if APR_HAS_USER is defined.
+     * @param left One gid to test
+     * @param right Another gid to test
+     * @return APR_SUCCESS if the apr_gid_t strutures identify the same group,
+     * APR_EMISMATCH if not, APR_BADARG if an apr_gid_t is invalid.
+     */
+     public static native int gidcompare(long left, long right);
+
+    /**
+     * Get the home directory for the named user
+     * This function is available only if APR_HAS_USER is defined.
+     * @param username The named user
+     * @param p The pool from which to allocate the string
+     * @return New string containing directory name
+     */
+     public static native String homepath(String username, long p)
+        throws Error;
+
+}
diff --git a/connectors/jni/java/overview.html b/connectors/jni/java/overview.html
new file mode 100644
index 0000000..af9d5db
--- /dev/null
+++ b/connectors/jni/java/overview.html
@@ -0,0 +1,30 @@
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+
+<html>
+<head>
+<title>Overview Documentation for Tomcat Native</title>
+</head>
+<body bgcolor="white">
+<p>The <em>Tomcat Native Library</em> component of the Jakarta Tomcat
+Connectors project offers JNI wrappers around APR and various other
+system libraries.</p>
+
+</body>
+</html>
diff --git a/connectors/jni/jnirelease.sh b/connectors/jni/jnirelease.sh
new file mode 100755
index 0000000..e6aac7e
--- /dev/null
+++ b/connectors/jni/jnirelease.sh
@@ -0,0 +1,85 @@
+#/bin/sh
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Default place to look for apr source.  Can be overridden with 
+#   --with-apr=[directory]
+apr_src_dir=`pwd`/srclib/apr-1.2.8
+
+while test $# -gt 0 
+do
+  # Normalize
+  case "$1" in
+  -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+  *) optarg= ;;
+  esac
+
+  case "$1" in
+  --with-apr=*)
+  apr_src_dir=$optarg
+  ;;
+  esac
+
+  shift
+done
+
+if test -d "$apr_src_dir"
+then
+  echo ""
+  echo "Looking for apr source in $apr_src_dir"
+else
+  echo ""
+  echo "Problem finding apr source in $apr_src_dir."
+  echo "Use:"
+  echo "  --with-apr=[directory]" 
+  exit 1
+fi
+
+# Replace JKJNIEXT with branch/or tag
+# and JKJNIVER by the version like 1.1.0
+JKJNIEXT="trunk"
+JKJNIVER="current"
+# JKJNIEXT="tags/other/TOMCAT_NATIVE_1_1_10"
+# JKJNIVER="1.1.10"
+SVNBASE=https://svn.apache.org/repos/asf/tomcat/connectors/
+JKJNIDIST=tomcat-native-${JKJNIVER}-src
+rm -rf ${JKJNIDIST}
+mkdir -p ${JKJNIDIST}/jni
+svn export $SVNBASE/${JKJNIEXT}/jni/native ${JKJNIDIST}/jni/native
+svn cat $SVNBASE/${JKJNIEXT}/KEYS > ${JKJNIDIST}/KEYS
+svn cat $SVNBASE/${JKJNIEXT}/LICENSE > ${JKJNIDIST}/LICENSE
+svn cat $SVNBASE/${JKJNIEXT}/NOTICE > ${JKJNIDIST}/NOTICE
+svn cat $SVNBASE/${JKJNIEXT}/jni/NOTICE.txt > ${JKJNIDIST}/NOTICE.txt
+svn cat $SVNBASE/${JKJNIEXT}/jni/README.txt > ${JKJNIDIST}/README.txt
+#
+# Prebuild
+cd ${JKJNIDIST}/jni/native
+./buildconf --with-apr=$apr_src_dir
+cd ../../../
+# Create source distribution
+tar cfz ${JKJNIDIST}.tar.gz ${JKJNIDIST}
+#
+# Create Win32 source distribution
+JKJNIDIST=tomcat-native-${JKJNIVER}-win32-src
+rm -rf ${JKJNIDIST}
+mkdir -p ${JKJNIDIST}/jni
+svn export --native-eol CRLF $SVNBASE/${JKJNIEXT}/jni/native ${JKJNIDIST}/jni/native
+svn cat $SVNBASE/${JKJNIEXT}/KEYS > ${JKJNIDIST}/KEYS
+svn cat $SVNBASE/${JKJNIEXT}/LICENSE > ${JKJNIDIST}/LICENSE
+svn cat $SVNBASE/${JKJNIEXT}/NOTICE > ${JKJNIDIST}/NOTICE
+svn cat $SVNBASE/${JKJNIEXT}/jni/NOTICE.txt > ${JKJNIDIST}/NOTICE.txt
+svn cat $SVNBASE/${JKJNIEXT}/jni/README.txt > ${JKJNIDIST}/README.txt
+zip -9rqo ${JKJNIDIST}.zip ${JKJNIDIST}
diff --git a/connectors/jni/native/BUILDING b/connectors/jni/native/BUILDING
new file mode 100644
index 0000000..7f9e7b3
--- /dev/null
+++ b/connectors/jni/native/BUILDING
@@ -0,0 +1,30 @@
+  Building from the cvs tree:
+  sh buildconf --with-apr=apr_source_location.
+  configure --with-apr=apr_install_location --with-ssl=openssl_install_location
+  make
+
+  Testing the build:
+  The make should produce a .so file named libtcnative-1.so.
+  Build the jar containing the example by
+  cd ..
+  ant jar
+  Run the example:
+  ant example-basic
+ 
+  Using it in Tomcat:
+  1 - In <Connector> use of conf/server.xml:
+    protocol="org.apache.coyote.http11.Http11AprProtocol"
+  2 - In bin/setenv.sh add the following: 
+    CATALINA_OPTS="$CATALINA_OPTS -Djava.library.path=tclib_location"
+    In my machine I am using:
+    /home/jfclere/tomcat-connectors/jni/native/.libs for tclib_location
+
+NOTES:
+  - configure --without-ssl : Configure without ssl support.
+  - To use it in Tomcat you may have to add in bin/setenv.sh:
+    LD_LIBRARY_PATH=openssl_install_location/lib; export LD_LIBRARY_PATH
+    (use ldd ./.libs/libtcnative-1.so to check it).
+  - quick testing: openssl s_client -connect localhost:8443
+  - For MAC OS X you must manually add a link
+    cd ${tcnative installdir}
+    ln -d libtcnative-1.dylib libtcnative-1.jnilib
diff --git a/connectors/jni/native/Makefile.in b/connectors/jni/native/Makefile.in
new file mode 100644
index 0000000..41e92ae
--- /dev/null
+++ b/connectors/jni/native/Makefile.in
@@ -0,0 +1,65 @@
+#
+# Top-level Makefile for TCNATIVE
+#
+
+CFLAGS = @CFLAGS@
+CPPFLAGS = @CPPFLAGS@
+
+# gets substituted into some targets
+TCNATIVE_MAJOR_VERSION=@TCNATIVE_MAJOR_VERSION@
+TCNATIVE_DOTTED_VERSION=@TCNATIVE_DOTTED_VERSION@
+TCNATIVE_LIBTOOL_VERSION=@TCNATIVE_LIBTOOL_VERSION@
+
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+# this sucks, but it's the only way to add extra flags to the LT_COMPILE
+INCLUDES = @CFLAGS@ @CPPFLAGS@ @TCNATIVE_INCLUDES@ @TCNATIVE_PRIV_INCLUDES@ @APR_INCLUDES@
+TCNATIVE_LDFLAGS = @TCNATIVE_LDFLAGS@
+TCNATIVE_LIBS = @TCNATIVE_LIBS@
+
+TARGET_LIB = lib@TCNATIVE_LIBNAME@.la
+EXTRA_OS_LINK=@EXTRA_OS_LINK@
+TCNATIVE_PCFILE = tcnative-$(TCNATIVE_MAJOR_VERSION).pc
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+
+TARGETS = $(TARGET_LIB)
+
+# bring in rules.mk for standard functionality
+@INCLUDE_RULES@
+@INCLUDE_OUTPUTS@
+
+LINK          = $(LIBTOOL) $(LTFLAGS) --mode=link $(LT_LDFLAGS) $(COMPILE) -version-info $(TCNATIVE_LIBTOOL_VERSION) $(ALL_LDFLAGS) -o $@
+CLEAN_SUBDIRS = test
+
+CLEAN_TARGETS = .make.dirs
+DISTCLEAN_TARGETS = config.cache config.log config.status libtool \
+	build/rules.mk tcnative.pc
+EXTRACLEAN_TARGETS = configure aclocal.m4 build-outputs.mk \
+	build/apr_common.m4 build/find_apr.m4 build/install.sh \
+	build/config.guess build/config.sub tcnative.spec
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+bindir=@bindir@
+libdir=@libdir@
+includedir=@includedir@
+top_srcdir=@abs_srcdir@
+top_blddir=@abs_builddir@
+
+
+install: $(TARGET_LIB)
+	$(APR_MKDIR) $(DESTDIR)$(includedir) $(DESTDIR)$(libdir)/pkgconfig \
+		     $(DESTDIR)$(libdir) $(DESTDIR)$(bindir)
+	$(INSTALL_DATA) tcnative.pc $(DESTDIR)$(libdir)/pkgconfig/$(TCNATIVE_PCFILE)
+	list='$(INSTALL_SUBDIRS)'; for i in $$list; do \
+		( cd $$i ; $(MAKE) DESTDIR=$(DESTDIR) install ); \
+	done
+	$(LIBTOOL) --mode=install $(INSTALL) -m 755 $(TARGET_LIB) $(DESTDIR)$(libdir)
+
+$(TARGET_LIB): $(OBJECTS)
+	$(LINK) @lib_target@ @TCNATIVE_LDFLAGS@ @TCNATIVE_LIBS@
+
+check: $(TARGET_LIB)
+	(cd test && $(MAKE) check)
diff --git a/connectors/jni/native/build.conf b/connectors/jni/native/build.conf
new file mode 100644
index 0000000..900ec4f
--- /dev/null
+++ b/connectors/jni/native/build.conf
@@ -0,0 +1,19 @@
+#
+# Configuration file for APRJAVA. Used by APR/build/gen-build.py
+#
+
+[options]
+
+# the platform-independent .c files
+paths =
+  src/*.c
+
+# we have no platform-specific subdirs
+platform_dirs =
+  os
+
+# the public headers
+headers = include/*.h
+
+# we have a recursive makefile for the test files (for now)
+# test/*.c
diff --git a/connectors/jni/native/build/buildcheck.sh b/connectors/jni/native/build/buildcheck.sh
new file mode 100755
index 0000000..984caf1
--- /dev/null
+++ b/connectors/jni/native/build/buildcheck.sh
@@ -0,0 +1,66 @@
+#! /bin/sh
+
+echo "buildconf: checking installation..."
+
+# any python
+python=`build/PrintPath python`
+if test -z "$python"; then
+echo "buildconf: python not found."
+echo "           You need python installed"
+echo "           to build Tomcat Native from SVN."
+exit 1
+else
+py_version=`python -c 'import sys; print sys.version' 2>&1|sed 's/ .*//;q'`
+echo "buildconf: python version $py_version (ok)"
+fi
+
+# autoconf 2.50 or newer
+ac_version=`${AUTOCONF:-autoconf} --version 2>/dev/null|sed -e 's/^[^0-9]*//;s/[a-z]* *$//;q'`
+if test -z "$ac_version"; then
+echo "buildconf: autoconf not found."
+echo "           You need autoconf version 2.50 or newer installed"
+echo "           to build Tomcat Native from SVN."
+exit 1
+fi
+IFS=.; set $ac_version; IFS=' '
+if test "$1" = "2" -a "$2" -lt "50" || test "$1" -lt "2"; then
+echo "buildconf: autoconf version $ac_version found."
+echo "           You need autoconf version 2.50 or newer installed"
+echo "           to build Tomcat Native from SVN."
+exit 1
+else
+echo "buildconf: autoconf version $ac_version (ok)"
+fi
+
+# Sample libtool --version outputs:
+# ltmain.sh (GNU libtool) 1.3.3 (1.385.2.181 1999/07/02 15:49:11)
+# ltmain.sh (GNU libtool 1.1361 2004/01/02 23:10:52) 1.5a
+# output is multiline from 1.5 onwards
+
+# Require libtool 1.4 or newer
+libtool=`build/PrintPath glibtool libtool libtool15 libtool14`
+lt_pversion=`$libtool --version 2>/dev/null|sed -e 's/([^)]*)//g;s/^[^0-9]*//;s/[- ].*//g;q'`
+if test -z "$lt_pversion"; then
+echo "buildconf: libtool not found."
+echo "           You need libtool version 1.4 or newer installed"
+echo "           to build Tomcat Native from SVN."
+exit 1
+fi
+lt_version=`echo $lt_pversion|sed -e 's/\([a-z]*\)$/.\1/'`
+IFS=.; set $lt_version; IFS=' '
+lt_status="good"
+if test "$1" = "1"; then
+   if test "$2" -lt "4"; then
+      lt_status="bad"
+   fi
+fi
+if test $lt_status = "good"; then
+   echo "buildconf: libtool version $lt_pversion (ok)"
+   exit 0
+fi
+
+echo "buildconf: libtool version $lt_pversion found."
+echo "           You need libtool version 1.4 or newer installed"
+echo "           to build Tomcat Native from SVN."
+
+exit 1
diff --git a/connectors/jni/native/build/get-version.sh b/connectors/jni/native/build/get-version.sh
new file mode 100755
index 0000000..fd685b2
--- /dev/null
+++ b/connectors/jni/native/build/get-version.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+#
+# extract version numbers from a header file
+#
+# USAGE: get-version.sh CMD VERSION_HEADER PREFIX
+#   where CMD is one of: all, major, libtool
+#   where PREFIX is the prefix to {MAJOR|MINOR|PATCH}_VERSION defines
+#
+#   get-version.sh all returns a dotted version number
+#   get-version.sh major returns just the major version number
+#   get-version.sh libtool returns a version "libtool -version-info" format
+#
+
+if test $# != 3; then
+  echo "USAGE: $0 CMD VERSION_HEADER PREFIX"
+  echo "  where CMD is one of: all, major, libtool"
+  exit 1
+fi
+
+major_sed="/#define.*$3_MAJOR_VERSION/s/^[^0-9]*\([0-9]*\).*$/\1/p"
+minor_sed="/#define.*$3_MINOR_VERSION/s/^[^0-9]*\([0-9]*\).*$/\1/p"
+patch_sed="/#define.*$3_PATCH_VERSION/s/^[^0-9]*\([0-9]*\).*$/\1/p"
+major="`sed -n $major_sed $2`"
+minor="`sed -n $minor_sed $2`"
+patch="`sed -n $patch_sed $2`"
+
+if test "$1" = "all"; then
+  echo ${major}.${minor}.${patch}
+elif test "$1" = "major"; then
+  echo ${major}
+elif test "$1" = "libtool"; then
+  # Yes, ${minor}:${patch}:${minor} is correct due to libtool idiocy.
+  echo ${minor}:${patch}:${minor}
+else
+  echo "ERROR: unknown version CMD ($1)"
+  exit 1
+fi
diff --git a/connectors/jni/native/build/lineends.pl b/connectors/jni/native/build/lineends.pl
new file mode 100644
index 0000000..ca3cec3
--- /dev/null
+++ b/connectors/jni/native/build/lineends.pl
@@ -0,0 +1,149 @@
+#!/usr/local/bin/perl
+#
+#  Heuristically converts line endings to the current OS's preferred format
+#  
+#  All existing line endings must be identical (e.g. lf's only, or even
+#  the accedental cr.cr.lf sequence.)  If some lines end lf, and others as
+#  cr.lf, the file is presumed binary.  If the cr character appears anywhere
+#  except prefixed to an lf, the file is presumed binary.  If there is no 
+#  change in the resulting file size, or the file is binary, the conversion 
+#  is discarded.
+#  
+#  Todo: Handle NULL stdin characters gracefully.
+#
+
+use IO::File;
+use File::Find;
+
+# The ignore list is '-' seperated, with this leading hyphen and
+# trailing hyphens in ever concatinated list below.
+$ignore = "-";
+
+# Image formats
+$ignore .= "gif-jpg-jpeg-png-ico-bmp-";
+
+# Archive formats
+$ignore .= "tar-gz-z-zip-jar-war-bz2-tgz-";
+
+# Many document formats
+$ignore .= "eps-psd-pdf-ai-";
+
+# Some encodings
+$ignore .= "ucs2-ucs4-";
+
+# Some binary objects
+$ignore .= "class-so-dll-exe-obj-a-o-lo-slo-sl-dylib-";
+
+# Some build env files
+$ignore .= "mcp-xdc-ncb-opt-pdb-ilk-sbr-";
+
+$preservedate = 1;
+
+$forceending = 0;
+
+$givenpaths = 0;
+
+$notnative = 0;
+
+while (defined @ARGV[0]) {
+    if (@ARGV[0] eq '--touch') {
+        $preservedate = 0;
+    }
+    elsif (@ARGV[0] eq '--nocr') {
+        $notnative = -1;
+    }
+    elsif (@ARGV[0] eq '--cr') {
+        $notnative = 1;
+    }
+    elsif (@ARGV[0] eq '--force') {
+        $forceending = 1;
+    }
+    elsif (@ARGV[0] eq '--FORCE') {
+        $forceending = 2;
+    }
+    elsif (@ARGV[0] =~ m/^-/) {
+        die "What is " . @ARGV[0] . " supposed to mean?\n\n" 
+	  . "Syntax:\t$0 [option()s] [path(s)]\n\n" . <<'OUTCH'
+Where:	paths specifies the top level directory to convert (default of '.')
+	options are;
+
+	  --cr     keep/add one ^M
+	  --nocr   remove ^M's
+	  --touch  the datestamp (default: keeps date/attribs)
+	  --force  mismatched corrections (unbalanced ^M's)
+	  --FORCE  all files regardless of file name!
+
+OUTCH
+    }
+    else {
+        find(\&totxt, @ARGV[0]);
+	print "scanned " . @ARGV[0] . "\n";
+	$givenpaths = 1;
+    }
+    shift @ARGV;
+}
+
+if (!$givenpaths) {
+    find(\&totxt, '.');
+    print "did .\n";
+}
+
+sub totxt {
+        $oname = $_;
+	$tname = '.#' . $_;
+        if (!-f) {
+            return;
+        }
+	@exts = split /\./;
+	if ($forceending < 2) {
+            while ($#exts && ($ext = pop(@exts))) {
+                if ($ignore =~ m|-$ext-|i) {
+                    return;
+                }
+	    }
+        }
+	@ostat = stat($oname);
+        $srcfl = new IO::File $oname, "r" or die;
+	$dstfl = new IO::File $tname, "w" or die;
+        binmode $srcfl; 
+	if ($notnative) {
+            binmode $dstfl;
+	} 
+	undef $t;
+        while (<$srcfl>) { 
+            if (s/(\r*)\n$/\n/) {
+		$n = length $1;
+		if (!defined $t) { 
+		    $t = $n; 
+		}
+		if (!$forceending && (($n != $t) || m/\r/)) {
+		    print "mismatch in " .$oname. ":" .$n. " expected " .$t. "\n";
+		    undef $t;
+		    last;
+		}
+	        elsif ($notnative > 0) {
+                    s/\n$/\r\n/; 
+                }
+            }
+	    print $dstfl $_; 
+	}
+	if (defined $t && (tell $srcfl == tell $dstfl)) {
+	    undef $t;
+	}
+	undef $srcfl;
+	undef $dstfl;
+	if (defined $t) {
+            unlink $oname or die;
+            rename $tname, $oname or die;
+            @anames = ($oname);
+            if ($preservedate) {
+                utime $ostat[9], $ostat[9], @anames;
+            }
+            chmod $ostat[2] & 07777, @anames;
+            chown $ostat[5], $ostat[6], @anames;
+            print "Converted file " . $oname . " to text in " . $File::Find::dir . "\n"; 
+	}
+	else {
+	    unlink $tname or die;
+	}
+}
diff --git a/connectors/jni/native/build/mkdir.sh b/connectors/jni/native/build/mkdir.sh
new file mode 100755
index 0000000..b947c92
--- /dev/null
+++ b/connectors/jni/native/build/mkdir.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+## 
+##  mkdir.sh -- make directory hierarchy
+##
+##  Based on `mkinstalldirs' from Noah Friedman <friedman@prep.ai.mit.edu>
+##  as of 1994-03-25, which was placed in the Public Domain.
+##  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
+
+
+umask 022
+errstatus=0
+for file in ${1+"$@"} ; do 
+    set fnord `echo ":$file" |\
+               sed -e 's/^:\//%/' -e 's/^://' -e 's/\// /g' -e 's/^%/\//'`
+    shift
+    pathcomp=
+    for d in ${1+"$@"}; do
+        pathcomp="$pathcomp$d"
+        case "$pathcomp" in
+            -* ) pathcomp=./$pathcomp ;;
+            ?: ) pathcomp="$pathcomp/" 
+                 continue ;;
+        esac
+        if test ! -d "$pathcomp"; then
+            echo "mkdir $pathcomp" 1>&2
+            mkdir "$pathcomp" || errstatus=$?
+        fi
+        pathcomp="$pathcomp/"
+    done
+done
+exit $errstatus
+
diff --git a/connectors/jni/native/build/rpm/tcnative.spec.in b/connectors/jni/native/build/rpm/tcnative.spec.in
new file mode 100644
index 0000000..26969e8
--- /dev/null
+++ b/connectors/jni/native/build/rpm/tcnative.spec.in
@@ -0,0 +1,78 @@
+
+%define tcnver 1
+
+Summary: Tomcat Native Java library
+Name: tcnative
+Version: TCN_VERSION
+Release: TCN_RELEASE
+License: Apache Software License
+Group: System Environment/Libraries
+URL: http://apr.apache.org/
+Source0: %{name}-%{version}.tar.gz
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot
+BuildPrereq: autoconf, libtool, doxygen, apr-devel >= 0:{version}-{release}, openssl >= 0.9.7
+
+%description
+The mission of the Tomcat Native Library (TCN) is to provide a
+free library of C data structures and routines.  This library
+contains additional utility interfaces for Java.
+
+%package devel
+Group: Development/Libraries
+Summary: Tomcat Native development kit
+Requires: tcnative = %{version}-%{release}, apr-devel >= 0:{version}-{release}, openssl-devel >= 0.9.7
+
+%description devel
+The mission of the Tomcat Native Library (TCN) is to provide a
+free library of C data structures and routines.  This library
+contains additional utility interfaces for Java.
+
+%prep
+%setup -q
+
+%build
+%configure --with-apr=%{_prefix} \
+        --includedir=%{_includedir}/apr-%{tcnver}
+make %{?_smp_mflags} && make dox
+
+%check
+# Run non-interactive tests
+pushd test
+make %{?_smp_mflags} testall CFLAGS=-fno-strict-aliasing
+./testall -v || exit 1
+popd
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make install DESTDIR=$RPM_BUILD_ROOT
+
+# Documentation
+mv docs/dox/html html
+
+# Unpackaged files
+rm -f $RPM_BUILD_ROOT%{_libdir}/tcnative.exp
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post -p /sbin/ldconfig
+
+%postun -p /sbin/ldconfig
+
+%files
+%defattr(-,root,root,-)
+%doc CHANGES LICENSE NOTICE
+%{_libdir}/libtcnative-%{tcnver}.so.*
+
+%files devel
+%defattr(-,root,root,-)
+%{_libdir}/libtcnative-%{tcnver}.*a
+%{_libdir}/libtcnative-%{tcnver}.so
+%{_libdir}/pkgconfig/tcnative-%{tcnver}.pc
+%{_includedir}/apr-%{tcnver}/*.h
+%doc --parents html
+
+%changelog
+* Tue Jun 22 2004 Mladen Turk <mturk@jboss.com> 1.0.0-1
+- update to support v1.0.2 of APR
+                                                                                
diff --git a/connectors/jni/native/build/tcnative.m4 b/connectors/jni/native/build/tcnative.m4
new file mode 100644
index 0000000..d18a41b
--- /dev/null
+++ b/connectors/jni/native/build/tcnative.m4
@@ -0,0 +1,371 @@
+dnl
+dnl TCN_FIND_APR: figure out where APR is located
+dnl
+AC_DEFUN(TCN_FIND_APR,[
+
+  dnl use the find_apr.m4 script to locate APR. sets apr_found and apr_config
+  APR_FIND_APR(,,,[1])
+  if test "$apr_found" = "no"; then
+    AC_MSG_ERROR(APR could not be located. Please use the --with-apr option.)
+  fi
+
+  sapr_pversion="`$apr_config --version`"
+  if test -z "$sapr_pversion"; then
+    AC_MSG_ERROR(APR config could not be located. Please use the --with-apr option.)
+  fi
+  sapr_version="`echo $sapr_pversion|sed -e 's/\([a-z]*\)$/.\1/'`"
+  IFS=.; set $sapr_version; IFS=' '
+  if test "${1}" -lt "1"; then
+    AC_MSG_ERROR(You need APR version 1.2.1 or newer installed.)
+  else
+    if test "${2}" -lt "2"; then
+      AC_MSG_ERROR(You need APR version 1.2.1 or newer installed.)
+    fi
+  fi
+
+  APR_BUILD_DIR="`$apr_config --installbuilddir`"
+
+  dnl make APR_BUILD_DIR an absolute directory (we'll need it in the
+  dnl sub-projects in some cases)
+  APR_BUILD_DIR="`cd $APR_BUILD_DIR && pwd`"
+
+  APR_INCLUDES="`$apr_config --includes`"
+  APR_LIBS="`$apr_config --link-libtool --libs`"
+  APR_SO_EXT="`$apr_config --apr-so-ext`"
+  APR_LIB_TARGET="`$apr_config --apr-lib-target`"
+
+  AC_SUBST(APR_INCLUDES)
+  AC_SUBST(APR_LIBS)
+  AC_SUBST(APR_BUILD_DIR)
+])
+
+dnl --------------------------------------------------------------------------
+dnl TCN_JDK
+dnl
+dnl Detection of JDK location and Java Platform (1.2, 1.3, 1.4, 1.5, 1.6)
+dnl result goes in JAVA_HOME / JAVA_PLATFORM (2 -> 1.2 and higher)
+dnl 
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [TCN_FIND_JDK],
+  [
+    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 6 5 4 3 2 ; 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
+  ])
+
+
+AC_DEFUN(
+  [TCN_FIND_JDK_OS],
+  [
+    tempval=""
+    JAVA_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
+
+        JAVA_OS = ${tempval}
+      ],
+      [   
+        AC_MSG_CHECKING(os_type directory)
+        JAVA_OS=NONE
+        if test -f ${JAVA_HOME}/${JAVA_INC}/jni_md.h; then
+          JAVA_OS=""
+        else
+          for f in ${JAVA_HOME}/${JAVA_INC}/*/jni_md.h; do
+            if test -f $f; then
+              JAVA_OS=`dirname ${f}`
+              JAVA_OS=`basename ${JAVA_OS}`
+              echo " ${JAVA_OS}"
+            fi
+          done
+          if test "${JAVA_OS}" = "NONE"; 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
+      ])
+  ])
+
+dnl check for sableVM
+dnl (copied from daemon/src/native/unix/support/apjava.m4)
+AC_DEFUN(
+  [TCN_SABLEVM],
+  [
+  if test x"$JAVA_HOME" != x
+  then
+    AC_PATH_PROG(SABLEVM,sablevm,NONE,$JAVA_HOME/bin)
+    if test "$SABLEVM" != "NONE"
+    then
+      AC_MSG_RESULT([Using sableVM: $SABLEVM])
+      CFLAGS="$CFLAGS -DHAVE_SABLEVM"
+    fi
+  fi
+  ])
+
+dnl TCN_HELP_STRING(LHS, RHS)
+dnl Autoconf 2.50 can not handle substr correctly.  It does have 
+dnl AC_HELP_STRING, so let's try to call it if we can.
+dnl Note: this define must be on one line so that it can be properly returned
+dnl as the help string.
+AC_DEFUN(TCN_HELP_STRING,[ifelse(regexp(AC_ACVERSION, 2\.1), -1, AC_HELP_STRING($1,$2),[  ]$1 substr([                       ],len($1))$2)])dnl
+
+dnl
+dnl TCN_CHECK_SSL_TOOLKIT
+dnl
+dnl Configure for the detected openssl toolkit installation, giving
+dnl preference to "--with-ssl=<path>" if it was specified.
+dnl
+AC_DEFUN(TCN_CHECK_SSL_TOOLKIT,[
+OPENSSL_WARNING=
+AC_MSG_CHECKING(for OpenSSL library)
+AC_ARG_WITH(ssl,
+[  --with-ssl[=PATH]   Build with OpenSSL [yes|no|path]],
+    use_openssl="$withval", use_openssl="auto")
+
+openssldirs="/usr /usr/local /usr/local/ssl /usr/pkg /usr/sfw"
+if test "$use_openssl" = "auto"
+then
+    for d in $openssldirs
+    do
+        if test -f $d/include/openssl/opensslv.h
+        then
+            use_openssl=$d
+            break
+        fi
+    done
+fi
+case "$use_openssl" in
+    no)
+        AC_MSG_RESULT(no)
+        TCN_OPENSSL_INC=""
+        USE_OPENSSL=""
+        ;;
+    auto)
+        TCN_OPENSSL_INC=""
+        USE_OPENSSL=""
+        AC_MSG_RESULT(not found)
+        ;;
+    *)
+        if test "$use_openssl" = "yes"
+        then
+            # User did not specify a path - guess it
+            for d in $openssldirs
+            do
+                if test -f $d/include/openssl/opensslv.h
+                then
+                    use_openssl=$d
+                    break
+                fi
+            done
+            if test "$use_openssl" = "yes"
+            then
+                AC_MSG_RESULT(not found)
+                AC_MSG_ERROR(
+[OpenSSL was not found in any of $openssldirs; use --with-ssl=/path])
+            fi
+        fi
+        USE_OPENSSL='-DOPENSSL'
+
+        if test "$use_openssl" = "/usr"
+        then
+            TCN_OPENSSL_INC=""
+            TCN_OPENSSL_LIBS="-lssl -lcrypto"
+        else
+            TCN_OPENSSL_INC="-I$use_openssl/include"
+            case $host in
+            *-solaris*)
+                TCN_OPENSSL_LIBS="-L$use_openssl/lib -R$use_openssl/lib -lssl -lcrypto"
+                ;;
+            *-hp-hpux*)
+                TCN_OPENSSL_LIBS="-L$use_openssl/lib -Wl,+b: -lssl -lcrypto"
+                ;;
+            *linux*)
+                TCN_OPENSSL_LIBS="-L$use_openssl/lib --Wl,-rpath,$use_openssl/lib -lssl -lcrypto"
+                ;;
+            *)
+                TCN_OPENSSL_LIBS="-L$use_openssl/lib -lssl -lcrypto"
+                ;;
+            esac
+        fi
+        AC_MSG_RESULT(using openssl from $use_openssl/lib and $use_openssl/include)
+
+        saved_cflags="$CFLAGS"
+        saved_libs="$LIBS"
+        CFLAGS="$CFLAGS $TCN_OPENSSL_INC"
+        LIBS="$LIBS $TCN_OPENSSL_LIBS"
+         
+AC_ARG_ENABLE(openssl-version-check,
+[AC_HELP_STRING([--enable-openssl-version-check],
+        [Check OpenSSL Version @<:@default=yes@:>@])])
+case "$enable_openssl_version_check" in
+yes|'')
+        AC_MSG_CHECKING(OpenSSL library version)
+        AC_TRY_RUN([
+#include <stdio.h>
+#include <openssl/opensslv.h>
+int main() {
+        if ((OPENSSL_VERSION_NUMBER >= 0x0090701fL &&
+         OPENSSL_VERSION_NUMBER < 0x00908000L) ||
+         OPENSSL_VERSION_NUMBER >= 0x0090802fL)
+            return (0);
+    printf("\n\nFound   OPENSSL_VERSION_NUMBER %#010x\n",
+        OPENSSL_VERSION_NUMBER);
+    printf("Require OPENSSL_VERSION_NUMBER 0x0090701f or greater (0.9.7a)\n"
+           "Require OPENSSL_VERSION_NUMBER 0x0090802f or greater (0.9.8a)\n\n");
+        return (1);
+}
+        ],
+        [AC_MSG_RESULT(ok)],
+        [AC_MSG_RESULT(not compatible)
+            OPENSSL_WARNING=yes
+        ],
+        [AC_MSG_RESULT(assuming target platform has compatible version)])
+;;
+no)
+    AC_MSG_RESULT(Skipped OpenSSL version check)
+;;
+esac
+
+        AC_MSG_CHECKING(for OpenSSL DSA support)
+        if test -f $use_openssl/include/openssl/dsa.h
+        then
+            AC_DEFINE(HAVE_OPENSSL_DSA)
+            AC_MSG_RESULT(yes)
+        else
+            AC_MSG_RESULT(no)
+        fi
+        CFLAGS="$saved_cflags"
+        LIBS="$saved_libs"
+        ;;
+esac
+if test "x$USE_OPENSSL" != "x"
+then
+    APR_ADDTO(TCNATIVE_PRIV_INCLUDES, [$TCN_OPENSSL_INC])
+    APR_ADDTO(TCNATIVE_LDFLAGS, [$TCN_OPENSSL_LIBS])
+    APR_ADDTO(CFLAGS, [-DHAVE_OPENSSL])
+fi
+])
diff --git a/connectors/jni/native/buildconf b/connectors/jni/native/buildconf
new file mode 100755
index 0000000..53c885c
--- /dev/null
+++ b/connectors/jni/native/buildconf
@@ -0,0 +1,96 @@
+#!/bin/sh
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+
+# Default place to look for apr source.  Can be overridden with 
+#   --with-apr=[directory]
+apr_src_dir=`pwd`/srclib/apr-1.2.7
+
+while test $# -gt 0 
+do
+  # Normalize
+  case "$1" in
+  -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+  *) optarg= ;;
+  esac
+
+  case "$1" in
+  --with-apr=*)
+  apr_src_dir=$optarg
+  ;;
+  esac
+
+  shift
+done
+
+if test -d "$apr_src_dir"
+then
+  echo ""
+  echo "Looking for apr source in $apr_src_dir"
+else
+  echo ""
+  echo "Problem finding apr source in $apr_src_dir."
+  echo "Use:"
+  echo "  --with-apr=[directory]" 
+  exit 1
+fi
+
+# Remove some files, then copy them from apr source tree
+rm -f build/apr_common.m4 build/find_apr.m4 build/install.sh \
+      build/config.guess build/config.sub
+cp $apr_src_dir/build/apr_common.m4 $apr_src_dir/build/find_apr.m4 \
+   $apr_src_dir/build/install.sh $apr_src_dir/build/config.guess  \
+   $apr_src_dir/build/config.sub build
+
+# Remove aclocal.m4 as it'll break some builds...
+rm -rf aclocal.m4 autom4te*.cache
+
+echo "Creating configure ..."
+### do some work to toss config.cache?
+if ${AUTOCONF:-autoconf}; then
+  :
+else
+  echo "autoconf failed"
+  exit 1
+fi
+
+#
+# Generate build-outputs.mk for the build systme
+#
+echo "Generating 'make' outputs ..."
+$apr_src_dir/build/gen-build.py make
+
+# Remove autoconf cache again
+rm -rf autom4te*.cache
+
+# Create RPM Spec file
+if [ -f `which cut` ]; then
+  echo rebuilding rpm spec file
+  ( REVISION=`build/get-version.sh all include/tcn_version.h TCN`
+    VERSION=`echo $REVISION | cut -d- -s -f1`
+    RELEASE=`echo $REVISION | cut -d- -s -f2`
+    if [ "x$VERSION" = "x" ]; then
+      VERSION=$REVISION
+      RELEASE=1
+    fi
+    cat ./build/rpm/tcnative.spec.in | \
+    sed -e "s/TCN_VERSION/$VERSION/" \
+        -e "s/TCN_RELEASE/$RELEASE/" \
+    > tcnative.spec )
+fi
+
diff --git a/connectors/jni/native/config.layout b/connectors/jni/native/config.layout
new file mode 100644
index 0000000..61422fc
--- /dev/null
+++ b/connectors/jni/native/config.layout
@@ -0,0 +1,248 @@
+##
+##  config.layout -- Pre-defined Installation Path Layouts
+##
+##  Hints:
+##  - layouts can be loaded with configure's --enable-layout=ID option
+##  - when no --enable-layout option is given, the default layout is `apr'
+##  - a trailing plus character (`+') on paths is replaced with a 
+##    `/<target>' suffix where <target> is currently hardcoded to 'apr'.
+##    (This may become a configurable parameter at some point.)
+##
+
+#   Generic path layout that needs --prefix=/some/path
+<Layout generic>
+    exec_prefix:   ${prefix}
+    bindir:        ${exec_prefix}/bin
+    sbindir:       ${exec_prefix}/bin
+    libdir:        ${exec_prefix}/lib
+    libexecdir:    ${exec_prefix}/modules
+    mandir:        ${prefix}/man
+    sysconfdir:    ${prefix}/conf
+    datadir:       ${prefix}
+    installbuilddir: ${datadir}/build
+    includedir:    ${prefix}/include/apr-${TCNATIVE_MAJOR_VERSION}
+    localstatedir: ${prefix}
+    libsuffix:     -${TCNATIVE_MAJOR_VERSION}
+</Layout>
+
+#   Classical Tomcat Native path layout designed for parallel installs.
+<Layout tcnative>
+    prefix:        /usr/local/apr
+    exec_prefix:   ${prefix}
+    bindir:        ${exec_prefix}/bin
+    sbindir:       ${exec_prefix}/bin
+    libdir:        ${exec_prefix}/lib
+    libexecdir:    ${exec_prefix}/modules
+    mandir:        ${prefix}/man
+    sysconfdir:    ${prefix}/conf
+    datadir:       ${prefix}
+    installbuilddir: ${datadir}/build
+    includedir:    ${prefix}/include/apr-${TCNATIVE_MAJOR_VERSION}
+    localstatedir: ${prefix}
+    libsuffix:     -${TCNATIVE_MAJOR_VERSION}
+</Layout>
+
+#   Classical single-installation APR path layout.
+<Layout classic>
+    prefix:        /usr/local/apr
+    exec_prefix:   ${prefix}
+    bindir:        ${exec_prefix}/bin
+    sbindir:       ${exec_prefix}/bin
+    libdir:        ${exec_prefix}/lib
+    libexecdir:    ${exec_prefix}/modules
+    mandir:        ${prefix}/man
+    sysconfdir:    ${prefix}/conf
+    datadir:       ${prefix}
+    installbuilddir: ${datadir}/build
+    includedir:    ${prefix}/include
+    localstatedir: ${prefix}
+</Layout>
+
+#   GNU standards conforming path layout.
+#   See FSF's GNU project `make-stds' document for details.
+<Layout GNU>
+    prefix:        /usr/local
+    exec_prefix:   ${prefix}
+    bindir:        ${exec_prefix}/bin
+    sbindir:       ${exec_prefix}/sbin
+    libdir:        ${exec_prefix}/lib
+    libexecdir:    ${exec_prefix}/libexec
+    mandir:        ${prefix}/man
+    sysconfdir:    ${prefix}/etc+
+    datadir:       ${prefix}/share+
+    installbuilddir: ${datadir}/build
+    includedir:    ${prefix}/include+
+    localstatedir: ${prefix}/var+
+    runtimedir:    ${localstatedir}/run
+</Layout>
+
+#   Mac OS X Server (Rhapsody)
+<Layout Mac OS X Server>
+    prefix:        /Local/Library/WebServer
+    exec_prefix:   /usr
+    bindir:        ${exec_prefix}/bin
+    sbindir:       ${exec_prefix}/sbin
+    libdir:        ${exec_prefix}/lib
+    libexecdir:    /System/Library/apr/Modules
+    mandir:        ${exec_prefix}/share/man
+    sysconfdir:    ${prefix}/Configuration
+    datadir:       ${prefix}
+    installbuilddir: /System/Library/apr/Build
+    includedir:    /System/Library/Frameworks/apr.framework/Versions/2.0/Headers
+    localstatedir: /var
+    runtimedir:    ${prefix}/Logs
+</Layout>
+
+#   Darwin/Mac OS Layout
+<Layout Darwin>
+    prefix:        /usr
+    exec_prefix:   ${prefix}
+    bindir:        ${exec_prefix}/bin
+    sbindir:       ${exec_prefix}/sbin
+    libdir:        ${exec_prefix}/lib
+    libexecdir:    ${exec_prefix}/libexec+
+    mandir:        ${prefix}/share/man
+    datadir:       /Library/WebServer
+    sysconfdir:    /etc+
+    installbuilddir: ${prefix}/share/httpd/build
+    includedir:    ${prefix}/include+
+    localstatedir: /var
+    runtimedir:    ${localstatedir}/run
+</Layout>
+
+#   Red Hat Linux 7.x layout
+<Layout RedHat>
+    prefix:        /usr
+    exec_prefix:   ${prefix}
+    bindir:        ${prefix}/bin
+    sbindir:       ${prefix}/sbin
+    libdir:        ${prefix}/lib
+    libexecdir:    ${prefix}/lib/apr
+    mandir:        ${prefix}/man
+    sysconfdir:    /etc/httpd/conf
+    datadir:       /var/www
+    installbuilddir: ${datadir}/build
+    includedir:    ${prefix}/include/apr
+    localstatedir: /var
+    runtimedir:    ${localstatedir}/run
+</Layout>     
+
+#   According to the /opt filesystem conventions
+<Layout opt>
+    prefix:        /opt/apr
+    exec_prefix:   ${prefix}
+    bindir:        ${exec_prefix}/bin
+    sbindir:       ${exec_prefix}/sbin
+    libdir:        ${exec_prefix}/lib
+    libexecdir:    ${exec_prefix}/libexec
+    mandir:        ${prefix}/man
+    sysconfdir:    /etc${prefix}
+    datadir:       ${prefix}/share
+    installbuilddir: ${datadir}/build
+    includedir:    ${prefix}/include
+    localstatedir: /var${prefix}
+    runtimedir:    ${localstatedir}/run
+</Layout>
+
+#  BeOS layout...
+<Layout beos>
+    prefix:        /boot/home/apr
+    exec_prefix:   ${prefix}
+    bindir:        ${exec_prefix}/bin
+    sbindir:       ${exec_prefix}/bin
+    libdir:        ${exec_prefix}/lib
+    libexecdir:    ${exec_prefix}/libexec
+    mandir:        ${prefix}/man
+    sysconfdir:    ${prefix}/conf
+    datadir:       ${prefix}
+    installbuilddir: ${datadir}/build
+    includedir:    ${prefix}/include
+    localstatedir: ${prefix}
+    runtimedir:    ${localstatedir}/logs
+</Layout>
+
+#   SuSE 6.x layout
+<Layout SuSE>
+    prefix:        /usr
+    exec_prefix:   ${prefix}
+    bindir:        ${prefix}/bin
+    sbindir:       ${prefix}/sbin
+    libdir:        ${prefix}/lib
+    libexecdir:    ${prefix}/lib/apr
+    mandir:        ${prefix}/share/man
+    sysconfdir:    /etc/httpd
+    datadir:       /usr/local/httpd
+    installbuilddir: ${datadir}/build
+    includedir:    ${prefix}/include/apr
+    localstatedir: /var/lib/httpd
+    runtimedir:    /var/run
+</Layout>
+
+#   BSD/OS layout
+<Layout BSDI>
+    prefix:        /var/www
+    exec_prefix:   /usr/contrib
+    bindir:        ${exec_prefix}/bin
+    sbindir:       ${exec_prefix}/bin
+    libdir:        ${exec_prefix}/lib
+    libexecdir:    ${exec_prefix}/libexec/apr
+    mandir:        ${exec_prefix}/man
+    sysconfdir:    ${prefix}/conf
+    datadir:       ${prefix}
+    installbuilddir: ${datadir}/build
+    includedir:    ${exec_prefix}/include/apr
+    localstatedir: /var
+    runtimedir:    ${localstatedir}/run
+</Layout>
+
+#   Solaris 8 Layout
+<Layout Solaris>
+    prefix:        /usr/apr
+    exec_prefix:   ${prefix}
+    bindir:        ${exec_prefix}/bin
+    sbindir:       ${exec_prefix}/bin
+    libdir:        ${exec_prefix}/lib
+    libexecdir:    ${exec_prefix}/libexec
+    mandir:        ${exec_prefix}/man
+    sysconfdir:    /etc/apr
+    datadir:       /var/apr
+    installbuilddir: ${datadir}/build
+    includedir:    ${exec_prefix}/include
+    localstatedir: ${prefix}
+    runtimedir:    /var/run
+</Layout>
+
+#   OpenBSD Layout
+<Layout OpenBSD>
+    prefix:        /var/www
+    exec_prefix:   /usr
+    bindir:        ${exec_prefix}/bin
+    sbindir:       ${exec_prefix}/sbin
+    libdir:        ${exec_prefix}/lib
+    libexecdir:    ${exec_prefix}/lib/apr/modules
+    mandir:        ${exec_prefix}/share/man
+    sysconfdir:    ${prefix}/conf
+    datadir:       ${prefix}
+    installbuilddir: ${prefix}/build
+    includedir:    ${exec_prefix}/lib/apr/include
+    localstatedir: ${prefix}
+    runtimedir:    ${prefix}/logs
+</Layout>
+
+# Debian layout
+<Layout Debian>
+    prefix:        
+    exec_prefix:   ${prefix}/usr
+    bindir:        ${exec_prefix}/bin
+    sbindir:       ${exec_prefix}/sbin
+    libdir:        ${exec_prefix}/lib
+    libexecdir:    ${exec_prefix}/lib/apr/modules
+    mandir:        ${exec_prefix}/share/man
+    datadir:       ${exec_prefix}/share/apr
+    includedir:    ${exec_prefix}/include/apr-${TCNATIVE_MAJOR_VERSION}
+    localstatedir: ${prefix}/var/run
+    runtimedir:    ${prefix}/var/run
+    infodir:       ${exec_prefix}/share/info
+    libsuffix     -${TCNATIVE_MAJOR_VERSION}
+    installbuilddir: ${prefix}/usr/share/apache2/build
+</Layout>
diff --git a/connectors/jni/native/configure.in b/connectors/jni/native/configure.in
new file mode 100644
index 0000000..6074497
--- /dev/null
+++ b/connectors/jni/native/configure.in
@@ -0,0 +1,243 @@
+dnl
+dnl Process this file with autoconf to produce a configure script
+dnl
+
+AC_PREREQ(2.50)
+AC_INIT(configure.in)
+
+AC_CONFIG_AUX_DIR(build)
+
+sinclude(build/apr_common.m4)
+sinclude(build/tcnative.m4)
+sinclude(build/find_apr.m4)
+
+dnl Generate ./config.nice for reproducing runs of configure
+dnl
+APR_CONFIG_NICE(config.nice)
+
+dnl # Some initial steps for configuration.  We setup the default directory
+dnl # and which files are to be configured.
+
+dnl Absolute source/build directory
+abs_srcdir=`(cd $srcdir && pwd)`
+abs_builddir=`pwd`
+
+if test "$abs_builddir" != "$abs_srcdir"; then
+  USE_VPATH=1
+  TCN_CONFIG_LOCATION=build
+else
+  TCN_CONFIG_LOCATION=source
+fi
+
+AC_SUBST(TCN_CONFIG_LOCATION)
+AC_CANONICAL_SYSTEM
+AC_PROG_INSTALL
+
+dnl
+dnl compute the top directory of the build
+dnl note: this is needed for LIBTOOL and exporting the bundled Expat
+dnl
+top_builddir="$abs_builddir"
+AC_SUBST(top_builddir)
+AC_SUBST(abs_srcdir)
+AC_SUBST(abs_builddir)
+
+dnl Initialize mkdir -p functionality.
+APR_MKDIR_P_CHECK($abs_srcdir/build/mkdir.sh)
+
+dnl get our version information
+get_version="$abs_srcdir/build/get-version.sh"
+version_hdr="$abs_srcdir/include/tcn_version.h"
+TCNATIVE_MAJOR_VERSION="`$get_version major $version_hdr TCN`"
+TCNATIVE_DOTTED_VERSION="`$get_version all $version_hdr TCN`"
+TCNATIVE_LIBTOOL_VERSION="`$get_version libtool $version_hdr TCN`"
+
+AC_SUBST(TCNATIVE_DOTTED_VERSION)
+AC_SUBST(TCNATIVE_MAJOR_VERSION)
+AC_SUBST(TCNATIVE_LIBTOOL_VERSION)
+
+echo "Tomcat Native Version: ${TCNATIVE_DOTTED_VERSION}"
+
+dnl Enable the layout handling code, then reparse the prefix-style
+dnl arguments due to autoconf being a PITA.
+APR_ENABLE_LAYOUT(tcnative)
+APR_PARSE_ARGUMENTS
+
+dnl
+dnl set up the compilation flags and stuff
+dnl
+
+TCNATIVE_INCLUDES=""
+TCNATIVE_PRIV_INCLUDES="-I$top_builddir/include"
+
+dnl
+dnl Find the APR includes directory and (possibly) the source (base) dir.
+dnl
+TCN_FIND_APR
+
+dnl
+dnl even though we use apr_rules.mk for building apr-util, we need
+dnl to grab CC and CPP ahead of time so that apr-util config tests
+dnl use the same compiler as APR; we need the same compiler options
+dnl and feature test macros as well
+dnl
+APR_SETIFNULL(CC, `$apr_config --cc`)
+APR_SETIFNULL(CPP, `$apr_config --cpp`)
+
+AC_PROG_INSTALL
+
+dnl
+dnl  Find the JVM related information
+dnl
+TCN_FIND_JDK
+TCN_SABLEVM
+
+dnl MAC OS X does not used include but Headers
+if test -d ${JAVA_HOME}/Headers; then
+  JAVA_INC=Headers
+else
+  JAVA_INC=include
+fi
+APR_ADDTO(TCNATIVE_PRIV_INCLUDES,[-I$JAVA_HOME/$JAVA_INC])
+
+dnl sableVM does not have/need $JAVA_OS/jni_md.h
+if test "$SABLEVM" = "NONE"; then
+  TCN_FIND_JDK_OS
+  if test -z "${JAVA_OS}"; then
+    AC_MSG_RESULT([jni_md.h found in $JAVA_HOME/$JAVA_INC])
+  else
+    APR_ADDTO(TCNATIVE_PRIV_INCLUDES,[-I$JAVA_HOME/$JAVA_INC/$JAVA_OS])
+  fi
+fi
+
+AC_SUBST(JAVA_HOME)
+AC_SUBST(JAVA_PLATFORM)
+AC_SUBST(JAVA_OS)
+
+
+dnl
+dnl Detect openssl toolkit installation
+dnl
+
+use_openssl=true;
+
+AC_ARG_ENABLE(openssl, 
+[ --disable-openssl   avoid using OpenSSL toolkit],
+[
+  use_openssl=false;
+  AC_MSG_RESULT([Disabling SSL support...])
+])
+
+if $use_openssl ; then
+  TCN_CHECK_SSL_TOOLKIT
+fi
+
+so_ext=$APR_SO_EXT
+lib_target=$APR_LIB_TARGET
+AC_SUBST(so_ext)
+AC_SUBST(lib_target)
+
+TCNATIVE_LIBNAME="tcnative${libsuffix}"
+AC_SUBST(TCNATIVE_LIBNAME)
+
+EXTRA_OS_LINK=""
+host_alias=`uname -s`
+case "$host_alias" in
+    dnl ### BeOS requires that ALL symbols resolve at LINK time!
+    dnl ###
+    dnl ### So, if we're building on BeOS then we need to add in the
+    dnl ### apr and expat libraries to the build or it'll die a truly horrible
+    dnl ### death. We now use the apr-config tool to determine the correct
+    dnl ### library to link against :)
+*AIX*|*Darwin*|*BeOS*)
+    dnl need such stuff as -liconv to be specified when building libaprutil.la
+    EXTRA_OS_LINK='$(TCNATIVE_LDFLAGS) $(TCNATIVE_LIBS)'
+    ;;
+*)
+    ;;
+esac
+
+AC_SUBST(EXTRA_OS_LINK)
+
+dnl CFLAGS for maintainer mode
+dnl it also allows the CFLAGS environment variable.
+CFLAGS="${CFLAGS}"
+AC_ARG_ENABLE(
+maintainer-mode,
+[  --enable-maintainer-mode   Turn on debugging and compile time warnings],
+[
+  if test "$GCC" = "yes"; then
+    CFLAGS="${CFLAGS} -DDEBUG -Wall"
+  else
+    CFLAGS="${CFLAGS} -DDEBUG"
+  fi
+AC_MSG_RESULT([...Enabling Maintainer mode...])
+])
+
+dnl
+dnl Prep all the flags and stuff for compilation and export to other builds
+dnl
+APR_ADDTO(TCNATIVE_LIBS, [$LIBS])
+APR_ADDTO(TCNATIVE_LIBS, [$APR_LIBS])
+APR_ADDTO(TCNATIVE_LDFLAGS, [$LDFLAGS])
+
+# Link libkstat for Solaris
+case $host in
+    *-solaris2*)
+        APR_ADDTO(TCNATIVE_LIBS, -lkstat)
+        ;;
+    *)
+        ;;
+esac
+
+AC_SUBST(TCNATIVE_EXPORT_LIBS)
+AC_SUBST(TCNATIVE_PRIV_INCLUDES)
+AC_SUBST(TCNATIVE_INCLUDES)
+AC_SUBST(TCNATIVE_LDFLAGS)
+AC_SUBST(TCNATIVE_LIBS)
+AC_SUBST(CFLAGS)
+AC_SUBST(CPPFLAGS)
+
+dnl copy apr's rules.mk into our build directory.
+if test ! -d ./build; then
+   $mkdir_p build
+fi
+cp $APR_BUILD_DIR/apr_rules.mk $abs_builddir/build/rules.mk
+
+
+dnl
+dnl BSD/OS (BSDi) needs to use a different include syntax in the Makefiles
+dnl
+case "$host_alias" in
+*bsdi* | BSD/OS)
+    # Check whether they've installed GNU make
+    if make --version > /dev/null 2>&1; then
+        INCLUDE_RULES="include $abs_builddir/build/rules.mk"
+        INCLUDE_OUTPUTS="include $abs_srcdir/build-outputs.mk"
+    else
+        INCLUDE_RULES=".include \"$abs_builddir/build/rules.mk\""
+        INCLUDE_OUTPUTS=".include \"$abs_srcdir/build-outputs.mk\""
+    fi
+    ;;
+*)
+    INCLUDE_RULES="include $abs_builddir/build/rules.mk"
+    INCLUDE_OUTPUTS="include $abs_srcdir/build-outputs.mk"
+    ;;
+esac
+
+AC_SUBST(INCLUDE_RULES)
+AC_SUBST(INCLUDE_OUTPUTS)
+
+if test -d $srcdir/test; then
+    test_Makefile="test/Makefile"
+fi
+
+dnl
+dnl everthing is done.
+MAKEFILES="Makefile"
+AC_OUTPUT([
+    tcnative.pc
+    $MAKEFILES
+	],[
+TCNATIVE_MAJOR_VERSION=$TCNATIVE_MAJOR_VERSION
+])
diff --git a/connectors/jni/native/include/ssl_private.h b/connectors/jni/native/include/ssl_private.h
new file mode 100644
index 0000000..b0ee244
--- /dev/null
+++ b/connectors/jni/native/include/ssl_private.h
@@ -0,0 +1,294 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+#ifndef SSL_PRIVATE_H
+#define SSL_PRIVATE_H
+
+/* Exclude unused OpenSSL features
+ * even if the OpenSSL supports them
+ */
+#ifndef OPENSSL_NO_IDEA
+#define OPENSSL_NO_IDEA
+#endif
+#ifndef OPENSSL_NO_KRB5
+#define OPENSSL_NO_KRB5
+#endif
+#ifndef OPENSSL_NO_MDC2
+#define OPENSSL_NO_MDC2
+#endif
+#ifndef OPENSSL_NO_RC5
+#define OPENSSL_NO_RC5
+#endif
+
+/* OpenSSL headers */
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+#include <openssl/x509v3.h>
+#include <openssl/md5.h>
+/* Avoid tripping over an engine build installed globally and detected
+ * when the user points at an explicit non-engine flavor of OpenSSL
+ */
+#ifndef OPENSSL_NO_ENGINE
+#include <openssl/engine.h>
+#endif
+
+#ifndef RAND_MAX
+#include <limits.h>
+#define RAND_MAX INT_MAX
+#endif
+
+#define SSL_ALGO_UNKNOWN (0)
+#define SSL_ALGO_RSA     (1<<0)
+#define SSL_ALGO_DSA     (1<<1)
+#define SSL_ALGO_ALL     (SSL_ALGO_RSA|SSL_ALGO_DSA)
+
+#define SSL_AIDX_RSA     (0)
+#define SSL_AIDX_DSA     (1)
+#define SSL_AIDX_MAX     (2)
+
+/*
+ * Define IDs for the temporary RSA keys and DH params
+ */
+
+#define SSL_TMP_KEY_RSA_512     (0)
+#define SSL_TMP_KEY_RSA_1024    (1)
+#define SSL_TMP_KEY_RSA_2048    (2)
+#define SSL_TMP_KEY_RSA_4096    (3)
+#define SSL_TMP_KEY_DH_512      (4)
+#define SSL_TMP_KEY_DH_1024     (5)
+#define SSL_TMP_KEY_DH_2048     (6)
+#define SSL_TMP_KEY_DH_4096     (7)
+#define SSL_TMP_KEY_MAX         (8)
+
+#define SSL_CRT_FORMAT_UNDEF    (0)
+#define SSL_CRT_FORMAT_ASN1     (1)
+#define SSL_CRT_FORMAT_TEXT     (2)
+#define SSL_CRT_FORMAT_PEM      (3)
+#define SSL_CRT_FORMAT_NETSCAPE (4)
+#define SSL_CRT_FORMAT_PKCS12   (5)
+#define SSL_CRT_FORMAT_SMIME    (6)
+#define SSL_CRT_FORMAT_ENGINE   (7)
+/* XXX this stupid macro helps us to avoid
+ * adding yet another param to load_*key()
+ */
+#define SSL_KEY_FORMAT_IISSGC   (8)
+
+/*
+ * Define the SSL options
+ */
+#define SSL_OPT_NONE            (0)
+#define SSL_OPT_RELSET          (1<<0)
+#define SSL_OPT_STDENVVARS      (1<<1)
+#define SSL_OPT_EXPORTCERTDATA  (1<<3)
+#define SSL_OPT_FAKEBASICAUTH   (1<<4)
+#define SSL_OPT_STRICTREQUIRE   (1<<5)
+#define SSL_OPT_OPTRENEGOTIATE  (1<<6)
+#define SSL_OPT_ALL             (SSL_OPT_STDENVVARS|SSL_OPT_EXPORTCERTDATA|SSL_OPT_FAKEBASICAUTH|SSL_OPT_STRICTREQUIRE|SSL_OPT_OPTRENEGOTIATE)
+
+/*
+ * Define the SSL Protocol options
+ */
+#define SSL_PROTOCOL_NONE       (0)
+#define SSL_PROTOCOL_SSLV2      (1<<0)
+#define SSL_PROTOCOL_SSLV3      (1<<1)
+#define SSL_PROTOCOL_TLSV1      (1<<2)
+#define SSL_PROTOCOL_ALL        (SSL_PROTOCOL_SSLV2|SSL_PROTOCOL_SSLV3|SSL_PROTOCOL_TLSV1)
+
+#define SSL_MODE_CLIENT         (0)
+#define SSL_MODE_SERVER         (1)
+#define SSL_MODE_COMBINED       (2)
+
+#define SSL_BIO_FLAG_RDONLY     (1<<0)
+#define SSL_BIO_FLAG_CALLBACK   (1<<1)
+#define SSL_DEFAULT_CACHE_SIZE  (256)
+#define SSL_DEFAULT_VHOST_NAME  ("_default_:443")
+#define SSL_MAX_STR_LEN         (2048)
+#define SSL_MAX_PASSWORD_LEN    (256)
+
+#define SSL_CVERIFY_UNSET           (-1)
+#define SSL_CVERIFY_NONE            (0)
+#define SSL_CVERIFY_OPTIONAL        (1)
+#define SSL_CVERIFY_REQUIRE         (2)
+#define SSL_CVERIFY_OPTIONAL_NO_CA  (3)
+#define SSL_VERIFY_PEER_STRICT      (SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
+
+#define SSL_SHUTDOWN_TYPE_UNSET     (0)
+#define SSL_SHUTDOWN_TYPE_STANDARD  (1)
+#define SSL_SHUTDOWN_TYPE_UNCLEAN   (2)
+#define SSL_SHUTDOWN_TYPE_ACCURATE  (3)
+
+#define SSL_TO_APR_ERROR(X)         (APR_OS_START_USERERR + 1000 + X)
+
+#define SSL_INFO_SESSION_ID                 (0x0001)
+#define SSL_INFO_CIPHER                     (0x0002)
+#define SSL_INFO_CIPHER_USEKEYSIZE          (0x0003)
+#define SSL_INFO_CIPHER_ALGKEYSIZE          (0x0004)
+#define SSL_INFO_CIPHER_VERSION             (0x0005)
+#define SSL_INFO_CIPHER_DESCRIPTION         (0x0006)
+#define SSL_INFO_PROTOCOL                   (0x0007)
+
+#define SSL_INFO_CLIENT_S_DN                (0x0010)
+#define SSL_INFO_CLIENT_I_DN                (0x0020)
+#define SSL_INFO_SERVER_S_DN                (0x0040)
+#define SSL_INFO_SERVER_I_DN                (0x0080)
+
+#define SSL_INFO_DN_COUNTRYNAME             (0x0001)
+#define SSL_INFO_DN_STATEORPROVINCENAME     (0x0002)
+#define SSL_INFO_DN_LOCALITYNAME            (0x0003)
+#define SSL_INFO_DN_ORGANIZATIONNAME        (0x0004)
+#define SSL_INFO_DN_ORGANIZATIONALUNITNAME  (0x0005)
+#define SSL_INFO_DN_COMMONNAME              (0x0006)
+#define SSL_INFO_DN_TITLE                   (0x0007)
+#define SSL_INFO_DN_INITIALS                (0x0008)
+#define SSL_INFO_DN_GIVENNAME               (0x0009)
+#define SSL_INFO_DN_SURNAME                 (0x000A)
+#define SSL_INFO_DN_DESCRIPTION             (0x000B)
+#define SSL_INFO_DN_UNIQUEIDENTIFIER        (0x000C)
+#define SSL_INFO_DN_EMAILADDRESS            (0x000D)
+
+#define SSL_INFO_CLIENT_MASK                (0x0100)
+
+#define SSL_INFO_CLIENT_M_VERSION           (0x0101)
+#define SSL_INFO_CLIENT_M_SERIAL            (0x0102)
+#define SSL_INFO_CLIENT_V_START             (0x0103)
+#define SSL_INFO_CLIENT_V_END               (0x0104)
+#define SSL_INFO_CLIENT_A_SIG               (0x0105)
+#define SSL_INFO_CLIENT_A_KEY               (0x0106)
+#define SSL_INFO_CLIENT_CERT                (0x0107)
+#define SSL_INFO_CLIENT_V_REMAIN            (0x0108)
+
+#define SSL_INFO_SERVER_MASK                (0x0200)
+
+#define SSL_INFO_SERVER_M_VERSION           (0x0201)
+#define SSL_INFO_SERVER_M_SERIAL            (0x0202)
+#define SSL_INFO_SERVER_V_START             (0x0203)
+#define SSL_INFO_SERVER_V_END               (0x0204)
+#define SSL_INFO_SERVER_A_SIG               (0x0205)
+#define SSL_INFO_SERVER_A_KEY               (0x0206)
+#define SSL_INFO_SERVER_CERT                (0x0207)
+#define SSL_INFO_CLIENT_CERT_CHAIN          (0x0400)
+
+#define SSL_VERIFY_ERROR_IS_OPTIONAL(errnum) \
+   ((errnum == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) \
+    || (errnum == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) \
+    || (errnum == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) \
+    || (errnum == X509_V_ERR_CERT_UNTRUSTED) \
+    || (errnum == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE))
+
+
+
+#define SSL_DEFAULT_PASS_PROMPT "Some of your private key files are encrypted for security reasons.\n"  \
+                                "In order to read them you have to provide the pass phrases.\n"         \
+                                "Enter password :"
+
+extern void *SSL_temp_keys[SSL_TMP_KEY_MAX];
+
+typedef struct {
+    /* client can have any number of cert/key pairs */
+    const char  *cert_file;
+    const char  *cert_path;
+    STACK_OF(X509_INFO) *certs;
+} ssl_pkc_t;
+
+typedef struct tcn_ssl_ctxt_t tcn_ssl_ctxt_t;
+
+typedef struct {
+    char            password[SSL_MAX_PASSWORD_LEN];
+    const char     *prompt;
+    tcn_callback_t cb;
+} tcn_pass_cb_t;
+
+extern tcn_pass_cb_t tcn_password_callback;
+
+struct tcn_ssl_ctxt_t {
+    apr_pool_t      *pool;
+    SSL_CTX         *ctx;
+    BIO             *bio_os;
+    BIO             *bio_is;
+
+    unsigned char   context_id[MD5_DIGEST_LENGTH];
+
+    int             protocol;
+    /* we are one or the other */
+    int             mode;
+
+    /* certificate revocation list */
+    X509_STORE      *crl;
+    /* pointer to the context verify store */
+    X509_STORE      *store;
+    const char      *cert_files[SSL_AIDX_MAX];
+    const char      *key_files[SSL_AIDX_MAX];
+    X509            *certs[SSL_AIDX_MAX];
+    EVP_PKEY        *keys[SSL_AIDX_MAX];
+
+    int             ca_certs;
+    int             shutdown_type;
+    char            *rand_file;
+
+    const char      *cipher_suite;
+    /* for client or downstream server authentication */
+    int             verify_depth;
+    int             verify_mode;
+    tcn_pass_cb_t   *cb_data;
+};
+
+typedef struct {
+    apr_pool_t     *pool;
+    tcn_ssl_ctxt_t *ctx;
+    SSL            *ssl;
+    X509           *peer;
+    int             shutdown_type;
+    apr_socket_t   *sock;
+    apr_pollset_t  *pollset;
+} tcn_ssl_conn_t;
+
+
+#define SSL_CTX_get_extra_certs(ctx)        ((ctx)->extra_certs)
+#define SSL_CTX_set_extra_certs(ctx, value) \
+    TCN_BEGIN_MACRO                         \
+        (ctx)->extra_certs = (value);       \
+    TCN_END_MACRO
+
+/*
+ *  Additional Functions
+ */
+void        SSL_init_app_data2_idx(void);
+void       *SSL_get_app_data2(SSL *);
+void        SSL_set_app_data2(SSL *, void *);
+int         SSL_password_prompt(tcn_pass_cb_t *);
+int         SSL_password_callback(char *, int, int, void *);
+void        SSL_BIO_close(BIO *);
+void        SSL_BIO_doref(BIO *);
+DH         *SSL_dh_get_tmp_param(int);
+DH         *SSL_dh_get_param_from_file(const char *);
+RSA        *SSL_callback_tmp_RSA(SSL *, int, int);
+DH         *SSL_callback_tmp_DH(SSL *, int, int);
+void        SSL_vhost_algo_id(const unsigned char *, unsigned char *, int);
+int         SSL_CTX_use_certificate_chain(SSL_CTX *, const char *, int);
+int         SSL_callback_SSL_verify(int, X509_STORE_CTX *);
+int         SSL_rand_seed(const char *file);
+#endif /* SSL_PRIVATE_H */
diff --git a/connectors/jni/native/include/tcn.h b/connectors/jni/native/include/tcn.h
new file mode 100644
index 0000000..c09ae6e
--- /dev/null
+++ b/connectors/jni/native/include/tcn.h
@@ -0,0 +1,286 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+#ifndef TCN_H
+#define TCN_H
+
+#include "apr.h"
+#include "apr_general.h"
+#include "apr_pools.h"
+#include "apr_portable.h"
+#include "apr_network_io.h"
+#include "apr_strings.h"
+
+#ifndef APR_HAS_THREADS
+#error "Missing APR_HAS_THREADS support from APR."
+#endif
+
+#if defined(DEBUG) || defined(_DEBUG)
+/* On -DDEBUG use the statistics */
+#ifndef TCN_DO_STATISTICS
+#define TCN_DO_STATISTICS
+#endif
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#include <process.h>
+#else
+#include <unistd.h>
+#endif
+
+#include "tcn_api.h"
+
+
+#if defined(_DEBUG) || defined(DEBUG)
+#include <assert.h>
+#define TCN_ASSERT(x)  assert((x))
+#else
+#define TCN_ASSERT(x) (void)0
+#endif
+
+#ifndef APR_MAX_IOVEC_SIZE
+#define APR_MAX_IOVEC_SIZE 1024
+#endif
+
+#define TCN_TIMEUP      APR_OS_START_USERERR + 1
+#define TCN_EAGAIN      APR_OS_START_USERERR + 2
+#define TCN_EINTR       APR_OS_START_USERERR + 3
+#define TCN_EINPROGRESS APR_OS_START_USERERR + 4
+#define TCN_ETIMEDOUT   APR_OS_START_USERERR + 5
+
+#define TCN_LOG_EMERG  1
+#define TCN_LOG_ERROR  2
+#define TCN_LOG_NOTICE 3
+#define TCN_LOG_WARN   4
+#define TCN_LOG_INFO   5
+#define TCN_LOG_DEBUG  6
+
+#define TCN_ERROR_WRAP(E)                   \
+    if (APR_STATUS_IS_TIMEUP(E))            \
+        (E) = TCN_TIMEUP;                   \
+    else if (APR_STATUS_IS_EAGAIN(E))       \
+        (E) = TCN_EAGAIN;                   \
+    else if (APR_STATUS_IS_EINTR(E))        \
+        (E) = TCN_EINTR;                    \
+    else if (APR_STATUS_IS_EINPROGRESS(E))  \
+        (E) = TCN_EINPROGRESS;              \
+    else if (APR_STATUS_IS_ETIMEDOUT(E))    \
+        (E) = TCN_ETIMEDOUT;                \
+    else                                    \
+        (E) = (E)
+
+#define TCN_CLASS_PATH  "org/apache/tomcat/jni/"
+#define TCN_FINFO_CLASS TCN_CLASS_PATH "FileInfo"
+#define TCN_AINFO_CLASS TCN_CLASS_PATH "Sockaddr"
+#define TCN_ERROR_CLASS TCN_CLASS_PATH "Error"
+#define TCN_PARENT_IDE  "TCN_PARENT_ID"
+
+#define UNREFERENCED(P)      (P) = (P)
+#define UNREFERENCED_STDARGS e = e; o = o
+#ifdef WIN32
+#define LLT(X) (X)
+#else
+#define LLT(X) ((long)(X))
+#endif
+#define P2J(P)          ((jlong)LLT(P))
+#define J2P(P, T)       ((T)LLT((jlong)P))
+/* On stack buffer size */
+#define TCN_BUFFER_SZ   8192
+#define TCN_STDARGS     JNIEnv *e, jobject o
+#define TCN_IMPARGS     JNIEnv *e, jobject o, void *sock
+#define TCN_IMPCALL(X)  e, o, X->opaque
+
+#define TCN_IMPLEMENT_CALL(RT, CL, FN)  \
+    JNIEXPORT RT JNICALL Java_org_apache_tomcat_jni_##CL##_##FN
+
+#define TCN_IMPLEMENT_METHOD(RT, FN)    \
+    static RT method_##FN
+
+#define TCN_GETNET_METHOD(FN)  method_##FN
+
+#define TCN_SOCKET_UNKNOWN  0
+#define TCN_SOCKET_APR      1
+#define TCN_SOCKET_SSL      2
+#define TCN_SOCKET_UNIX     3
+#define TCN_SOCKET_NTPIPE   4
+
+#define TCN_SOCKET_GET_POOL 0
+#define TCN_SOCKET_GET_IMPL 1
+#define TCN_SOCKET_GET_APRS 2
+#define TCN_SOCKET_GET_TYPE 3
+
+typedef struct {
+    int type;
+    apr_status_t (*cleanup)(void *);
+    apr_status_t (APR_THREAD_FUNC *close) (apr_socket_t *);
+    apr_status_t (APR_THREAD_FUNC *shutdown) (apr_socket_t *, apr_shutdown_how_e);
+    apr_status_t (APR_THREAD_FUNC *opt_get)(apr_socket_t *, apr_int32_t, apr_int32_t *);
+    apr_status_t (APR_THREAD_FUNC *opt_set)(apr_socket_t *, apr_int32_t, apr_int32_t);
+    apr_status_t (APR_THREAD_FUNC *timeout_get)(apr_socket_t *, apr_interval_time_t *);
+    apr_status_t (APR_THREAD_FUNC *timeout_set)(apr_socket_t *, apr_interval_time_t);
+    apr_status_t (APR_THREAD_FUNC *send) (apr_socket_t *, const char *, apr_size_t *);
+    apr_status_t (APR_THREAD_FUNC *sendv)(apr_socket_t *, const struct iovec *, apr_int32_t, apr_size_t *);
+    apr_status_t (APR_THREAD_FUNC *recv) (apr_socket_t *, char *, apr_size_t *);
+} tcn_nlayer_t;
+
+typedef struct {
+    apr_pool_t   *pool;
+    apr_pool_t   *child;
+    apr_socket_t *sock;
+    void         *opaque;
+    char         *jsbbuff;
+    char         *jrbbuff;
+    tcn_nlayer_t *net;
+} tcn_socket_t;
+
+/* Private helper functions */
+void            tcn_Throw(JNIEnv *, const char *, ...);
+void            tcn_ThrowException(JNIEnv *, const char *);
+void            tcn_ThrowMemoryException(JNIEnv *, const char *, int, const char *);
+void            tcn_ThrowAPRException(JNIEnv *, apr_status_t);
+jstring         tcn_new_string(JNIEnv *, const char *);
+jstring         tcn_new_stringn(JNIEnv *, const char *, size_t);
+jbyteArray      tcn_new_arrayb(JNIEnv *, const unsigned char *, size_t);
+jobjectArray    tcn_new_arrays(JNIEnv *env, size_t len);
+char           *tcn_get_string(JNIEnv *, jstring);
+char           *tcn_strdup(JNIEnv *, jstring);
+char           *tcn_pstrdup(JNIEnv *, jstring, apr_pool_t *);
+apr_status_t    tcn_load_finfo_class(JNIEnv *, jclass);
+apr_status_t    tcn_load_ainfo_class(JNIEnv *, jclass);
+
+#define J2S(V)  c##V
+#define J2L(V)  p##V
+
+#define J2T(T) (apr_time_t)((T))
+
+#define TCN_BEGIN_MACRO     if (1) {
+#define TCN_END_MACRO       } else (void)(0)
+
+#define TCN_ALLOC_CSTRING(V)     \
+    const char *c##V = V ? (const char *)((*e)->GetStringUTFChars(e, V, 0)) : NULL
+
+#define TCN_FREE_CSTRING(V)      \
+    if (c##V) (*e)->ReleaseStringUTFChars(e, V, c##V)
+
+#define TCN_ALLOC_JSTRING(V)     \
+    char *c##V = tcn_get_string(e, (V))
+
+#define AJP_TO_JSTRING(V)   (*e)->NewStringUTF((e), (V))
+
+#define TCN_FREE_JSTRING(V)      \
+    TCN_BEGIN_MACRO              \
+        if (c##V)                \
+            free(c##V);          \
+    TCN_END_MACRO
+
+#define TCN_CHECK_ALLOCATED(x)                              \
+        if (x == NULL) {                                    \
+            tcn_ThrowMemoryException(e, __FILE__, __LINE__, \
+            "APR memory allocation failed");                \
+            goto cleanup;                                   \
+        } else (void)(0)
+
+#define TCN_THROW_IF_ERR(x, r)                  \
+    TCN_BEGIN_MACRO                             \
+        apr_status_t R = (x);                   \
+        if (R != APR_SUCCESS) {                 \
+            tcn_ThrowAPRException(e, R);        \
+            (r) = 0;                            \
+            goto cleanup;                       \
+        }                                       \
+    TCN_END_MACRO
+
+#define TCN_THROW_OS_ERROR(E)   \
+    tcn_ThrowAPRException((E), apr_get_os_error())
+
+#define TCN_LOAD_CLASS(E, C, N, R)                  \
+    TCN_BEGIN_MACRO                                 \
+        jclass _##C = (*(E))->FindClass((E), N);    \
+        if (_##C == NULL) {                         \
+            (*(E))->ExceptionClear((E));            \
+            return R;                               \
+        }                                           \
+        C = (*(E))->NewGlobalRef((E), _##C);        \
+        (*(E))->DeleteLocalRef((E), _##C);          \
+    TCN_END_MACRO
+
+#define TCN_UNLOAD_CLASS(E, C)                      \
+        (*(E))->DeleteGlobalRef((E), (C))
+
+#define TCN_IS_NULL(E, O)                           \
+        ((*(E))->IsSameObject((E), (O), NULL) == JNI_TRUE)
+
+#define TCN_GET_METHOD(E, C, M, N, S, R)            \
+    TCN_BEGIN_MACRO                                 \
+        M = (*(E))->GetMethodID((E), C, N, S);      \
+        if (M == NULL) {                            \
+            return R;                               \
+        }                                           \
+    TCN_END_MACRO
+
+#define TCN_MAX_METHODS 8
+
+typedef struct {
+    jobject     obj;
+    jmethodID   mid[TCN_MAX_METHODS];
+    void        *opaque;
+} tcn_callback_t;
+
+#define TCN_MIN(a, b) ((a) < (b) ? (a) : (b))
+#define TCN_MAX(a, b) ((a) > (b) ? (a) : (b))
+
+#ifdef WIN32
+#define TCN_ALLOC_WSTRING(V)     \
+    jsize wl##V = (*e)->GetStringLength(e, V);   \
+    const jchar *ws##V = V ? (const jchar *)((*e)->GetStringChars(e, V, 0)) : NULL; \
+    jchar *w##V = NULL
+
+#define TCN_INIT_WSTRING(V)                                     \
+        w##V = (jchar *)malloc((wl##V + 1) * sizeof(jchar));    \
+        wcsncpy(w##V, ws##V, wl##V);                        \
+        w##V[wl##V] = 0
+
+#define TCN_FREE_WSTRING(V)      \
+    if (ws##V) (*e)->ReleaseStringChars(e, V, ws##V); \
+    if (ws##V) free (w##V)
+
+#define J2W(V)  w##V
+
+#endif
+
+#if  !APR_HAVE_IPV6
+#define APR_INET6 APR_INET
+#endif
+
+#define GET_S_FAMILY(T, F)           \
+    if (F == 0) T = APR_UNSPEC;      \
+    else if (F == 1) T = APR_INET;   \
+    else if (F == 2) T = APR_INET6;  \
+    else T = F
+
+#define GET_S_TYPE(T, F)             \
+    if (F == 0) T = SOCK_STREAM;     \
+    else if (F == 1) T = SOCK_DGRAM; \
+    else T = F
+
+#endif /* TCN_H */
diff --git a/connectors/jni/native/include/tcn_api.h b/connectors/jni/native/include/tcn_api.h
new file mode 100644
index 0000000..eb9381c
--- /dev/null
+++ b/connectors/jni/native/include/tcn_api.h
@@ -0,0 +1,167 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+#ifndef TCN_API_H
+#define TCN_API_H
+
+#include "apr.h"
+#include "apr_general.h"
+#include "apr_pools.h"
+#include "apr_portable.h"
+#include "apr_network_io.h"
+#include "apr_strings.h"
+
+#ifndef APR_HAS_THREADS
+#error "Missing APR_HAS_THREADS support from APR."
+#endif
+#include <jni.h>
+
+/**
+ * TCN_DECLARE_EXPORT is defined when building the TCN dynamic library,
+ * so that all public symbols are exported.
+ *
+ * TCN_DECLARE_STATIC is defined when including the TCN public headers,
+ * to provide static linkage when the dynamic library may be unavailable.
+ *
+ * TCN_DECLARE_STATIC and TCN_DECLARE_EXPORT are left undefined when
+ * including the TCN public headers, to import and link the symbols from
+ * the dynamic TCN library and assure appropriate indirection and calling
+ * conventions at compile time.
+ */
+
+#if !defined(WIN32)
+/**
+ * The public TCN functions are declared with TCN_DECLARE(), so they may
+ * use the most appropriate calling convention.  Public APR functions with
+ * variable arguments must use TCN_DECLARE_NONSTD().
+ *
+ * @deffunc TCN_DECLARE(rettype) apr_func(args);
+ */
+#define TCN_DECLARE(type)            type
+/**
+ * The public TCN functions using variable arguments are declared with
+ * TCN_DECLARE_NONSTD(), as they must use the C language calling convention.
+ *
+ * @deffunc TCN_DECLARE_NONSTD(rettype) apr_func(args, ...);
+ */
+#define TCN_DECLARE_NONSTD(type)     type
+/**
+ * The public TCN variables are declared with TCN_DECLARE_DATA.
+ * This assures the appropriate indirection is invoked at compile time.
+ *
+ * @deffunc TCN_DECLARE_DATA type apr_variable;
+ * @tip extern TCN_DECLARE_DATA type apr_variable; syntax is required for
+ * declarations within headers to properly import the variable.
+ */
+#define TCN_DECLARE_DATA
+#elif defined(TCN_DECLARE_STATIC)
+#define TCN_DECLARE(type)            type __stdcall
+#define TCN_DECLARE_NONSTD(type)     type
+#define TCN_DECLARE_DATA
+#elif defined(TCN_DECLARE_EXPORT)
+#define TCN_DECLARE(type)            __declspec(dllexport) type __stdcall
+#define TCN_DECLARE_NONSTD(type)     __declspec(dllexport) type
+#define TCN_DECLARE_DATA             __declspec(dllexport)
+#else
+/**
+ * The public TCN functions are declared with TCN_DECLARE(), so they may
+ * use the most appropriate calling convention.  Public APR functions with
+ * variable arguments must use TCN_DECLARE_NONSTD().
+ *
+ */
+#define TCN_DECLARE(type)            __declspec(dllimport) type __stdcall
+/**
+ * The public TCN functions using variable arguments are declared with
+ * TCN_DECLARE_NONSTD(), as they must use the C language calling convention.
+ *
+ */
+#define TCN_DECLARE_NONSTD(type)     __declspec(dllimport) type
+/**
+ * The public TCN variables are declared with TCN_DECLARE_DATA.
+ * This assures the appropriate indirection is invoked at compile time.
+ *
+ * @remark extern TCN_DECLARE_DATA type apr_variable; syntax is required for
+ * declarations within headers to properly import the variable.
+ */
+#define TCN_DECLARE_DATA             __declspec(dllimport)
+#endif
+
+#if !defined(WIN32) || defined(TCN_MODULE_DECLARE_STATIC)
+/**
+ * Declare a dso module's exported module structure as TCN_MODULE_DECLARE_DATA.
+ *
+ * Unless TCN_MODULE_DECLARE_STATIC is defined at compile time, symbols
+ * declared with TCN_MODULE_DECLARE_DATA are always exported.
+ * @code
+ * module TCN_MODULE_DECLARE_DATA mod_tag
+ * @endcode
+ */
+#if defined(WIN32)
+#define TCN_MODULE_DECLARE(type)            type __stdcall
+#else
+#define TCN_MODULE_DECLARE(type)            type
+#endif
+#define TCN_MODULE_DECLARE_NONSTD(type)     type
+#define TCN_MODULE_DECLARE_DATA
+#else
+/**
+ * TCN_MODULE_DECLARE_EXPORT is a no-op.  Unless contradicted by the
+ * TCN_MODULE_DECLARE_STATIC compile-time symbol, it is assumed and defined.
+ */
+#define TCN_MODULE_DECLARE_EXPORT
+#define TCN_MODULE_DECLARE(type)          __declspec(dllexport) type __stdcall
+#define TCN_MODULE_DECLARE_NONSTD(type)   __declspec(dllexport) type
+#define TCN_MODULE_DECLARE_DATA           __declspec(dllexport)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file tcn_api.h
+ * @brief
+ *
+ * Tomcat Native Public API
+ */
+
+/* Return global apr pool
+ */
+TCN_DECLARE(apr_pool_t *) tcn_get_global_pool(void);
+
+/* Return global String class
+ */
+TCN_DECLARE(jclass) tcn_get_string_class(void);
+
+/* Return global JVM initalized on JNI_OnLoad
+ */
+TCN_DECLARE(JavaVM *) tcn_get_java_vm(void);
+
+/* Get current thread JNIEnv
+ */
+TCN_DECLARE(jint) tcn_get_java_env(JNIEnv **);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TCN_API_H */
diff --git a/connectors/jni/native/include/tcn_version.h b/connectors/jni/native/include/tcn_version.h
new file mode 100644
index 0000000..ac51016
--- /dev/null
+++ b/connectors/jni/native/include/tcn_version.h
@@ -0,0 +1,99 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+#ifndef TCN_VERSION_H
+#define TCN_VERSION_H
+
+#include "apr_version.h"
+
+#include "tcn.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file tcn_version.h
+ * @brief
+ *
+ * Tomcat Native Version
+ *
+ * There are several different mechanisms for accessing the version. There
+ * is a string form, and a set of numbers; in addition, there are constants
+ * which can be compiled into your application, and you can query the library
+ * being used for its actual version.
+ *
+ * Note that it is possible for an application to detect that it has been
+ * compiled against a different version of APU by use of the compile-time
+ * constants and the use of the run-time query function.
+ *
+ * TCN version numbering follows the guidelines specified in:
+ *
+ *     http://apr.apache.org/versioning.html
+ */
+
+/* The numeric compile-time version constants. These constants are the
+ * authoritative version numbers for TCN.
+ */
+
+/** major version
+ * Major API changes that could cause compatibility problems for older
+ * programs such as structure size changes.  No binary compatibility is
+ * possible across a change in the major version.
+ */
+#define TCN_MAJOR_VERSION       1
+
+/**
+ * Minor API changes that do not cause binary compatibility problems.
+ * Should be reset to 0 when upgrading TCN_MAJOR_VERSION
+ */
+#define TCN_MINOR_VERSION       1
+
+/** patch level */
+#define TCN_PATCH_VERSION       11
+
+/**
+ *  This symbol is defined for internal, "development" copies of TCN. This
+ *  symbol will be #undef'd for releases.
+ */
+#undef TCN_IS_DEV_VERSION
+
+
+/** The formatted string of APU's version */
+#define TCN_VERSION_STRING \
+     APR_STRINGIFY(TCN_MAJOR_VERSION) "."\
+     APR_STRINGIFY(TCN_MINOR_VERSION) "."\
+     APR_STRINGIFY(TCN_PATCH_VERSION)\
+     TCN_IS_DEV_STRING
+
+/** Internal: string form of the "is dev" flag */
+#ifdef TCN_IS_DEV_VERSION
+#define TCN_IS_DEV_STRING "-dev"
+#else
+#define TCN_IS_DEV_STRING ""
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TCN_VERSION_H */
diff --git a/connectors/jni/native/libtcnative.dsp b/connectors/jni/native/libtcnative.dsp
new file mode 100644
index 0000000..0e5d069
--- /dev/null
+++ b/connectors/jni/native/libtcnative.dsp
@@ -0,0 +1,231 @@
+# Microsoft Developer Studio Project File - Name="libtcnative" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=libtcnative - 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 "libtcnative.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 "libtcnative.mak" CFG="libtcnative - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "libtcnative - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "libtcnative - 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)" == "libtcnative - 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 /Zi /O2 /Oy- /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /Zi /O2 /Oy- /I "./include" /I "../apr/include" /I "../apr/include/arch/win32" /I "$(JAVA_HOME)/include" /I "$(JAVA_HOME)/include/win32" /I "../openssl/inc32" /D "NDEBUG" /D "TCN_DECLARE_EXPORT" /D "WIN32" /D "_WINDOWS" /D "NO_IDEA" /D "NO_RC5" /D "NO_MDC2" /D "OPENSSL_NO_IDEA" /D "OPENSSL_NO_RC5" /D "OPENSSL_NO_MDC2" /D "HAVE_OPENSSL" /D HAVE_SSL_SET_STATE=1 /Fd"Release\libtcnative_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# 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 advapi32.lib ws2_32.lib mswsock.lib wldap32.lib psapi.lib ole32.lib shlwapi.lib /nologo /base:"0x6EE00000" /subsystem:windows /dll /debug /machine:I386 /opt:ref
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib wldap32.lib psapi.lib ole32.lib shlwapi.lib libeay32.lib ssleay32.lib /nologo /base:"0x6EE00000" /subsystem:windows /dll /debug /machine:I386 /out:"Release/libtcnative-1.dll" /libpath:"../openssl/out32" /libpath:"../openssl/out32dll" /opt:ref
+
+!ELSEIF  "$(CFG)" == "libtcnative - 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 /W4 /GX /Zi /Od /I "./include" /I "../apr/include" /I "../apr/include/arch/win32" /I "$(JAVA_HOME)/include" /I "$(JAVA_HOME)/include/win32" /I "../openssl/inc32" /D "_DEBUG" /D "TCN_DECLARE_EXPORT" /D "WIN32" /D "_WINDOWS" /D "NO_IDEA" /D "NO_RC5" /D "NO_MDC2" /D "OPENSSL_NO_IDEA" /D "OPENSSL_NO_RC5" /D "OPENSSL_NO_MDC2" /D "HAVE_OPENSSL" /D HAVE_SSL_SET_STATE=1 /Fd"Debug\libtcnative_src" /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# 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 advapi32.lib ws2_32.lib mswsock.lib wldap32.lib psapi.lib ole32.lib shlwapi.lib /nologo /base:"0x6EE00000" /subsystem:windows /dll /incremental:no /debug /machine:I386
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib wldap32.lib psapi.lib ole32.lib shlwapi.lib libeay32.lib ssleay32.lib /nologo /base:"0x6EE00000" /subsystem:windows /dll /incremental:no /debug /machine:I386 /out:"Debug/libtcnative-1.dll" /libpath:"../openssl/out32" /libpath:"../openssl/out32dll"
+
+!ENDIF 
+
+# Begin Target
+
+# Name "libtcnative - Win32 Release"
+# Name "libtcnative - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\src\address.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\dir.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\error.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\file.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\info.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\jnilib.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\lock.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\misc.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\mmap.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\multicast.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\network.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\poll.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\pool.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\proc.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\shm.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\ssl.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\sslcontext.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\sslinfo.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\sslnetwork.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\sslutils.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\stdlib.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\thread.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\user.c
+# End Source File
+# End Group
+# Begin Group "Generated Files"
+
+# PROP Default_Filter ""
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\include\ssl_private.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\include\tcn.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\include\tcn_api.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\include\tcn_version.h
+# End Source File
+# End Group
+# Begin Group "Platform Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\os\win32\ntpipe.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\os\win32\registry.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\os\win32\system.c
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=.\os\win32\libtcnative.rc
+# End Source File
+# End Target
+# End Project
diff --git a/connectors/jni/native/libtcnative.dsw b/connectors/jni/native/libtcnative.dsw
new file mode 100644
index 0000000..042257e
--- /dev/null
+++ b/connectors/jni/native/libtcnative.dsw
@@ -0,0 +1,71 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "apr"=..\apr\apr.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "libapr"=..\apr\libapr.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "libtcnative"=.\libtcnative.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name libapr
+    End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "tcnative"=.\tcnative.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name apr
+    End Project Dependency
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/connectors/jni/native/os/netware/system.c b/connectors/jni/native/os/netware/system.c
new file mode 100644
index 0000000..c53b8c2
--- /dev/null
+++ b/connectors/jni/native/os/netware/system.c
@@ -0,0 +1,45 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Mladen Turk
+ * @version $Revision$, $Date$
+ */
+ 
+#include "apr.h"
+#include "apr_pools.h"
+#include "apr_network_io.h"
+#include "apr_poll.h"
+
+#include "tcn.h"
+
+TCN_IMPLEMENT_CALL(jboolean, OS, is)(TCN_STDARGS, jint type)
+{
+    UNREFERENCED_STDARGS;
+    if (type == 2)
+        return JNI_TRUE;
+    else
+        return JNI_FALSE;
+}
+
+TCN_IMPLEMENT_CALL(jint, OS, info)(TCN_STDARGS,
+                                   jlongArray inf)
+{
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(inf);
+    return APR_ENOTIMPL;
+}
diff --git a/connectors/jni/native/os/unix/system.c b/connectors/jni/native/os/unix/system.c
new file mode 100644
index 0000000..2fd5519
--- /dev/null
+++ b/connectors/jni/native/os/unix/system.c
@@ -0,0 +1,385 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+#include "apr.h"
+#include "apr_pools.h"
+#include "apr_network_io.h"
+#include "apr_poll.h"
+
+#include "tcn.h"
+#if defined(__linux__)
+#include <sys/sysinfo.h>
+#elif defined(sun)
+#include <unistd.h>
+#include <sys/swap.h>
+#include <procfs.h>
+#include <kstat.h>
+#include <sys/sysinfo.h>
+#endif
+
+#if defined(DARWIN)
+#include <mach/mach_init.h>
+#include <mach/mach_host.h>
+#include <mach/host_info.h>
+#include <sys/sysctl.h>
+#include <sys/stat.h>
+#endif
+
+#include <syslog.h>
+#include <stdarg.h>
+
+#ifndef LOG_WARN
+#define LOG_WARN LOG_WARNING
+#endif
+
+#if defined(sun)
+#define MAX_PROC_PATH_LEN 64
+#define MAX_CPUS 512
+#define PSINFO_T_SZ sizeof(psinfo_t)
+#define PRUSAGE_T_SZ sizeof(prusage_t)
+
+static int proc_open(const char *type)
+{
+    char proc_path[MAX_PROC_PATH_LEN+1];
+
+    sprintf(proc_path, "/proc/self/%s", type);
+    return open(proc_path, O_RDONLY);
+}
+
+static int proc_read(void *buf, const size_t size, int filedes)
+{
+    ssize_t bytes;
+
+    if (filedes >= 0) {
+        bytes = pread(filedes, buf, size, 0);
+        if (bytes != size)
+            return -1;
+        else
+            return 0;
+    }
+    else
+        return -1;
+}
+
+#endif
+
+TCN_IMPLEMENT_CALL(jboolean, OS, is)(TCN_STDARGS, jint type)
+{
+    UNREFERENCED_STDARGS;
+    if (type == 1)
+        return JNI_TRUE;
+#if defined(__linux__)
+    else if (type == 5)
+        return JNI_TRUE;
+#endif
+#if defined(sun)
+    else if (type == 6)
+        return JNI_TRUE;
+#endif
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
+    else if (type == 7)
+        return JNI_TRUE;
+#endif
+    else
+        return JNI_FALSE;
+}
+
+TCN_IMPLEMENT_CALL(jint, OS, info)(TCN_STDARGS,
+                                   jlongArray inf)
+{
+    jint rv;
+    int  i;
+    jsize ilen = (*e)->GetArrayLength(e, inf);
+    jlong *pvals = (*e)->GetLongArrayElements(e, inf, NULL);
+
+    UNREFERENCED(o);
+    if (ilen < 16) {
+        return APR_EINVAL;
+    }
+    for (i = 0; i < 16; i++)
+        pvals[i] = 0;
+#if defined(__linux__)
+    {
+        struct sysinfo info;
+        if (sysinfo(&info))
+            rv = apr_get_os_error();
+        else {
+            pvals[0] = (jlong)(info.totalram  * info.mem_unit);
+            pvals[1] = (jlong)(info.freeram   * info.mem_unit);
+            pvals[2] = (jlong)(info.totalswap * info.mem_unit);
+            pvals[3] = (jlong)(info.freeswap  * info.mem_unit);
+            pvals[4] = (jlong)(info.sharedram * info.mem_unit);
+            pvals[5] = (jlong)(info.bufferram * info.mem_unit);
+            pvals[6] = (jlong)(100 - (info.freeram * 100 / info.totalram));
+            rv = APR_SUCCESS;
+        }
+    }
+#elif defined(sun)
+    {
+        /* static variables with basic procfs info */
+        static long creation = 0;              /* unix timestamp of process creation */
+        static int psinf_fd = 0;               /* file descriptor for the psinfo procfs file */
+        static int prusg_fd = 0;               /* file descriptor for the usage procfs file */
+        static size_t rss = 0;                 /* maximum of resident set size from previous calls */
+        /* static variables with basic kstat info */
+        static kstat_ctl_t *kstat_ctl = NULL;  /* kstat control object, only initialized once */
+        static kstat_t *kstat_cpu[MAX_CPUS];   /* array of kstat objects for per cpu statistics */
+        static int cpu_count = 0;              /* number of cpu structures found in kstat */
+        static kid_t kid = 0;                  /* kstat ID, for which the kstat_ctl holds the correct chain */
+        /* non-static variables - general use */
+        int res = 0;                           /* general result state */
+        /* non-static variables - sysinfo/swapctl use */
+        long ret_sysconf;                      /* value returned from sysconf call */
+        long tck_dividend;                     /* factor used by transforming tick numbers to milliseconds */
+        long tck_divisor;                      /* divisor used by transforming tick numbers to milliseconds */
+        long sys_pagesize = sysconf(_SC_PAGESIZE); /* size of a system memory page in bytes */
+        long sys_clk_tck = sysconf(_SC_CLK_TCK); /* number of system ticks per second */
+        struct anoninfo info;                  /* structure for information about sizes in anonymous memory system */
+        /* non-static variables - procfs use */
+        psinfo_t psinf;                        /* psinfo structure from procfs */
+        prusage_t prusg;                       /* usage structure from procfs */
+        size_t new_rss = 0;                    /* resident set size read from procfs */
+        time_t now;                            /* time needed for calculating process creation time */
+        /* non-static variables - kstat use */
+        kstat_t *kstat = NULL;                 /* kstat working pointer */
+        cpu_sysinfo_t cpu;                     /* cpu sysinfo working pointer */
+        kid_t new_kid = 0;                     /* kstat ID returned from chain update */
+        int new_kstat = 0;                     /* flag indicating, if kstat structure has changed since last call */
+
+        rv = APR_SUCCESS;
+
+        if (sys_pagesize <= 0) {
+            rv = apr_get_os_error();
+        }
+        else {
+            ret_sysconf = sysconf(_SC_PHYS_PAGES);
+            if (ret_sysconf >= 0) {
+                pvals[0] = (jlong)((jlong)sys_pagesize * ret_sysconf);
+            }
+            else {
+                rv = apr_get_os_error();
+            }
+            ret_sysconf = sysconf(_SC_AVPHYS_PAGES);
+            if (ret_sysconf >= 0) {
+                pvals[1] = (jlong)((jlong)sys_pagesize * ret_sysconf);
+            }
+            else {
+                rv = apr_get_os_error();
+            }
+            res=swapctl(SC_AINFO, &info);
+            if (res >= 0) {
+                pvals[2] = (jlong)((jlong)sys_pagesize * info.ani_max);
+                pvals[3] = (jlong)((jlong)sys_pagesize * info.ani_free);
+                pvals[6] = (jlong)(100 - (jlong)info.ani_free * 100 / info.ani_max);
+            }
+            else {
+                rv = apr_get_os_error();
+            }
+        }
+
+        if (psinf_fd == 0) {
+            psinf_fd = proc_open("psinfo");
+        }
+        res = proc_read(&psinf, PSINFO_T_SZ, psinf_fd);
+        if (res >= 0) {
+            new_rss = psinf.pr_rssize*1024;
+            pvals[13] = (jlong)(new_rss);
+            if (new_rss > rss) {
+                rss = new_rss;
+            }
+            pvals[14] = (jlong)(rss);
+        }
+        else {
+            psinf_fd = 0;
+            rv = apr_get_os_error();
+        }
+        if (prusg_fd == 0) {
+            prusg_fd = proc_open("usage");
+        }
+        res = proc_read(&prusg, PRUSAGE_T_SZ, prusg_fd);
+        if (res >= 0) {
+            if (creation <= 0) {
+                time(&now);
+                creation = (long)(now - (prusg.pr_tstamp.tv_sec -
+                                         prusg.pr_create.tv_sec));
+            }
+            pvals[10] = (jlong)(creation);
+            pvals[11] = (jlong)((jlong)prusg.pr_stime.tv_sec * 1000 +
+                                (prusg.pr_stime.tv_nsec / 1000000));
+            pvals[12] = (jlong)((jlong)prusg.pr_utime.tv_sec * 1000 +
+                                (prusg.pr_utime.tv_nsec / 1000000));
+            pvals[15] = (jlong)(prusg.pr_majf);
+        }
+        else {
+            prusg_fd = 0;
+            rv = apr_get_os_error();
+        }
+
+        if (sys_clk_tck <= 0) {
+            rv = apr_get_os_error();
+        }
+        else {
+            tck_dividend = 1000;
+            tck_divisor = sys_clk_tck;
+            for (i = 0; i < 3; i++) {
+                if (tck_divisor % 2 == 0) {
+                    tck_divisor = tck_divisor / 2;
+                    tck_dividend = tck_dividend / 2;
+                }
+                if (tck_divisor % 5 == 0) {
+                    tck_divisor = tck_divisor / 5;
+                    tck_dividend = tck_dividend / 5;
+                }
+            }
+            if (kstat_ctl == NULL) {
+                kstat_ctl = kstat_open();
+                kid = kstat_ctl->kc_chain_id;
+                new_kstat = 1;
+            } else {
+                new_kid = kstat_chain_update(kstat_ctl);
+                if (new_kid < 0) {
+                    res=kstat_close(kstat_ctl);
+                    kstat_ctl = kstat_open();
+                    kid = kstat_ctl->kc_chain_id;
+                    new_kstat = 1;
+                } else if (new_kid > 0 && kid != new_kid) {
+                    kid = new_kid;
+                    new_kstat = 1;
+                }
+            }
+            if (new_kstat) {
+                cpu_count = 0;
+                for (kstat = kstat_ctl->kc_chain; kstat; kstat = kstat->ks_next) {
+                    if (strncmp(kstat->ks_name, "cpu_stat", 8) == 0) {
+                        kstat_cpu[cpu_count++]=kstat;
+                    }
+                }
+            }
+            for (i = 0; i < cpu_count; i++) {
+                new_kid = kstat_read(kstat_ctl, kstat_cpu[i], NULL);
+                if (new_kid >= 0) {
+                    cpu = ((cpu_stat_t *)kstat_cpu[i]->ks_data)->cpu_sysinfo;
+                    if ( tck_divisor == 1 ) {
+                        pvals[7] += (jlong)(((jlong)cpu.cpu[CPU_IDLE]) * tck_dividend);
+                        pvals[7] += (jlong)(((jlong)cpu.cpu[CPU_WAIT]) * tck_dividend);
+                        pvals[8] += (jlong)(((jlong)cpu.cpu[CPU_KERNEL]) * tck_dividend);
+                        pvals[9] += (jlong)(((jlong)cpu.cpu[CPU_USER]) * tck_dividend);
+                    } else {
+                        pvals[7] += (jlong)(((jlong)cpu.cpu[CPU_IDLE]) * tck_dividend / tck_divisor);
+                        pvals[7] += (jlong)(((jlong)cpu.cpu[CPU_WAIT]) * tck_dividend / tck_divisor);
+                        pvals[8] += (jlong)(((jlong)cpu.cpu[CPU_KERNEL]) * tck_dividend / tck_divisor);
+                        pvals[9] += (jlong)(((jlong)cpu.cpu[CPU_USER]) * tck_dividend / tck_divisor);
+                    }
+                }
+            }
+        }
+
+        /*
+         * The next two are not implemented yet for Solaris
+         * inf[4]  - Amount of shared memory
+         * inf[5]  - Memory used by buffers
+         *
+         */
+    }
+
+#elif defined(DARWIN)
+
+    uint64_t mem_total;
+    size_t len = sizeof(mem_total);
+
+    vm_statistics_data_t vm_info;
+    mach_msg_type_number_t info_count = HOST_VM_INFO_COUNT;
+
+    sysctlbyname("hw.memsize", &mem_total, &len, NULL, 0);
+    pvals[0] = (jlong)mem_total;
+
+    host_statistics(mach_host_self (), HOST_VM_INFO, (host_info_t)&vm_info, &info_count);
+    pvals[1] = (jlong)(((double)vm_info.free_count)*vm_page_size);
+    pvals[6] = (jlong)(100 - (pvals[1] * 100 / mem_total));
+    rv = APR_SUCCESS;
+
+/* DARWIN */
+#else
+    rv = APR_ENOTIMPL;
+#endif
+   (*e)->ReleaseLongArrayElements(e, inf, pvals, 0);
+    return rv;
+}
+
+#define LOG_MSG_DOMAIN                   "Native"
+
+
+TCN_IMPLEMENT_CALL(jstring, OS, expand)(TCN_STDARGS, jstring val)
+{
+    jstring str;
+    TCN_ALLOC_CSTRING(val);
+
+    UNREFERENCED(o);
+
+    /* TODO: Make ${ENVAR} expansion */
+    str = (*e)->NewStringUTF(e, J2S(val));
+
+    TCN_FREE_CSTRING(val);
+    return str;
+}
+
+TCN_IMPLEMENT_CALL(void, OS, sysloginit)(TCN_STDARGS, jstring domain)
+{
+    const char *d;
+    TCN_ALLOC_CSTRING(domain);
+
+    UNREFERENCED(o);
+    if ((d = J2S(domain)) == NULL)
+        d = LOG_MSG_DOMAIN;
+
+    openlog(d, LOG_CONS | LOG_PID, LOG_LOCAL0);
+    TCN_FREE_CSTRING(domain);
+}
+
+TCN_IMPLEMENT_CALL(void, OS, syslog)(TCN_STDARGS, jint level,
+                                     jstring msg)
+{
+    TCN_ALLOC_CSTRING(msg);
+    int id = LOG_DEBUG;
+    UNREFERENCED(o);
+
+    switch (level) {
+        case TCN_LOG_EMERG:
+            id = LOG_EMERG;
+        break;
+        case TCN_LOG_ERROR:
+            id = LOG_ERR;
+        break;
+        case TCN_LOG_NOTICE:
+            id = LOG_NOTICE;
+        break;
+        case TCN_LOG_WARN:
+            id = LOG_WARN;
+        break;
+        case TCN_LOG_INFO:
+            id = LOG_INFO;
+        break;
+    }
+    syslog (id, "%s", J2S(msg));
+
+    TCN_FREE_CSTRING(msg);
+}
diff --git a/connectors/jni/native/os/unix/uxpipe.c b/connectors/jni/native/os/unix/uxpipe.c
new file mode 100644
index 0000000..7495196
--- /dev/null
+++ b/connectors/jni/native/os/unix/uxpipe.c
@@ -0,0 +1,355 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** UNIX AF_LOCAL network wrapper
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+
+#include "tcn.h"
+#include "apr_thread_mutex.h"
+#include "apr_poll.h"
+
+/* ### should be tossed in favor of APR */
+#include <sys/stat.h>
+#include <sys/un.h> /* for sockaddr_un */
+
+#ifdef TCN_DO_STATISTICS
+#include "apr_atomic.h"
+
+static volatile apr_uint32_t uxp_created  = 0;
+static volatile apr_uint32_t uxp_closed   = 0;
+static volatile apr_uint32_t uxp_cleared  = 0;
+static volatile apr_uint32_t uxp_accepted = 0;
+
+void uxp_network_dump_statistics()
+{
+    fprintf(stderr, "NT Network Statistics ..\n");
+    fprintf(stderr, "Sockets created         : %d\n", uxp_created);
+    fprintf(stderr, "Sockets accepted        : %d\n", uxp_accepted);
+    fprintf(stderr, "Sockets closed          : %d\n", uxp_closed);
+    fprintf(stderr, "Sockets cleared         : %d\n", uxp_cleared);
+}
+
+#endif
+
+#define DEFNAME     "/var/run/tomcatnativesock"
+#define DEFNAME_FMT "/var/run/tomcatnativesock%08x%08x"
+#define DEFSIZE     8192
+#define DEFTIMEOUT  60000
+
+#define TCN_UXP_UNKNOWN     0
+#define TCN_UXP_CLIENT      1
+#define TCN_UXP_ACCEPTED    2
+#define TCN_UXP_SERVER      3
+
+#define TCN_UNIX_MAXPATH    1024
+typedef struct {
+    apr_pool_t          *pool;
+    apr_socket_t        *sock;               /* APR socket */
+    int                 sd;
+    struct sockaddr_un  uxaddr;
+    int                 timeout;
+    int                 mode;                 /* Client or server mode */
+    char                name[TCN_UNIX_MAXPATH+1];
+} tcn_uxp_conn_t;
+
+static apr_status_t APR_THREAD_FUNC
+uxp_socket_timeout_set(apr_socket_t *sock, apr_interval_time_t t)
+{
+    tcn_uxp_conn_t *con = (tcn_uxp_conn_t *)sock;
+    if (t < 0)
+        con->timeout = -1;
+    else
+        con->timeout = (int)(apr_time_as_msec(t));
+    return APR_SUCCESS;
+}
+
+static apr_status_t APR_THREAD_FUNC
+uxp_socket_timeout_get(apr_socket_t *sock, apr_interval_time_t *t)
+{
+    tcn_uxp_conn_t *con = (tcn_uxp_conn_t*)sock;
+    if (con->timeout < 0)
+        *t = -1;
+    else
+        *t = con->timeout * 1000;
+    return APR_SUCCESS;
+}
+
+static APR_INLINE apr_status_t APR_THREAD_FUNC
+uxp_socket_opt_set(apr_socket_t *sock, apr_int32_t opt, apr_int32_t on)
+{
+    tcn_uxp_conn_t *con = (tcn_uxp_conn_t *)sock;
+    return apr_socket_opt_set(con->sock, opt, on);
+}
+
+static APR_INLINE apr_status_t APR_THREAD_FUNC
+uxp_socket_opt_get(apr_socket_t *sock, apr_int32_t opt, apr_int32_t *on)
+{
+    tcn_uxp_conn_t *con = (tcn_uxp_conn_t *)sock;
+    return apr_socket_opt_get(con->sock, opt, on);
+}
+
+static apr_status_t uxp_cleanup(void *data)
+{
+    tcn_uxp_conn_t *con = (tcn_uxp_conn_t *)data;
+
+    if (con) {
+        if (con->sock) {
+            apr_socket_close(con->sock);
+            con->sock = NULL;
+        }
+        if (con->mode == TCN_UXP_SERVER) {
+            unlink(con->name);
+            con->mode = TCN_UXP_UNKNOWN;
+        }
+    }
+
+#ifdef TCN_DO_STATISTICS
+    apr_atomic_inc32(&uxp_cleared);
+#endif
+    return APR_SUCCESS;
+}
+
+static apr_status_t APR_THREAD_FUNC
+uxp_socket_shutdown(apr_socket_t *sock, apr_shutdown_how_e how)
+{
+    tcn_uxp_conn_t *con = (tcn_uxp_conn_t *)sock;
+    return apr_socket_shutdown(con->sock, how);
+}
+
+static apr_status_t APR_THREAD_FUNC
+uxp_socket_close(apr_socket_t *sock)
+{
+#ifdef TCN_DO_STATISTICS
+    apr_atomic_inc32(&uxp_closed);
+#endif
+    return uxp_cleanup(sock);
+}
+
+static apr_status_t APR_THREAD_FUNC
+uxp_socket_recv(apr_socket_t *sock, char *buf, apr_size_t *len)
+{
+    tcn_uxp_conn_t *con = (tcn_uxp_conn_t *)sock;
+    return apr_socket_recv(con->sock, buf, len);
+}
+
+
+static apr_status_t APR_THREAD_FUNC
+uxp_socket_send(apr_socket_t *sock, const char *buf,
+                apr_size_t *len)
+{
+    tcn_uxp_conn_t *con = (tcn_uxp_conn_t *)sock;
+    return apr_socket_send(con->sock, buf, len);
+}
+
+static apr_status_t APR_THREAD_FUNC
+uxp_socket_sendv(apr_socket_t *sock,
+                 const struct iovec *vec,
+                 apr_int32_t nvec, apr_size_t *len)
+{
+    tcn_uxp_conn_t *con = (tcn_uxp_conn_t *)sock;
+    return apr_socket_sendv(con->sock, vec, nvec, len);
+}
+
+static apr_status_t uxp_socket_cleanup(void *data)
+{
+    tcn_socket_t *s = (tcn_socket_t *)data;
+
+    if (s->net->cleanup) {
+        (*s->net->cleanup)(s->opaque);
+        s->net->cleanup = NULL;
+    }
+#ifdef TCN_DO_STATISTICS
+    apr_atomic_inc32(&uxp_cleared);
+#endif
+    return APR_SUCCESS;
+}
+
+static tcn_nlayer_t uxp_socket_layer = {
+    TCN_SOCKET_UNIX,
+    uxp_cleanup,
+    uxp_socket_close,
+    uxp_socket_shutdown,
+    uxp_socket_opt_get,
+    uxp_socket_opt_set,
+    uxp_socket_timeout_get,
+    uxp_socket_timeout_set,
+    uxp_socket_send,
+    uxp_socket_sendv,
+    uxp_socket_recv
+};
+
+TCN_IMPLEMENT_CALL(jlong, Local, create)(TCN_STDARGS, jstring name,
+                                         jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    tcn_socket_t   *s   = NULL;
+    tcn_uxp_conn_t *con = NULL;
+    int sd;
+    TCN_ALLOC_CSTRING(name);
+
+    UNREFERENCED(o);
+    TCN_ASSERT(pool != 0);
+
+    if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+        tcn_ThrowAPRException(e, apr_get_netos_error());
+        return 0;
+    }
+#ifdef TCN_DO_STATISTICS
+    uxp_created++;
+#endif
+    con = (tcn_uxp_conn_t *)apr_pcalloc(p, sizeof(tcn_uxp_conn_t));
+    con->pool = p;
+    con->mode = TCN_UXP_UNKNOWN;
+    con->timeout = DEFTIMEOUT;
+    con->sd = sd;
+    con->uxaddr.sun_family = AF_UNIX;
+    if (J2S(name)) {
+        strcpy(con->uxaddr.sun_path, J2S(name));
+        TCN_FREE_CSTRING(name);
+    }
+    else
+        strcpy(con->uxaddr.sun_path, DEFNAME);
+    s = (tcn_socket_t *)apr_pcalloc(p, sizeof(tcn_socket_t));
+    s->pool   = p;
+    s->net    = &uxp_socket_layer;
+    s->opaque = con;
+    apr_pool_cleanup_register(p, (const void *)s,
+                              uxp_socket_cleanup,
+                              apr_pool_cleanup_null);
+
+    apr_os_sock_put(&(con->sock), &(con->sd), p);
+
+    return P2J(s);
+
+}
+
+TCN_IMPLEMENT_CALL(jint, Local, bind)(TCN_STDARGS, jlong sock,
+                                      jlong sa)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(sa);
+    TCN_ASSERT(sock != 0);
+    if (s->net->type == TCN_SOCKET_UNIX) {
+        int rc;
+        tcn_uxp_conn_t *c = (tcn_uxp_conn_t *)s->opaque;
+        c->mode = TCN_UXP_SERVER;
+        rc = bind(c->sd, (struct sockaddr *)&(c->uxaddr), sizeof(c->uxaddr));
+        if (rc < 0)
+            return errno;
+        else
+            return APR_SUCCESS;
+    }
+    else
+        return APR_EINVAL;
+}
+
+TCN_IMPLEMENT_CALL(jint, Local, listen)(TCN_STDARGS, jlong sock,
+                                        jint backlog)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    UNREFERENCED_STDARGS;
+
+    TCN_ASSERT(sock != 0);
+    if (s->net->type == TCN_SOCKET_UNIX) {
+        tcn_uxp_conn_t *c = (tcn_uxp_conn_t *)s->opaque;
+        c->mode = TCN_UXP_SERVER;
+        return apr_socket_listen(c->sock, (apr_int32_t)backlog);
+    }
+    else
+        return APR_EINVAL;
+}
+
+TCN_IMPLEMENT_CALL(jlong, Local, accept)(TCN_STDARGS, jlong sock)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    apr_pool_t   *p = NULL;
+    tcn_socket_t *a = NULL;
+    tcn_uxp_conn_t *con = NULL;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(sock != 0);
+
+    TCN_THROW_IF_ERR(apr_pool_create(&p, s->pool), p);
+    if (s->net->type == TCN_SOCKET_UNIX) {
+        apr_socklen_t len;
+        tcn_uxp_conn_t *c = (tcn_uxp_conn_t *)s->opaque;
+        con = (tcn_uxp_conn_t *)apr_pcalloc(p, sizeof(tcn_uxp_conn_t));
+        con->pool = p;
+        con->mode = TCN_UXP_ACCEPTED;
+        con->timeout = c->timeout;
+        len = sizeof(c->uxaddr);
+        /* Block until a client connects */
+        con->sd = accept(c->sd, (struct sockaddr *)&(con->uxaddr), &len);
+        if (con->sd < 0) {
+            tcn_ThrowAPRException(e, apr_get_os_error());
+            goto cleanup;
+        }
+    }
+    else {
+        tcn_ThrowAPRException(e, APR_ENOTIMPL);
+        goto cleanup;
+    }
+    if (con) {
+#ifdef TCN_DO_STATISTICS
+        apr_atomic_inc32(&uxp_accepted);
+#endif
+        a = (tcn_socket_t *)apr_pcalloc(p, sizeof(tcn_socket_t));
+        a->pool   = p;
+	    a->net    = &uxp_socket_layer;
+        a->opaque = con;
+        apr_pool_cleanup_register(p, (const void *)a,
+                                  uxp_socket_cleanup,
+                                  apr_pool_cleanup_null);
+        apr_os_sock_put(&(con->sock), &(con->sd), p);
+    }
+    return P2J(a);
+cleanup:
+    if (p)
+        apr_pool_destroy(p);
+    return 0;
+}
+
+TCN_IMPLEMENT_CALL(jint, Local, connect)(TCN_STDARGS, jlong sock,
+                                         jlong sa)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    tcn_uxp_conn_t *con = NULL;
+    int rc;
+
+    UNREFERENCED(o);
+    UNREFERENCED(sa);
+    TCN_ASSERT(sock != 0);
+    if (s->net->type != TCN_SOCKET_UNIX)
+        return APR_ENOTSOCK;
+    con = (tcn_uxp_conn_t *)s->opaque;
+    if (con->mode != TCN_UXP_UNKNOWN)
+        return APR_EINVAL;
+    do {
+        rc = connect(con->sd, (const struct sockaddr *)&(con->uxaddr),
+                     sizeof(con->uxaddr));
+    } while (rc == -1 && errno == EINTR);
+
+    if (rc == -1 && errno != EISCONN)
+        return errno;
+    con->mode = TCN_UXP_CLIENT;
+
+    return APR_SUCCESS;
+}
diff --git a/connectors/jni/native/os/win32/apache.ico b/connectors/jni/native/os/win32/apache.ico
new file mode 100644
index 0000000..bfb4f63
--- /dev/null
+++ b/connectors/jni/native/os/win32/apache.ico
Binary files differ
diff --git a/connectors/jni/native/os/win32/libtcnative.rc b/connectors/jni/native/os/win32/libtcnative.rc
new file mode 100644
index 0000000..fafecf1
--- /dev/null
+++ b/connectors/jni/native/os/win32/libtcnative.rc
@@ -0,0 +1,69 @@
+#include <windows.h>
+
+LANGUAGE 0x9,0x1
+1 11 logmessages.bin
+
+#define TCN_COPYRIGHT "Copyright 2000-2006 The Apache Software " \
+                      "Foundation or its licensors, as applicable."
+
+#define TCN_LICENSE "Licensed under the Apache License, Version 2.0 " \
+                    "(the ""License""); you may not use this file except " \
+                    "in compliance with the License.  You may obtain a " \
+                    "copy of the License at\r\n\r\n" \
+                    "http://www.apache.org/licenses/LICENSE-2.0\r\n\r\n" \
+                    "Unless required by applicable law or agreed to in " \
+                    "writing, software distributed under the License is " \
+                    "distributed on an ""AS IS"" BASIS, WITHOUT " \
+                    "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 TCN_VERISON "1.1.11"
+1000 ICON "apache.ico"
+
+1001 DIALOGEX 0, 0, 252, 51
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_CAPTION
+CAPTION "Password prompt"
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+    ICON            1000,-1,8,6,21,20
+    LTEXT           "Some of your private key files are encrypted for security reasons.\nIn order to read them you have to provide the pass phrases.",
+                    -1,29,5,220,19
+    LTEXT           "Enter password:",-1,7,28,75,8
+    EDITTEXT        1002,67,27,174,12,ES_PASSWORD | ES_AUTOHSCROLL
+END
+
+1 VERSIONINFO
+ FILEVERSION 1,1,11,0
+ PRODUCTVERSION 1,1,11,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "Comments",  TCN_LICENSE "\0"
+            VALUE "CompanyName", "Apache Software Foundation\0"
+            VALUE "FileDescription", "Tomcat Native Java Library\0"
+            VALUE "FileVersion", TCN_VERISON "\0"
+            VALUE "InternalName", "libtcnative-1\0"
+            VALUE "LegalCopyright", TCN_COPYRIGHT "\0"
+            VALUE "OriginalFilename", "libtcnative-1.dll\0"
+            VALUE "ProductName", "Tomcat Native Java Library\0"
+            VALUE "ProductVersion", TCN_VERISON "\0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
diff --git a/connectors/jni/native/os/win32/logmessages.bin b/connectors/jni/native/os/win32/logmessages.bin
new file mode 100644
index 0000000..44ce985
--- /dev/null
+++ b/connectors/jni/native/os/win32/logmessages.bin
Binary files differ
diff --git a/connectors/jni/native/os/win32/logmessages.mc b/connectors/jni/native/os/win32/logmessages.mc
new file mode 100644
index 0000000..68f86f6
--- /dev/null
+++ b/connectors/jni/native/os/win32/logmessages.mc
@@ -0,0 +1,41 @@
+MessageId=0x1
+Severity=Error
+SymbolicName=LOG_MSG_EMERG
+Language=English
+Emerg: %1
+.
+
+MessageId=0x2
+Severity=Error
+SymbolicName=LOG_MSG_ERROR
+Language=English
+Error: %1
+.
+
+MessageId=0x3
+Severity=Warning
+SymbolicName=LOG_MSG_NOTICE
+Language=English
+Notice: %1
+.
+
+MessageId=0x4
+Severity=Warning
+SymbolicName=LOG_MSG_WARN
+Language=English
+Warn: %1
+.
+
+MessageId=0x5
+Severity=Informational
+SymbolicName=LOG_MSG_INFO
+Language=English
+Info: %1
+.
+
+MessageId=0x6
+Severity=Success
+SymbolicName=LOG_MSG_DEBUG
+Language=English
+Debug: %1
+.
diff --git a/connectors/jni/native/os/win32/ntpipe.c b/connectors/jni/native/os/win32/ntpipe.c
new file mode 100644
index 0000000..cda69e3
--- /dev/null
+++ b/connectors/jni/native/os/win32/ntpipe.c
@@ -0,0 +1,506 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** NT Pipes network wrapper
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0500
+#endif
+#define STRICT
+#include <winsock2.h>
+#include <mswsock.h>
+#include <ws2tcpip.h>
+#include <sddl.h>
+
+#include "tcn.h"
+#include "apr_thread_mutex.h"
+#include "apr_poll.h"
+
+#ifdef TCN_DO_STATISTICS
+#include "apr_atomic.h"
+
+static volatile apr_uint32_t ntp_created  = 0;
+static volatile apr_uint32_t ntp_closed   = 0;
+static volatile apr_uint32_t ntp_cleared  = 0;
+static volatile apr_uint32_t ntp_accepted = 0;
+
+void ntp_network_dump_statistics()
+{
+    fprintf(stderr, "NT Network Statistics ..\n");
+    fprintf(stderr, "Sockets created         : %d\n", ntp_created);
+    fprintf(stderr, "Sockets accepted        : %d\n", ntp_accepted);
+    fprintf(stderr, "Sockets closed          : %d\n", ntp_closed);
+    fprintf(stderr, "Sockets cleared         : %d\n", ntp_cleared);
+}
+
+#endif
+
+#define DEFNAME     "\\\\.\\PIPE\\TOMCATNATIVEPIPE"
+#define DEFNAME_FMT "\\\\.\\PIPE\\TOMCATNATIVEPIPE%08X%08X"
+#define DEFSIZE     8192
+#define DEFTIMEOUT  60000
+
+#define TCN_NTP_UNKNOWN 0
+#define TCN_NTP_CLIENT  1
+#define TCN_NTP_SERVER  2
+
+typedef struct {
+    apr_pool_t     *pool;
+    apr_socket_t   *sock;               /* Dummy socket */
+    OVERLAPPED     rd_o;
+    OVERLAPPED     wr_o;
+    HANDLE         h_pipe;
+    HANDLE         rd_event;
+    HANDLE         wr_event;
+    DWORD          timeout;
+    int            mode;                 /* Client or server mode */
+    int            nmax;
+    DWORD          sndbuf;
+    DWORD          rcvbuf;
+    char           name[MAX_PATH+1];
+    SECURITY_ATTRIBUTES sa;
+} tcn_ntp_conn_t;
+
+static const char *NTSD_STRING = "D:"     /* Discretionary ACL */
+                   "(D;OICI;GA;;;BG)"     /* Deny access to Built-in Guests */
+                   "(D;OICI;GA;;;AN)"     /* Deny access to Anonymous Logon */
+                   "(A;OICI;GRGWGX;;;AU)" /* Allow read/write/execute to Authenticated Users */
+                   "(A;OICI;GA;;;BA)"     /* Allow full control to Administrators */
+                   "(A;OICI;GA;;;LS)"     /* Allow full control to Local service account */
+                   "(A;OICI;GA;;;SY)";    /* Allow full control to Local system */
+
+
+
+static apr_status_t APR_THREAD_FUNC
+ntp_socket_timeout_set(apr_socket_t *sock, apr_interval_time_t t)
+{
+    tcn_ntp_conn_t *con = (tcn_ntp_conn_t *)sock;
+    if (t < 0)
+        con->timeout = INFINITE;
+    else
+        con->timeout = (DWORD)(apr_time_as_msec(t));
+    return APR_SUCCESS;
+}
+
+static apr_status_t APR_THREAD_FUNC
+ntp_socket_timeout_get(apr_socket_t *sock, apr_interval_time_t *t)
+{
+    tcn_ntp_conn_t *con = (tcn_ntp_conn_t*)sock;
+    if (con->timeout == INFINITE)
+        *t = -1;
+    else
+        *t = con->timeout * 1000;
+    return APR_SUCCESS;
+}
+
+static APR_INLINE apr_status_t APR_THREAD_FUNC
+ntp_socket_opt_set(apr_socket_t *sock, apr_int32_t opt, apr_int32_t on)
+{
+    tcn_ntp_conn_t *con = (tcn_ntp_conn_t *)sock;
+    apr_status_t rv = APR_SUCCESS;
+    switch (opt) {
+        case APR_SO_SNDBUF:
+            con->sndbuf = (DWORD)on;
+        break;
+        case APR_SO_RCVBUF:
+            con->rcvbuf = (DWORD)on;
+        break;
+        default:
+            rv = APR_EINVAL;
+        break;
+    }
+    return rv;
+}
+
+static APR_INLINE apr_status_t APR_THREAD_FUNC
+ntp_socket_opt_get(apr_socket_t *sock, apr_int32_t opt, apr_int32_t *on)
+{
+    tcn_ntp_conn_t *con = (tcn_ntp_conn_t *)sock;
+    apr_status_t rv = APR_SUCCESS;
+    switch (opt) {
+        case APR_SO_SNDBUF:
+            *on = con->sndbuf;
+        break;
+        case APR_SO_RCVBUF:
+            *on = con->rcvbuf;
+        break;
+        default:
+            rv = APR_EINVAL;
+        break;
+    }
+    return rv;
+}
+
+static apr_status_t ntp_cleanup(void *data)
+{
+    tcn_ntp_conn_t *con = (tcn_ntp_conn_t *)data;
+
+    if (con) {
+        if (con->h_pipe) {
+            FlushFileBuffers(con->h_pipe);
+            CloseHandle(con->h_pipe);
+            con->h_pipe = NULL;
+        }
+        if (con->rd_event) {
+            CloseHandle(con->rd_event);
+            con->rd_event = NULL;
+        }
+        if (con->wr_event) {
+            CloseHandle(con->wr_event);
+            con->wr_event= NULL;
+        }
+    }
+
+#ifdef TCN_DO_STATISTICS
+    apr_atomic_inc32(&ntp_cleared);
+#endif
+    return APR_SUCCESS;
+}
+
+static apr_status_t APR_THREAD_FUNC
+ntp_socket_shutdown(apr_socket_t *sock, apr_shutdown_how_e how)
+{
+    UNREFERENCED(how);
+    return ntp_cleanup(sock);;
+}
+
+static apr_status_t APR_THREAD_FUNC
+ntp_socket_close(apr_socket_t *sock)
+{
+#ifdef TCN_DO_STATISTICS
+    apr_atomic_inc32(&ntp_closed);
+#endif
+    return ntp_cleanup(sock);;
+}
+
+static apr_status_t APR_THREAD_FUNC
+ntp_socket_recv(apr_socket_t *sock, char *buf, apr_size_t *len)
+{
+    tcn_ntp_conn_t *con = (tcn_ntp_conn_t *)sock;
+    DWORD readed;
+
+    if (!ReadFile(con->h_pipe, buf, *len, &readed, &con->rd_o)) {
+        DWORD err = GetLastError();
+        if (err == ERROR_IO_PENDING) {
+            DWORD r = WaitForSingleObject(con->rd_event, con->timeout);
+            if (r == WAIT_TIMEOUT)
+                return APR_TIMEUP;
+            else if (r != WAIT_OBJECT_0)
+                return APR_EOF;
+        }
+        else if (err == ERROR_BROKEN_PIPE || err == ERROR_NO_DATA) {
+            /* Server closed the pipe */
+            return APR_EOF;
+        }
+        GetOverlappedResult(con->h_pipe, &con->rd_o, &readed, FALSE);
+    }
+    *len = readed;
+    return APR_SUCCESS;
+}
+
+static apr_status_t APR_THREAD_FUNC
+ntp_socket_send(apr_socket_t *sock, const char *buf,
+                apr_size_t *len)
+{
+    tcn_ntp_conn_t *con = (tcn_ntp_conn_t *)sock;
+    DWORD written;
+
+    if (!WriteFile(con->h_pipe, buf, *len, &written, &con->wr_o)) {
+        DWORD err = GetLastError();
+        if (err == ERROR_IO_PENDING) {
+            DWORD r = WaitForSingleObject(con->wr_event, con->timeout);
+            if (r == WAIT_TIMEOUT)
+                return APR_TIMEUP;
+            else if (r != WAIT_OBJECT_0)
+                return APR_EOF;
+        }
+        else if (err == ERROR_BROKEN_PIPE || err == ERROR_NO_DATA) {
+            /* Server closed the pipe */
+            return APR_EOF;
+        }
+        GetOverlappedResult(con->h_pipe, &con->wr_o, &written, FALSE);
+    }
+    *len = written;
+    return APR_SUCCESS;
+}
+
+static apr_status_t APR_THREAD_FUNC
+ntp_socket_sendv(apr_socket_t *sock,
+                 const struct iovec *vec,
+                 apr_int32_t nvec, apr_size_t *len)
+{
+    tcn_ntp_conn_t *con = (tcn_ntp_conn_t *)sock;
+    apr_status_t rv;
+    apr_size_t written = 0;
+    apr_int32_t i;
+
+    for (i = 0; i < nvec; i++) {
+        apr_size_t rd = vec[i].iov_len;
+        if ((rv = ntp_socket_send((apr_socket_t *)con,
+                                  vec[i].iov_base, &rd)) != APR_SUCCESS) {
+            *len = written;
+            return rv;
+        }
+        written += rd;
+    }
+    *len = written;
+    return APR_SUCCESS;
+}
+
+static apr_status_t ntp_socket_cleanup(void *data)
+{
+    tcn_socket_t *s = (tcn_socket_t *)data;
+
+    if (s->net->cleanup) {
+        (*s->net->cleanup)(s->opaque);
+        s->net->cleanup = NULL;
+    }
+#ifdef TCN_DO_STATISTICS
+    apr_atomic_inc32(&ntp_cleared);
+#endif
+    return APR_SUCCESS;
+}
+
+static tcn_nlayer_t ntp_socket_layer = {
+    TCN_SOCKET_NTPIPE,
+    ntp_cleanup,
+    ntp_socket_close,
+    ntp_socket_shutdown,
+    ntp_socket_opt_get,
+    ntp_socket_opt_set,
+    ntp_socket_timeout_get,
+    ntp_socket_timeout_set,
+    ntp_socket_send,
+    ntp_socket_sendv,
+    ntp_socket_recv
+};
+
+static BOOL create_DACL(LPSECURITY_ATTRIBUTES psa)
+{
+
+    return ConvertStringSecurityDescriptorToSecurityDescriptor(
+                NTSD_STRING,
+                SDDL_REVISION_1,
+                &(psa->lpSecurityDescriptor),
+                NULL);
+}
+
+TCN_IMPLEMENT_CALL(jlong, Local, create)(TCN_STDARGS, jstring name,
+                                         jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    tcn_socket_t   *s   = NULL;
+    tcn_ntp_conn_t *con = NULL;
+    TCN_ALLOC_CSTRING(name);
+
+    UNREFERENCED(o);
+    TCN_ASSERT(pool != 0);
+
+#ifdef TCN_DO_STATISTICS
+    ntp_created++;
+#endif
+    con = (tcn_ntp_conn_t *)apr_pcalloc(p, sizeof(tcn_ntp_conn_t));
+    con->pool = p;
+    con->mode = TCN_NTP_UNKNOWN;
+    con->nmax = PIPE_UNLIMITED_INSTANCES;
+    con->timeout = DEFTIMEOUT;
+    con->sndbuf  = DEFSIZE;
+    con->rcvbuf  = DEFSIZE;
+    if (J2S(name)) {
+        strncpy(con->name, J2S(name), MAX_PATH);
+        con->name[MAX_PATH] = '\0';
+        TCN_FREE_CSTRING(name);
+    }
+    else
+        strcpy(con->name, DEFNAME);
+    con->sa.nLength = sizeof(con->sa);
+    con->sa.bInheritHandle = TRUE;
+    if (!create_DACL(&con->sa)) {
+        tcn_ThrowAPRException(e, apr_get_os_error());
+        return 0;
+    }
+
+    s = (tcn_socket_t *)apr_pcalloc(p, sizeof(tcn_socket_t));
+    s->pool   = p;
+    s->net    = &ntp_socket_layer;
+    s->opaque = con;
+    apr_pool_cleanup_register(p, (const void *)s,
+                              ntp_socket_cleanup,
+                              apr_pool_cleanup_null);
+
+    fflush(stderr);
+    return P2J(s);
+
+}
+
+TCN_IMPLEMENT_CALL(jint, Local, bind)(TCN_STDARGS, jlong sock,
+                                      jlong sa)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(sa);
+    TCN_ASSERT(sock != 0);
+    if (s->net->type == TCN_SOCKET_NTPIPE) {
+        tcn_ntp_conn_t *c = (tcn_ntp_conn_t *)s->opaque;
+        c->mode = TCN_NTP_SERVER;
+        return APR_SUCCESS;
+    }
+    else
+        return APR_EINVAL;
+}
+
+TCN_IMPLEMENT_CALL(jint, Local, listen)(TCN_STDARGS, jlong sock,
+                                        jint backlog)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    UNREFERENCED_STDARGS;
+
+    TCN_ASSERT(sock != 0);
+    if (s->net->type == TCN_SOCKET_NTPIPE) {
+        tcn_ntp_conn_t *c = (tcn_ntp_conn_t *)s->opaque;
+        c->mode = TCN_NTP_SERVER;
+        if (backlog > 0)
+            c->nmax = backlog;
+        else
+            c->nmax = PIPE_UNLIMITED_INSTANCES;
+        return APR_SUCCESS;
+    }
+    else
+        return APR_EINVAL;
+}
+
+TCN_IMPLEMENT_CALL(jlong, Local, accept)(TCN_STDARGS, jlong sock)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    apr_pool_t   *p = NULL;
+    tcn_socket_t *a = NULL;
+    tcn_ntp_conn_t *con = NULL;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(sock != 0);
+
+    TCN_THROW_IF_ERR(apr_pool_create(&p, s->pool), p);
+    if (s->net->type == TCN_SOCKET_NTPIPE) {
+        tcn_ntp_conn_t *c = (tcn_ntp_conn_t *)s->opaque;
+        con = (tcn_ntp_conn_t *)apr_pcalloc(p, sizeof(tcn_ntp_conn_t));
+        con->pool = p;
+        con->mode = TCN_NTP_SERVER;
+        con->nmax = c->nmax;
+        con->timeout = c->timeout;
+        strcpy(con->name, c->name);
+        con->h_pipe = CreateNamedPipe(con->name,
+                                      PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+                                      PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
+                                      con->nmax,
+                                      con->sndbuf,
+                                      con->rcvbuf,
+                                      con->timeout,
+                                      &c->sa);
+        if (con->h_pipe == INVALID_HANDLE_VALUE) {
+            tcn_ThrowAPRException(e, apr_get_os_error());
+            goto cleanup;
+        }
+        /* Block until a client connects */
+        if (!ConnectNamedPipe(con->h_pipe, NULL)) {
+            DWORD err = GetLastError();
+            if (err != ERROR_PIPE_CONNECTED) {
+                CloseHandle(con->h_pipe);
+                tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(err));
+                goto cleanup;
+            }
+        }
+        /* Create overlapped events */
+        con->rd_event    = CreateEvent(NULL, TRUE, FALSE, NULL);
+        con->rd_o.hEvent = con->rd_event;
+        con->wr_event    = CreateEvent(NULL, TRUE, FALSE, NULL);
+        con->wr_o.hEvent = con->wr_event;
+    }
+    else {
+        tcn_ThrowAPRException(e, APR_ENOTIMPL);
+        goto cleanup;
+    }
+    if (con) {
+#ifdef TCN_DO_STATISTICS
+        apr_atomic_inc32(&ntp_accepted);
+#endif
+        a = (tcn_socket_t *)apr_pcalloc(p, sizeof(tcn_socket_t));
+        a->pool   = p;
+        a->net    = &ntp_socket_layer;
+        a->opaque = con;
+        apr_pool_cleanup_register(p, (const void *)a,
+                                  ntp_socket_cleanup,
+                                  apr_pool_cleanup_null);
+    }
+    return P2J(a);
+cleanup:
+    if (p)
+        apr_pool_destroy(p);
+    return 0;
+}
+
+TCN_IMPLEMENT_CALL(jint, Local, connect)(TCN_STDARGS, jlong sock,
+                                         jlong sa)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    apr_pool_t   *p = NULL;
+    tcn_socket_t *a = NULL;
+    tcn_ntp_conn_t *con = NULL;
+
+    UNREFERENCED(o);
+    UNREFERENCED(sa);
+    TCN_ASSERT(sock != 0);
+    if (s->net->type != TCN_SOCKET_NTPIPE)
+        return APR_ENOTSOCK;
+    con = (tcn_ntp_conn_t *)s->opaque;
+    if (con->mode == TCN_NTP_SERVER)
+        return APR_EINVAL;
+    con->mode = TCN_NTP_CLIENT;
+
+    while (TRUE) {
+        con->h_pipe = CreateFile(con->name,
+                                 GENERIC_WRITE | GENERIC_READ,
+                                 FILE_SHARE_READ | FILE_SHARE_WRITE ,
+                                 NULL,
+                                 OPEN_EXISTING,
+                                 FILE_FLAG_OVERLAPPED,
+                                 NULL);
+        if (con->h_pipe != INVALID_HANDLE_VALUE)
+            break;
+        if (GetLastError() == ERROR_PIPE_BUSY) {
+            /* All pipe instances are busy, so wait for
+             * timeout value specified by the server process in
+             * the CreateNamedPipe function.
+             */
+            if (!WaitNamedPipe(con->name, NMPWAIT_USE_DEFAULT_WAIT))
+                return apr_get_os_error();
+        }
+        else
+            return apr_get_os_error();
+    }
+
+    /* Create overlapped events */
+    con->rd_event    = CreateEvent(NULL, TRUE, FALSE, NULL);
+    con->rd_o.hEvent = con->rd_event;
+    con->wr_event    = CreateEvent(NULL, TRUE, FALSE, NULL);
+    con->wr_o.hEvent = con->wr_event;
+
+    return APR_SUCCESS;
+}
diff --git a/connectors/jni/native/os/win32/registry.c b/connectors/jni/native/os/win32/registry.c
new file mode 100644
index 0000000..3f8b3e3
--- /dev/null
+++ b/connectors/jni/native/os/win32/registry.c
@@ -0,0 +1,789 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0500
+#endif
+#include <winsock2.h>
+#include <mswsock.h>
+#include <ws2tcpip.h>
+#include <shlwapi.h>
+
+#include "apr.h"
+#include "apr_pools.h"
+#include "apr_arch_misc.h"   /* for apr_os_level */
+#include "apr_arch_atime.h"  /* for FileTimeToAprTime */
+
+#include "tcn.h"
+
+#define SAFE_CLOSE_KEY(k)                               \
+    if ((k) != NULL && (k) != INVALID_HANDLE_VALUE) {   \
+        RegCloseKey((k));                               \
+        (k) = NULL;                                     \
+    }
+
+typedef struct {
+    apr_pool_t     *pool;
+    HKEY           root;
+    HKEY           key;
+} tcn_nt_registry_t;
+
+
+#define TCN_HKEY_CLASSES_ROOT       1
+#define TCN_HKEY_CURRENT_CONFIG     2
+#define TCN_HKEY_CURRENT_USER       3
+#define TCN_HKEY_LOCAL_MACHINE      4
+#define TCN_HKEY_USERS              5
+
+static const struct {
+    HKEY k;
+} TCN_KEYS[] = {
+    INVALID_HANDLE_VALUE,
+    HKEY_CLASSES_ROOT,
+    HKEY_CURRENT_CONFIG,
+    HKEY_CURRENT_USER,
+    HKEY_LOCAL_MACHINE,
+    HKEY_USERS,
+    INVALID_HANDLE_VALUE
+};
+
+#define TCN_KEY_ALL_ACCESS          0x0001
+#define TCN_KEY_CREATE_LINK         0x0002
+#define TCN_KEY_CREATE_SUB_KEY      0x0004
+#define TCN_KEY_ENUMERATE_SUB_KEYS  0x0008
+#define TCN_KEY_EXECUTE             0x0010
+#define TCN_KEY_NOTIFY              0x0020
+#define TCN_KEY_QUERY_VALUE         0x0040
+#define TCN_KEY_READ                0x0080
+#define TCN_KEY_SET_VALUE           0x0100
+#define TCN_KEY_WOW64_64KEY         0x0200
+#define TCN_KEY_WOW64_32KEY         0x0400
+#define TCN_KEY_WRITE               0x0800
+
+#define TCN_REGSAM(s, x)                    \
+        s = 0;                              \
+        if (x & TCN_KEY_ALL_ACCESS)         \
+            s |= KEY_ALL_ACCESS;            \
+        if (x & TCN_KEY_CREATE_LINK)        \
+            s |= KEY_CREATE_LINK;           \
+        if (x & TCN_KEY_CREATE_SUB_KEY)     \
+            s |= KEY_CREATE_SUB_KEY;        \
+        if (x & TCN_KEY_ENUMERATE_SUB_KEYS) \
+            s |= KEY_ENUMERATE_SUB_KEYS;    \
+        if (x & TCN_KEY_EXECUTE)            \
+            s |= KEY_EXECUTE;               \
+        if (x & TCN_KEY_NOTIFY)             \
+            s |= KEY_NOTIFY;                \
+        if (x & TCN_KEY_READ)               \
+            s |= KEY_READ;                  \
+        if (x & TCN_KEY_SET_VALUE)          \
+            s |= KEY_SET_VALUE;             \
+        if (x & TCN_KEY_WOW64_64KEY)        \
+            s |= KEY_WOW64_64KEY;           \
+        if (x & TCN_KEY_WOW64_32KEY)        \
+            s |= KEY_WOW64_32KEY;           \
+        if (x & TCN_KEY_WRITE)              \
+            s |= KEY_WRITE
+
+#define TCN_REG_BINARY              1
+#define TCN_REG_DWORD               2
+#define TCN_REG_EXPAND_SZ           3
+#define TCN_REG_MULTI_SZ            4
+#define TCN_REG_QWORD               5
+#define TCN_REG_SZ                  6
+
+static const struct {
+    DWORD t;
+} TCN_REGTYPES[] = {
+    REG_NONE,
+    REG_BINARY,
+    REG_DWORD,
+    REG_EXPAND_SZ,
+    REG_MULTI_SZ,
+    REG_QWORD,
+    REG_SZ,
+    REG_NONE
+};
+
+static apr_status_t registry_cleanup(void *data)
+{
+    tcn_nt_registry_t *reg = (tcn_nt_registry_t *)data;
+
+    if (reg) {
+        SAFE_CLOSE_KEY(reg->key);
+    }
+    return APR_SUCCESS;
+}
+
+TCN_IMPLEMENT_CALL(jlong, Registry, create)(TCN_STDARGS, jint root, jstring name,
+                                            jint sam, jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    tcn_nt_registry_t *reg = NULL;
+    TCN_ALLOC_WSTRING(name);
+    HKEY key;
+    LONG rc;
+    REGSAM s;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(pool != 0);
+
+    if (root < TCN_HKEY_CLASSES_ROOT || root > TCN_HKEY_USERS) {
+        tcn_ThrowException(e, "Invalid Registry Root Key");
+        goto cleanup;
+    }
+    if (sam < TCN_KEY_ALL_ACCESS || root > TCN_KEY_WRITE) {
+        tcn_ThrowException(e, "Invalid Registry Key Security");
+        goto cleanup;
+    }
+    reg = (tcn_nt_registry_t *)apr_palloc(p, sizeof(tcn_nt_registry_t));
+    reg->pool = p;
+    reg->root = TCN_KEYS[root].k;
+    reg->key  = NULL;
+    TCN_INIT_WSTRING(name);
+    TCN_REGSAM(s, sam);
+    rc = RegCreateKeyExW(reg->root, J2W(name), 0, NULL, REG_OPTION_NON_VOLATILE,
+                         s, NULL, &key, NULL);
+    if (rc !=  ERROR_SUCCESS) {
+        tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(rc));
+        goto cleanup;
+    }
+    reg->key = key;
+    apr_pool_cleanup_register(p, (const void *)reg,
+                              registry_cleanup,
+                              apr_pool_cleanup_null);
+
+cleanup:
+    TCN_FREE_WSTRING(name);
+    return P2J(reg);
+}
+
+TCN_IMPLEMENT_CALL(jlong, Registry, open)(TCN_STDARGS, jint root, jstring name,
+                                          jint sam, jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    tcn_nt_registry_t *reg = NULL;
+    TCN_ALLOC_WSTRING(name);
+    HKEY key;
+    LONG rc;
+    REGSAM s;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(pool != 0);
+
+    if (root < TCN_HKEY_CLASSES_ROOT || root > TCN_HKEY_USERS) {
+        tcn_ThrowException(e, "Invalid Registry Root Key");
+        goto cleanup;
+    }
+    if (sam < TCN_KEY_ALL_ACCESS || root > TCN_KEY_WRITE) {
+        tcn_ThrowException(e, "Invalid Registry Key Security");
+        goto cleanup;
+    }
+    reg = (tcn_nt_registry_t *)apr_palloc(p, sizeof(tcn_nt_registry_t));
+    reg->pool = p;
+    reg->root = TCN_KEYS[root].k;
+    reg->key  = NULL;
+    TCN_INIT_WSTRING(name);
+    TCN_REGSAM(s, sam);
+    rc = RegOpenKeyExW(reg->root, J2W(name), 0, s, &key);
+    if (rc !=  ERROR_SUCCESS) {
+        tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(rc));
+        goto cleanup;
+    }
+    reg->key = key;
+    apr_pool_cleanup_register(p, (const void *)reg,
+                              registry_cleanup,
+                              apr_pool_cleanup_null);
+
+cleanup:
+    TCN_FREE_WSTRING(name);
+    return P2J(reg);
+}
+
+TCN_IMPLEMENT_CALL(jint, Registry, close)(TCN_STDARGS, jlong reg)
+{
+    tcn_nt_registry_t *r = J2P(reg, tcn_nt_registry_t *);
+    UNREFERENCED_STDARGS;
+
+    TCN_ASSERT(reg != 0);
+
+    registry_cleanup(r);
+    apr_pool_cleanup_kill(r->pool, r, registry_cleanup);
+    return APR_SUCCESS;
+}
+
+TCN_IMPLEMENT_CALL(jint, Registry, getType)(TCN_STDARGS, jlong key,
+                                            jstring name)
+{
+    tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *);
+    TCN_ALLOC_WSTRING(name);
+    LONG rc;
+    DWORD v;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(key != 0);
+    TCN_INIT_WSTRING(name);
+    rc = RegQueryValueExW(k->key, J2W(name), NULL, &v, NULL, NULL);
+    if (rc != ERROR_SUCCESS)
+        v = -rc;
+    TCN_FREE_WSTRING(name);
+    switch (v) {
+        case REG_BINARY:
+            v = TCN_REG_BINARY;
+            break;
+        case REG_DWORD:
+            v = TCN_REG_DWORD;
+            break;
+        case REG_EXPAND_SZ:
+            v = TCN_REG_EXPAND_SZ;
+            break;
+        case REG_MULTI_SZ:
+            v = TCN_REG_MULTI_SZ;
+            break;
+        case REG_QWORD:
+            v = TCN_REG_QWORD;
+            break;
+        case REG_SZ:
+            v = TCN_REG_SZ;
+            break;
+        case REG_DWORD_BIG_ENDIAN:
+            v = 0;
+            break;
+    }
+    return v;
+}
+
+TCN_IMPLEMENT_CALL(jint, Registry, getSize)(TCN_STDARGS, jlong key,
+                                            jstring name)
+{
+    tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *);
+    TCN_ALLOC_WSTRING(name);
+    LONG rc;
+    DWORD v;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(key != 0);
+    TCN_INIT_WSTRING(name);
+    rc = RegQueryValueExW(k->key, J2W(name), NULL, NULL, NULL, &v);
+    if (rc != ERROR_SUCCESS)
+        v = -rc;
+    TCN_FREE_WSTRING(name);
+    return v;
+}
+
+TCN_IMPLEMENT_CALL(jint, Registry, getValueI)(TCN_STDARGS, jlong key,
+                                              jstring name)
+{
+    tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *);
+    TCN_ALLOC_WSTRING(name);
+    LONG rc;
+    DWORD t, l;
+    DWORD v = 0;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(key != 0);
+    TCN_INIT_WSTRING(name);
+    rc = RegQueryValueExW(k->key, J2W(name), NULL, &t, NULL, &l);
+    if (rc != ERROR_SUCCESS) {
+        tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(rc));
+        goto cleanup;
+    }
+    if (t == REG_DWORD) {
+        l = sizeof(DWORD);
+        rc = RegQueryValueExW(k->key, J2W(name), NULL, NULL, (LPBYTE)&v, &l);
+        if (rc != ERROR_SUCCESS) {
+            tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(rc));
+            goto cleanup;
+        }
+    }
+    else if (t == REG_SZ || t == REG_BINARY ||
+             t == REG_MULTI_SZ || t == REG_EXPAND_SZ)
+        v = l;
+    else {
+        v = 0;
+        tcn_ThrowException(e, "Unable to convert the value to integer");
+    }
+cleanup:
+    TCN_FREE_WSTRING(name);
+    return v;
+}
+
+TCN_IMPLEMENT_CALL(jlong, Registry, getValueJ)(TCN_STDARGS, jlong key,
+                                               jstring name)
+{
+    tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *);
+    TCN_ALLOC_WSTRING(name);
+    LONG rc;
+    DWORD t, l;
+    UINT64 v = 0;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(key != 0);
+    TCN_INIT_WSTRING(name);
+    rc = RegQueryValueExW(k->key, J2W(name), NULL, &t, NULL, &l);
+    if (rc != ERROR_SUCCESS) {
+        tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(rc));
+        goto cleanup;
+    }
+    if (t == REG_DWORD) {
+        DWORD tv;
+        l = sizeof(DWORD);
+        rc = RegQueryValueExW(k->key, J2W(name), NULL, NULL, (LPBYTE)&tv, &l);
+        if (rc != ERROR_SUCCESS) {
+            tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(rc));
+            goto cleanup;
+        }
+        v = tv;
+    }
+    else if (t == REG_QWORD) {
+        l = sizeof(UINT64);
+        rc = RegQueryValueExW(k->key, J2W(name), NULL, NULL, (LPBYTE)&v, &l);
+        if (rc != ERROR_SUCCESS) {
+            tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(rc));
+            goto cleanup;
+        }
+    }
+    else if (t == REG_SZ || t == REG_BINARY ||
+             t == REG_MULTI_SZ || t == REG_EXPAND_SZ)
+        v = l;
+    else {
+        v = 0;
+        tcn_ThrowException(e, "Unable to convert the value to long");
+    }
+cleanup:
+    TCN_FREE_WSTRING(name);
+    return v;
+}
+
+TCN_IMPLEMENT_CALL(jstring, Registry, getValueS)(TCN_STDARGS, jlong key,
+                                                 jstring name)
+{
+    tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *);
+    TCN_ALLOC_WSTRING(name);
+    LONG rc;
+    DWORD t, l;
+    jstring v = NULL;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(key != 0);
+    TCN_INIT_WSTRING(name);
+    rc = RegQueryValueExW(k->key, J2W(name), NULL, &t, NULL, &l);
+    if (rc != ERROR_SUCCESS) {
+        tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(rc));
+        goto cleanup;
+    }
+    if (t == REG_SZ || t == REG_EXPAND_SZ) {
+        jchar *vw = (jchar *)malloc(l);
+        rc = RegQueryValueExW(k->key, J2W(name), NULL, NULL, (LPBYTE)vw, &l);
+        if (rc != ERROR_SUCCESS) {
+            tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(rc));
+            free(vw);
+            goto cleanup;
+        }
+        v = (*e)->NewString((e), vw, wcslen(vw));
+        free(vw);
+    }
+cleanup:
+    TCN_FREE_WSTRING(name);
+    return v;
+}
+
+TCN_IMPLEMENT_CALL(jbyteArray, Registry, getValueB)(TCN_STDARGS, jlong key,
+                                                    jstring name)
+{
+    tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *);
+    TCN_ALLOC_WSTRING(name);
+    LONG rc;
+    DWORD t, l;
+    jbyteArray v = NULL;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(key != 0);
+    TCN_INIT_WSTRING(name);
+    rc = RegQueryValueExW(k->key, J2W(name), NULL, &t, NULL, &l);
+    if (rc != ERROR_SUCCESS) {
+        tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(rc));
+        goto cleanup;
+    }
+    if (t == REG_BINARY) {
+        BYTE *b = (BYTE *)malloc(l);
+        rc = RegQueryValueExW(k->key, J2W(name), NULL, NULL, b, &l);
+        if (rc != ERROR_SUCCESS) {
+            tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(rc));
+            free(b);
+            goto cleanup;
+        }
+        v = tcn_new_arrayb(e, b, l);
+        free(b);
+    }
+cleanup:
+    TCN_FREE_WSTRING(name);
+    return v;
+}
+
+static jsize get_multi_sz_count(LPCWSTR str)
+{
+    LPCWSTR p = str;
+    jsize   cnt = 0;
+    for ( ; p && *p; p++) {
+        cnt++;
+        while (*p)
+            p++;
+    }
+    return cnt;
+}
+
+TCN_IMPLEMENT_CALL(jobjectArray, Registry, getValueA)(TCN_STDARGS, jlong key,
+                                                      jstring name)
+{
+    tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *);
+    TCN_ALLOC_WSTRING(name);
+    LONG rc;
+    DWORD t, l;
+    jobjectArray v = NULL;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(key != 0);
+    TCN_INIT_WSTRING(name);
+    rc = RegQueryValueExW(k->key, J2W(name), NULL, &t, NULL, &l);
+    if (rc != ERROR_SUCCESS) {
+        tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(rc));
+        goto cleanup;
+    }
+    if (t == REG_MULTI_SZ) {
+        jsize cnt = 0;
+        jchar *p;
+        jchar *vw = (jchar *)malloc(l);
+        rc = RegQueryValueExW(k->key, J2W(name), NULL, NULL, (LPBYTE)vw, &l);
+        if (rc != ERROR_SUCCESS) {
+            tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(rc));
+            free(vw);
+            goto cleanup;
+        }
+        cnt = get_multi_sz_count(vw);
+        if (cnt) {
+            jsize idx = 0;
+            v = tcn_new_arrays(e, cnt);
+            for (p = vw ; p && *p; p++) {
+                jstring s;
+                jchar *b = p;
+                while (*p)
+                    p++;
+                s = (*e)->NewString((e), b, (jsize)(p - b));
+                (*e)->SetObjectArrayElement((e), v, idx++, s);
+            }
+        }
+        free(vw);
+    }
+cleanup:
+    TCN_FREE_WSTRING(name);
+    return v;
+}
+
+TCN_IMPLEMENT_CALL(jint, Registry, setValueI)(TCN_STDARGS, jlong key,
+                                              jstring name, jint val)
+{
+    tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *);
+    TCN_ALLOC_WSTRING(name);
+    LONG rc;
+    DWORD v = (DWORD)val;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(key != 0);
+    TCN_INIT_WSTRING(name);
+    rc = RegSetValueExW(k->key, J2W(name), 0, REG_DWORD, (CONST BYTE *)&v, sizeof(DWORD));
+    TCN_FREE_WSTRING(name);
+    return v;
+}
+
+TCN_IMPLEMENT_CALL(jint, Registry, setValueJ)(TCN_STDARGS, jlong key,
+                                              jstring name, jlong val)
+{
+    tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *);
+    TCN_ALLOC_WSTRING(name);
+    LONG rc;
+    UINT64 v = (UINT64)val;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(key != 0);
+    TCN_INIT_WSTRING(name);
+    rc = RegSetValueExW(k->key, J2W(name), 0, REG_QWORD, (CONST BYTE *)&v, sizeof(UINT64));
+    TCN_FREE_WSTRING(name);
+    return rc;
+}
+
+TCN_IMPLEMENT_CALL(jint, Registry, setValueS)(TCN_STDARGS, jlong key,
+                                              jstring name, jstring val)
+{
+    tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *);
+    TCN_ALLOC_WSTRING(name);
+    TCN_ALLOC_WSTRING(val);
+    LONG rc;
+    DWORD len;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(key != 0);
+    TCN_INIT_WSTRING(name);
+    TCN_INIT_WSTRING(val);
+    len = wcslen(J2W(val));
+    rc = RegSetValueExW(k->key, J2W(name), 0, REG_SZ,
+                        (CONST BYTE *)J2W(val), (len + 1) * 2);
+    TCN_FREE_WSTRING(name);
+    TCN_FREE_WSTRING(val);
+    return rc;
+}
+
+TCN_IMPLEMENT_CALL(jint, Registry, setValueE)(TCN_STDARGS, jlong key,
+                                              jstring name, jstring val)
+{
+    tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *);
+    TCN_ALLOC_WSTRING(name);
+    TCN_ALLOC_WSTRING(val);
+    LONG rc;
+    DWORD len;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(key != 0);
+    TCN_INIT_WSTRING(name);
+    TCN_INIT_WSTRING(val);
+    len = wcslen(J2W(val));
+    rc = RegSetValueExW(k->key, J2W(name), 0, REG_EXPAND_SZ,
+                        (CONST BYTE *)J2W(val), (len + 1) * 2);
+    TCN_FREE_WSTRING(name);
+    TCN_FREE_WSTRING(val);
+    return rc;
+}
+
+TCN_IMPLEMENT_CALL(jint, Registry, setValueA)(TCN_STDARGS, jlong key,
+                                              jstring name,
+                                              jobjectArray vals)
+{
+    tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *);
+    TCN_ALLOC_WSTRING(name);
+    LONG rc;
+    jsize i, len;
+    jsize sl = 0;
+    jchar *msz, *p;
+    UNREFERENCED(o);
+    TCN_ASSERT(key != 0);
+    TCN_INIT_WSTRING(name);
+    len = (*e)->GetArrayLength((e), vals);
+    for (i = 0; i < len; i++) {
+        jstring s = (jstring)(*e)->GetObjectArrayElement((e), vals, i);
+        sl += (*e)->GetStringLength((e), s) + 1;
+    }
+    sl = (sl + 1) * 2;
+    p = msz = (jchar *)calloc(1, sl);
+    for (i = 0; i < len; i++) {
+        jsize   l;
+        jstring s = (jstring)(*e)->GetObjectArrayElement((e), vals, i);
+        l = (*e)->GetStringLength((e), s);
+        wcsncpy(p, (*e)->GetStringChars(e, s, 0), l);
+        p += l + 1;
+    }
+    rc = RegSetValueExW(k->key, J2W(name), 0, REG_MULTI_SZ,
+                        (CONST BYTE *)msz, sl);
+    TCN_FREE_WSTRING(name);
+    free(msz);
+    return rc;
+}
+
+TCN_IMPLEMENT_CALL(jint, Registry, setValueB)(TCN_STDARGS, jlong key,
+                                              jstring name,
+                                              jbyteArray val)
+{
+    tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *);
+    TCN_ALLOC_WSTRING(name);
+    jsize nbytes = (*e)->GetArrayLength(e, val);
+    jbyte *bytes = (*e)->GetByteArrayElements(e, val, NULL);
+    LONG rc;
+
+    rc = RegSetValueExW(k->key, J2W(name), 0, REG_BINARY,
+                        bytes, (DWORD)nbytes);
+    (*e)->ReleaseByteArrayElements(e, val, bytes, JNI_ABORT);
+    TCN_FREE_WSTRING(name);
+    return rc;
+}
+
+#define MAX_VALUE_NAME 4096
+
+TCN_IMPLEMENT_CALL(jobjectArray, Registry, enumKeys)(TCN_STDARGS, jlong key)
+{
+    tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *);
+    LONG rc;
+    jobjectArray v = NULL;
+    jsize cnt = 0;
+
+    WCHAR    achKey[MAX_PATH];
+    WCHAR    achClass[MAX_PATH] = L"";
+    DWORD    cchClassName = MAX_PATH;
+    DWORD    cSubKeys;
+    DWORD    cbMaxSubKey;
+    DWORD    cchMaxClass;
+    DWORD    cValues;
+    DWORD    cchMaxValue;
+    DWORD    cbMaxValueData;
+    DWORD    cbSecurityDescriptor;
+    FILETIME ftLastWriteTime;
+
+    DWORD cchValue = MAX_VALUE_NAME;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(key != 0);
+    rc = RegQueryInfoKeyW(k->key,
+                          achClass,
+                          &cchClassName,
+                          NULL,
+                          &cSubKeys,
+                          &cbMaxSubKey,
+                          &cchMaxClass,
+                          &cValues,
+                          &cchMaxValue,
+                          &cbMaxValueData,
+                          &cbSecurityDescriptor,
+                          &ftLastWriteTime);
+    if (rc != ERROR_SUCCESS) {
+        tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(rc));
+        goto cleanup;
+    }
+    cnt = cSubKeys;
+    if (cnt) {
+        jsize idx = 0;
+        v = tcn_new_arrays(e, cnt);
+        for (idx = 0; idx < cnt; idx++) {
+            jstring s;
+            DWORD achKeyLen = MAX_PATH;
+            rc = RegEnumKeyExW(k->key,
+                               idx,
+                               achKey,
+                               &achKeyLen,
+                               NULL,
+                               NULL,
+                               NULL,
+                               &ftLastWriteTime);
+            if (rc == (DWORD)ERROR_SUCCESS) {
+                s = (*e)->NewString((e), achKey, wcslen(achKey));
+                (*e)->SetObjectArrayElement((e), v, idx, s);
+            }
+        }
+    }
+cleanup:
+    return v;
+}
+
+TCN_IMPLEMENT_CALL(jobjectArray, Registry, enumValues)(TCN_STDARGS, jlong key)
+{
+    tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *);
+    LONG rc;
+    jobjectArray v = NULL;
+    jsize cnt = 0;
+
+    WCHAR    achClass[MAX_PATH] = L"";
+    DWORD    cchClassName = MAX_PATH;
+    DWORD    cSubKeys;
+    DWORD    cbMaxSubKey;
+    DWORD    cchMaxClass;
+    DWORD    cValues;
+    DWORD    cchMaxValue;
+    DWORD    cbMaxValueData;
+    DWORD    cbSecurityDescriptor;
+    FILETIME ftLastWriteTime;
+
+    WCHAR  achValue[MAX_VALUE_NAME];
+    DWORD  cchValue = MAX_VALUE_NAME;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(key != 0);
+    /* Get the class name and the value count. */
+    rc = RegQueryInfoKeyW(k->key,
+                          achClass,
+                          &cchClassName,
+                          NULL,
+                          &cSubKeys,
+                          &cbMaxSubKey,
+                          &cchMaxClass,
+                          &cValues,
+                          &cchMaxValue,
+                          &cbMaxValueData,
+                          &cbSecurityDescriptor,
+                          &ftLastWriteTime);
+    if (rc != ERROR_SUCCESS) {
+        tcn_ThrowAPRException(e, APR_FROM_OS_ERROR(rc));
+        goto cleanup;
+    }
+    cnt = cValues;
+    if (cnt) {
+        jsize idx = 0;
+        v = tcn_new_arrays(e, cnt);
+        for (idx = 0; idx < cnt; idx++) {
+            jstring s;
+            cchValue = MAX_VALUE_NAME;
+            achValue[0] = '\0';
+            rc = RegEnumValueW(k->key, idx,
+                               achValue,
+                               &cchValue,
+                               NULL,
+                               NULL,    // &dwType,
+                               NULL,    // &bData,
+                               NULL);   // &bcData
+            if (rc == (DWORD)ERROR_SUCCESS) {
+                s = (*e)->NewString((e), achValue, wcslen(achValue));
+                (*e)->SetObjectArrayElement((e), v, idx, s);
+            }
+        }
+    }
+cleanup:
+    return v;
+}
+
+TCN_IMPLEMENT_CALL(jint, Registry, deleteKey)(TCN_STDARGS, jint root, jstring name,
+                                              jboolean only_if_empty)
+{
+    DWORD rv;
+    TCN_ALLOC_WSTRING(name);
+
+    UNREFERENCED(o);
+    if (root < TCN_HKEY_CLASSES_ROOT || root > TCN_HKEY_USERS) {
+        rv = EBADF;
+        goto cleanup;
+    }
+    if (only_if_empty)
+        rv = SHDeleteEmptyKeyW(TCN_KEYS[root].k, J2W(name));
+    else
+        rv = SHDeleteKeyW(TCN_KEYS[root].k, J2W(name));
+cleanup:
+    TCN_FREE_WSTRING(name);
+    return rv;
+}
+
+TCN_IMPLEMENT_CALL(jint, Registry, deleteValue)(TCN_STDARGS, jlong key,
+                                                jstring name)
+{
+    LONG rv;
+    TCN_ALLOC_WSTRING(name);
+    tcn_nt_registry_t *k = J2P(key, tcn_nt_registry_t *);
+
+    UNREFERENCED(o);
+    rv = RegDeleteValueW(k->key, J2W(name));
+    TCN_FREE_WSTRING(name);
+    return (jint)rv;
+}
diff --git a/connectors/jni/native/os/win32/system.c b/connectors/jni/native/os/win32/system.c
new file mode 100644
index 0000000..f2f8404
--- /dev/null
+++ b/connectors/jni/native/os/win32/system.c
@@ -0,0 +1,466 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0500
+#endif
+#include <winsock2.h>
+#include <mswsock.h>
+#include <ws2tcpip.h>
+#include "apr.h"
+#include "apr_pools.h"
+#include "apr_poll.h"
+#include "apr_network_io.h"
+#include "apr_arch_misc.h" /* for apr_os_level */
+#include "apr_arch_atime.h"  /* for FileTimeToAprTime */
+
+#include "tcn.h"
+#ifdef HAVE_OPENSSL
+#include "ssl_private.h"
+#endif
+
+#pragma warning(push)
+#pragma warning(disable : 4201)
+#if (_WIN32_WINNT < 0x0501)
+#include <winternl.h>
+#endif
+#include <psapi.h>
+#pragma warning(pop)
+
+
+static CRITICAL_SECTION dll_critical_section;   /* dll's critical section */
+static HINSTANCE        dll_instance = NULL;
+static SYSTEM_INFO      dll_system_info;
+static HANDLE           h_kernel = NULL;
+static HANDLE           h_ntdll  = NULL;
+static char             dll_file_name[MAX_PATH];
+
+typedef BOOL (WINAPI *pfnGetSystemTimes)(LPFILETIME, LPFILETIME, LPFILETIME);
+static pfnGetSystemTimes fnGetSystemTimes = NULL;
+#if (_WIN32_WINNT < 0x0501)
+typedef NTSTATUS (WINAPI *pfnNtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG);
+static pfnNtQuerySystemInformation fnNtQuerySystemInformation = NULL;
+#endif
+
+BOOL
+WINAPI
+DllMain(
+    HINSTANCE instance,
+    DWORD reason,
+    LPVOID reserved)
+{
+
+    switch (reason) {
+        /** The DLL is loading due to process
+         *  initialization or a call to LoadLibrary.
+         */
+        case DLL_PROCESS_ATTACH:
+            InitializeCriticalSection(&dll_critical_section);
+            dll_instance = instance;
+            GetSystemInfo(&dll_system_info);
+            if ((h_kernel = LoadLibrary("kernel32.dll")) != NULL)
+                fnGetSystemTimes = (pfnGetSystemTimes)GetProcAddress(h_kernel,
+                                                            "GetSystemTimes");
+            if (fnGetSystemTimes == NULL) {
+                FreeLibrary(h_kernel);
+                h_kernel = NULL;
+#if (_WIN32_WINNT < 0x0501)
+                if ((h_ntdll = LoadLibrary("ntdll.dll")) != NULL)
+                    fnNtQuerySystemInformation =
+                        (pfnNtQuerySystemInformation)GetProcAddress(h_ntdll,
+                                                "NtQuerySystemInformation");
+
+                if (fnNtQuerySystemInformation == NULL) {
+                    FreeLibrary(h_ntdll);
+                    h_ntdll = NULL;
+                }
+#endif
+            }
+            GetModuleFileName(instance, dll_file_name, sizeof(dll_file_name));
+            break;
+        /** The attached process creates a new thread.
+         */
+        case DLL_THREAD_ATTACH:
+            break;
+
+        /** The thread of the attached process terminates.
+         */
+        case DLL_THREAD_DETACH:
+            break;
+
+        /** DLL unload due to process termination
+         *  or FreeLibrary.
+         */
+        case DLL_PROCESS_DETACH:
+            if (h_kernel)
+                FreeLibrary(h_kernel);
+            if (h_ntdll)
+                FreeLibrary(h_ntdll);
+            DeleteCriticalSection(&dll_critical_section);
+            break;
+
+        default:
+            break;
+    }
+
+    return TRUE;
+    UNREFERENCED_PARAMETER(reserved);
+}
+
+
+TCN_IMPLEMENT_CALL(jstring, OS, syserror)(TCN_STDARGS, jint err)
+{
+    jstring str;
+    void *buf;
+
+    UNREFERENCED(o);
+    if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+                       FORMAT_MESSAGE_FROM_SYSTEM |
+                       FORMAT_MESSAGE_IGNORE_INSERTS,
+                       NULL,
+                       err,
+                       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                       (LPTSTR)&buf,
+                       0,
+                       NULL)) {
+        str = AJP_TO_JSTRING("Unknown Error");
+    }
+    else {
+        str = AJP_TO_JSTRING((const char *)buf);
+        LocalFree(buf);
+    }
+    return str;
+}
+
+TCN_IMPLEMENT_CALL(jstring, OS, expand)(TCN_STDARGS, jstring val)
+{
+    jstring str;
+    jchar buf[TCN_BUFFER_SZ] = L"";
+    DWORD len;
+    TCN_ALLOC_WSTRING(val);
+
+    UNREFERENCED(o);
+    TCN_INIT_WSTRING(val);
+
+    len = ExpandEnvironmentStringsW(J2W(val), buf, TCN_BUFFER_SZ - 1);
+    if (len > (TCN_BUFFER_SZ - 1)) {
+        jchar *dbuf = malloc((len + 1) * 2);
+        ExpandEnvironmentStringsW(J2W(val), dbuf, len);
+        str = (*e)->NewString(e, dbuf, wcslen(dbuf));
+        free(dbuf);
+    }
+    else
+        str = (*e)->NewString(e, buf, wcslen(buf));
+
+    TCN_FREE_WSTRING(val);
+    return str;
+}
+
+#define LOG_MSG_EMERG                    0xC0000001L
+#define LOG_MSG_ERROR                    0xC0000002L
+#define LOG_MSG_NOTICE                   0x80000003L
+#define LOG_MSG_WARN                     0x80000004L
+#define LOG_MSG_INFO                     0x40000005L
+#define LOG_MSG_DEBUG                    0x00000006L
+#define LOG_MSG_DOMAIN                   "Native"
+
+static char log_domain[MAX_PATH] = "Native";
+
+static void init_log_source(const char *domain)
+{
+    HKEY  key;
+    DWORD ts;
+    char event_key[MAX_PATH];
+
+    strcpy(event_key, "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\");
+    strcat(event_key, domain);
+    if (!RegCreateKey(HKEY_LOCAL_MACHINE, event_key, &key)) {
+        RegSetValueEx(key, "EventMessageFile", 0, REG_SZ, (LPBYTE)&dll_file_name[0],
+                      strlen(dll_file_name) + 1);
+        ts = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE;
+
+        RegSetValueEx(key, "TypesSupported", 0, REG_DWORD, (LPBYTE) &ts, sizeof(DWORD));
+        RegCloseKey(key);
+    }
+    strcpy(log_domain, domain);
+}
+
+TCN_IMPLEMENT_CALL(void, OS, sysloginit)(TCN_STDARGS, jstring domain)
+{
+    const char *d;
+    TCN_ALLOC_CSTRING(domain);
+
+    UNREFERENCED(o);
+
+    if ((d = J2S(domain)) == NULL)
+        d = LOG_MSG_DOMAIN;
+    init_log_source(d);
+
+    TCN_FREE_CSTRING(domain);
+}
+
+TCN_IMPLEMENT_CALL(void, OS, syslog)(TCN_STDARGS, jint level,
+                                     jstring msg)
+{
+    TCN_ALLOC_CSTRING(msg);
+    DWORD id = LOG_MSG_DEBUG;
+    WORD  il = EVENTLOG_SUCCESS;
+    HANDLE  source;
+    const char *messages[1];
+    UNREFERENCED(o);
+
+    switch (level) {
+        case TCN_LOG_EMERG:
+            id = LOG_MSG_EMERG;
+            il = EVENTLOG_ERROR_TYPE;
+        break;
+        case TCN_LOG_ERROR:
+            id = LOG_MSG_ERROR;
+            il = EVENTLOG_ERROR_TYPE;
+        break;
+        case TCN_LOG_NOTICE:
+            id = LOG_MSG_NOTICE;
+            il = EVENTLOG_WARNING_TYPE;
+        break;
+        case TCN_LOG_WARN:
+            id = LOG_MSG_WARN;
+            il = EVENTLOG_WARNING_TYPE;
+        break;
+        case TCN_LOG_INFO:
+            id = LOG_MSG_INFO;
+            il = EVENTLOG_INFORMATION_TYPE;
+        break;
+    }
+
+    messages[0] = J2S(msg);
+    source = RegisterEventSource(NULL, log_domain);
+
+    if (source != NULL) {
+        ReportEvent(source, il,
+                    0,
+                    id,
+                    NULL,
+                    1, 0,
+                    messages, NULL);
+        DeregisterEventSource(source);
+    }
+
+    TCN_FREE_CSTRING(msg);
+}
+
+TCN_IMPLEMENT_CALL(jboolean, OS, is)(TCN_STDARGS, jint type)
+{
+    UNREFERENCED_STDARGS;
+#ifdef _WIN64
+    if (type == 4)
+        return JNI_TRUE;
+    else
+#endif
+    if (type == 3)
+        return JNI_TRUE;
+    else
+        return JNI_FALSE;
+}
+
+TCN_IMPLEMENT_CALL(jint, OS, info)(TCN_STDARGS,
+                                   jlongArray inf)
+{
+    MEMORYSTATUSEX ms;
+    ULONGLONG st[4];
+    FILETIME ft[4];
+    PROCESS_MEMORY_COUNTERS pmc;
+    jint rv;
+    int i;
+    jsize ilen = (*e)->GetArrayLength(e, inf);
+    jlong *pvals = (*e)->GetLongArrayElements(e, inf, NULL);
+
+    if (ilen < 16) {
+        return APR_EINVAL;
+    }
+    for (i = 0; i < 16; i++)
+        pvals[i] = 0;
+
+    ms.dwLength = sizeof(MEMORYSTATUSEX);
+
+    UNREFERENCED(o);
+    if (GlobalMemoryStatusEx(&ms)) {
+        pvals[0] = (jlong)ms.ullTotalPhys;
+        pvals[1] = (jlong)ms.ullAvailPhys;
+        pvals[2] = (jlong)ms.ullTotalPageFile;
+        pvals[3] = (jlong)ms.ullAvailPageFile;
+        /* Slots 4 and 5 are for shared memory */
+        pvals[6] = (jlong)ms.dwMemoryLoad;
+    }
+    else
+        goto cleanup;
+
+    memset(st, 0, sizeof(st));
+
+    if (fnGetSystemTimes) {
+        if ((*fnGetSystemTimes)(&ft[0], &ft[1], &ft[2])) {
+            st[0] = (((ULONGLONG)ft[0].dwHighDateTime << 32) | ft[0].dwLowDateTime) / 10;
+            st[1] = (((ULONGLONG)ft[1].dwHighDateTime << 32) | ft[1].dwLowDateTime) / 10;
+            st[2] = (((ULONGLONG)ft[2].dwHighDateTime << 32) | ft[2].dwLowDateTime) / 10;
+        }
+        else
+            goto cleanup;
+    }
+#if (_WIN32_WINNT < 0x0501)
+    else if (fnNtQuerySystemInformation) {
+        BYTE buf[2048]; /* This should ne enough for 32 processors */
+        NTSTATUS rs = (*fnNtQuerySystemInformation)(SystemProcessorPerformanceInformation,
+                                           (LPVOID)buf, 2048, NULL);
+        if (rs == 0) {
+            PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION pspi = (PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)&buf[0];
+            DWORD i;
+            /* Calculate all processors */
+            for (i = 0; i < dll_system_info.dwNumberOfProcessors; i++) {
+                st[0] += pspi[i].IdleTime.QuadPart / 10;
+                st[1] += pspi[i].KernelTime.QuadPart / 10;
+                st[2] += pspi[i].UserTime.QuadPart / 10;
+            }
+        }
+        else
+            goto cleanup;
+    }
+#endif
+    pvals[7] = st[0];
+    pvals[8] = st[1];
+    pvals[9] = st[2];
+
+    memset(st, 0, sizeof(st));
+    if (GetProcessTimes(GetCurrentProcess(), &ft[0], &ft[1], &ft[2], &ft[3])) {
+        FileTimeToAprTime((apr_time_t *)&st[0], &ft[0]);
+        st[1] = (((ULONGLONG)ft[2].dwHighDateTime << 32) | ft[2].dwLowDateTime) / 10;
+        st[2] = (((ULONGLONG)ft[3].dwHighDateTime << 32) | ft[3].dwLowDateTime) / 10;
+    }
+    pvals[10] = st[0];
+    pvals[11] = st[1];
+    pvals[12] = st[2];
+
+    if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) {
+        pvals[13] = pmc.WorkingSetSize;
+        pvals[14] = pmc.PeakWorkingSetSize;
+        pvals[15] = pmc.PageFaultCount;
+    }
+
+    (*e)->ReleaseLongArrayElements(e, inf, pvals, 0);
+    return APR_SUCCESS;
+cleanup:
+    rv = apr_get_os_error();
+    (*e)->ReleaseLongArrayElements(e, inf, pvals, 0);
+    return rv;
+}
+
+#ifdef HAVE_OPENSSL
+
+static DWORD WINAPI password_thread(void *data)
+{
+    tcn_pass_cb_t *cb = (tcn_pass_cb_t *)data;
+    MSG     msg;
+    HWINSTA hwss;
+    HWINSTA hwsu;
+    HDESK   hwds;
+    HDESK   hwdu;
+    HWND    hwnd;
+
+    /* Ensure connection to service window station and desktop, and
+     * save their handles.
+     */
+    GetDesktopWindow();
+    hwss = GetProcessWindowStation();
+    hwds = GetThreadDesktop(GetCurrentThreadId());
+
+    /* Impersonate the client and connect to the User's
+     * window station and desktop.
+     */
+    hwsu = OpenWindowStation("WinSta0", FALSE, MAXIMUM_ALLOWED);
+    if (hwsu == NULL) {
+        ExitThread(1);
+        return 1;
+    }
+    SetProcessWindowStation(hwsu);
+    hwdu = OpenDesktop("Default", 0, FALSE, MAXIMUM_ALLOWED);
+    if (hwdu == NULL) {
+        SetProcessWindowStation(hwss);
+        CloseWindowStation(hwsu);
+        ExitThread(1);
+        return 1;
+    }
+    SetThreadDesktop(hwdu);
+
+    hwnd = CreateDialog(dll_instance, MAKEINTRESOURCE(1001), NULL, NULL);
+    if (hwnd != NULL)
+        ShowWindow(hwnd, SW_SHOW);
+    else  {
+        ExitThread(1);
+        return 1;
+    }
+    while (1) {
+        if (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE)) {
+            if (msg.message == WM_KEYUP) {
+                int nVirtKey = (int)msg.wParam;
+                if (nVirtKey == VK_ESCAPE) {
+                    DestroyWindow(hwnd);
+                    break;
+                }
+                else if (nVirtKey == VK_RETURN) {
+                    HWND he = GetDlgItem(hwnd, 1002);
+                    if (he) {
+                        int n = GetWindowText(he, cb->password, SSL_MAX_PASSWORD_LEN - 1);
+                        cb->password[n] = '\0';
+                    }
+                    DestroyWindow(hwnd);
+                    break;
+                }
+            }
+            TranslateMessage(&msg);
+            DispatchMessage(&msg);
+        }
+        else
+            Sleep(100);
+    }
+    /* Restore window station and desktop.
+     */
+    SetThreadDesktop(hwds);
+    SetProcessWindowStation(hwss);
+    CloseDesktop(hwdu);
+    CloseWindowStation(hwsu);
+
+    ExitThread(0);
+    return 0;
+}
+
+int WIN32_SSL_password_prompt(tcn_pass_cb_t *data)
+{
+    DWORD id;
+    HANDLE thread;
+    /* TODO: See how to display this from service mode */
+    thread = CreateThread(NULL, 0,
+                password_thread, data,
+                0, &id);
+    WaitForSingleObject(thread, INFINITE);
+    CloseHandle(thread);
+    return (int)strlen(data->password);
+}
+
+#endif
diff --git a/connectors/jni/native/src/address.c b/connectors/jni/native/src/address.c
new file mode 100644
index 0000000..b2291fa
--- /dev/null
+++ b/connectors/jni/native/src/address.c
@@ -0,0 +1,106 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+#include "tcn.h"
+
+TCN_IMPLEMENT_CALL(jlong, Address, info)(TCN_STDARGS,
+                                         jstring hostname,
+                                         jint family, jint port,
+                                         jint flags, jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    TCN_ALLOC_CSTRING(hostname);
+    apr_sockaddr_t *sa = NULL;
+    apr_int32_t f;
+
+
+    UNREFERENCED(o);
+    GET_S_FAMILY(f, family);
+    TCN_THROW_IF_ERR(apr_sockaddr_info_get(&sa,
+            J2S(hostname), f, (apr_port_t)port,
+            (apr_int32_t)flags, p), sa);
+
+cleanup:
+    TCN_FREE_CSTRING(hostname);
+    return P2J(sa);
+}
+
+TCN_IMPLEMENT_CALL(jstring, Address, getnameinfo)(TCN_STDARGS,
+                                                  jlong sa, jint flags)
+{
+    apr_sockaddr_t *s = J2P(sa, apr_sockaddr_t *);
+    char *hostname;
+
+    UNREFERENCED(o);
+    if (apr_getnameinfo(&hostname, s, (apr_int32_t)flags) == APR_SUCCESS)
+        return AJP_TO_JSTRING(hostname);
+    else
+        return NULL;
+}
+
+TCN_IMPLEMENT_CALL(jstring, Address, getip)(TCN_STDARGS, jlong sa)
+{
+    apr_sockaddr_t *s = J2P(sa, apr_sockaddr_t *);
+    char *ipaddr;
+
+    UNREFERENCED(o);
+    if (apr_sockaddr_ip_get(&ipaddr, s) == APR_SUCCESS)
+        return AJP_TO_JSTRING(ipaddr);
+    else
+        return NULL;
+}
+
+TCN_IMPLEMENT_CALL(jlong, Address, get)(TCN_STDARGS, jint which,
+                                        jlong sock)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    apr_sockaddr_t *sa = NULL;
+
+    UNREFERENCED(o);
+    TCN_THROW_IF_ERR(apr_socket_addr_get(&sa,
+                        (apr_interface_e)which, s->sock), sa);
+cleanup:
+    return P2J(sa);
+}
+
+TCN_IMPLEMENT_CALL(jint, Address, equal)(TCN_STDARGS,
+                                         jlong a, jlong b)
+{
+    apr_sockaddr_t *sa = J2P(a, apr_sockaddr_t *);
+    apr_sockaddr_t *sb = J2P(b, apr_sockaddr_t *);
+
+    UNREFERENCED_STDARGS;
+    return apr_sockaddr_equal(sa, sb) ? JNI_TRUE : JNI_FALSE;
+}
+
+TCN_IMPLEMENT_CALL(jint, Address, getservbyname)(TCN_STDARGS,
+                                                 jlong sa, jstring servname)
+{
+    apr_sockaddr_t *s = J2P(sa, apr_sockaddr_t *);
+    TCN_ALLOC_CSTRING(servname);
+    apr_status_t rv;
+
+    UNREFERENCED(o);
+    rv = apr_getservbyname(s, J2S(servname));
+    TCN_FREE_CSTRING(servname);
+    return (jint)rv;
+}
diff --git a/connectors/jni/native/src/dir.c b/connectors/jni/native/src/dir.c
new file mode 100644
index 0000000..df0c102
--- /dev/null
+++ b/connectors/jni/native/src/dir.c
@@ -0,0 +1,105 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Mladen Turk
+ * @version $Revision$, $Date$
+ */
+ 
+#include "tcn.h"
+#include "apr_file_io.h"
+
+TCN_IMPLEMENT_CALL(jint, Directory, make)(TCN_STDARGS, jstring path,
+                                          jint perm, jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    TCN_ALLOC_CSTRING(path);
+    apr_status_t rv;
+
+    UNREFERENCED(o);
+    rv = apr_dir_make(J2S(path), (apr_fileperms_t)perm, p);
+    TCN_FREE_CSTRING(path);
+    return (jint)rv;
+}
+
+TCN_IMPLEMENT_CALL(jint, Directory, makeRecursive)(TCN_STDARGS, jstring path,
+                                                    jint perm, jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    TCN_ALLOC_CSTRING(path);
+    apr_status_t rv;
+
+    UNREFERENCED(o);
+    rv = apr_dir_make_recursive(J2S(path), (apr_fileperms_t)perm, p);
+    TCN_FREE_CSTRING(path);
+    return (jint)rv;
+}
+
+TCN_IMPLEMENT_CALL(jint, Directory, remove)(TCN_STDARGS, jstring path,
+                                            jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    TCN_ALLOC_CSTRING(path);
+    apr_status_t rv;
+
+    UNREFERENCED(o);
+    rv = apr_dir_remove(J2S(path), p);
+    TCN_FREE_CSTRING(path);
+    return (jint)rv;
+}
+
+TCN_IMPLEMENT_CALL(jstring, Directory, tempGet)(TCN_STDARGS, jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    jstring name = NULL;
+    const char *tname;
+
+    UNREFERENCED(o);
+    if (apr_temp_dir_get(&tname, p) == APR_SUCCESS)
+        name = AJP_TO_JSTRING(tname);
+
+    return name;
+}
+
+TCN_IMPLEMENT_CALL(jlong, Directory, open)(TCN_STDARGS, jstring path,
+                                      jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    apr_dir_t *d = NULL;
+    TCN_ALLOC_CSTRING(path);
+
+    UNREFERENCED(o);
+    TCN_THROW_IF_ERR(apr_dir_open(&d, J2S(path), p), d);
+
+cleanup:
+    TCN_FREE_CSTRING(path);
+    return P2J(d);
+}
+
+TCN_IMPLEMENT_CALL(jint, Directory, close)(TCN_STDARGS, jlong dir)
+{
+    apr_dir_t *d = J2P(dir, apr_dir_t *);
+    UNREFERENCED_STDARGS;
+    return (jint)apr_dir_close(d);
+}
+
+TCN_IMPLEMENT_CALL(jint, Directory, rewind)(TCN_STDARGS, jlong dir)
+{
+    apr_dir_t *d = J2P(dir, apr_dir_t *);
+    UNREFERENCED_STDARGS;
+    return (jint)apr_dir_rewind(d);
+}
diff --git a/connectors/jni/native/src/error.c b/connectors/jni/native/src/error.c
new file mode 100644
index 0000000..0ef0dfb
--- /dev/null
+++ b/connectors/jni/native/src/error.c
@@ -0,0 +1,248 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+#include "tcn.h"
+
+static const char *tcn_errors[] = {
+                            "Unknown user error",
+    /* TCN_TIMEUP      */   "Operation timed out",
+    /* TCN_EAGAIN      */   "There is no data ready",
+    /* TCN_EINTR       */   "Interrupted system call",
+    /* TCN_EINPROGRESS */   "Operation in progress",
+    /* TCN_ETIMEDOUT   */   "Connection timed out",
+    NULL
+};
+
+/* Merge IS_ETIMEDOUT with APR_TIMEUP
+ */
+#define TCN_STATUS_IS_ETIMEDOUT(x) (APR_STATUS_IS_ETIMEDOUT((x)) || ((x) == APR_TIMEUP))
+/*
+ * Convenience function to help throw an java.lang.Exception.
+ */
+void tcn_ThrowException(JNIEnv *env, const char *msg)
+{
+    jclass javaExceptionClass;
+
+    javaExceptionClass = (*env)->FindClass(env, "java/lang/Exception");
+    if (javaExceptionClass == NULL) {
+        fprintf(stderr, "Cannot find java/lang/Exception class\n");
+        return;
+    }
+    (*env)->ThrowNew(env, javaExceptionClass, msg);
+    (*env)->DeleteLocalRef(env, javaExceptionClass);
+
+}
+
+void tcn_ThrowMemoryException(JNIEnv *env, const char *file, int line, const char *msg)
+{
+    jclass javaExceptionClass;
+    javaExceptionClass = (*env)->FindClass(env, "java/lang/OutOfMemoryError");
+    if (javaExceptionClass == NULL) {
+        fprintf(stderr, "Cannot find java/lang/OutOfMemoryError\n");
+        return;
+    }
+
+    if (file) {
+        char fmt[TCN_BUFFER_SZ];
+        char *f = (char *)(file + strlen(file) - 1);
+        while (f != file && '\\' != *f && '/' != *f) {
+            f--;
+        }
+        if (f != file) {
+            f++;
+        }
+        sprintf(fmt, "%s for [%04d@%s]", msg, line, f);
+        (*env)->ThrowNew(env, javaExceptionClass, &fmt[0]);
+    }
+    else
+        (*env)->ThrowNew(env, javaExceptionClass, msg);
+    (*env)->DeleteLocalRef(env, javaExceptionClass);
+
+}
+
+
+void tcn_Throw(JNIEnv *env, const char *fmt, ...)
+{
+    char msg[TCN_BUFFER_SZ] = {'\0'};
+    va_list ap;
+
+    va_start(ap, fmt);
+    apr_vsnprintf(msg, TCN_BUFFER_SZ, fmt, ap);
+    tcn_ThrowException(env, msg);
+    va_end(ap);
+}
+
+/*
+ * Convenience function to help throw an APR Exception
+ * from native error code.
+ */
+void tcn_ThrowAPRException(JNIEnv *e, apr_status_t err)
+{
+    jclass aprErrorClass;
+    jmethodID constructorID = 0;
+    jobject throwObj;
+    jstring jdescription;
+    char serr[512] = {0};
+
+    aprErrorClass = (*e)->FindClass(e, TCN_ERROR_CLASS);
+    if (aprErrorClass == NULL) {
+        fprintf(stderr, "Cannot find " TCN_ERROR_CLASS " class\n");
+        return;
+    }
+
+    /* Find the constructor ID */
+    constructorID = (*e)->GetMethodID(e, aprErrorClass,
+                                      "<init>",
+                                      "(ILjava/lang/String;)V");
+    if (constructorID == NULL) {
+        fprintf(stderr,
+                "Cannot find constructor for " TCN_ERROR_CLASS " class\n");
+        goto cleanup;
+    }
+
+    apr_strerror(err, serr, 512);
+    /* Obtain the string objects */
+    jdescription = AJP_TO_JSTRING(serr);
+    if (jdescription == NULL) {
+        fprintf(stderr,
+                "Cannot allocate description for " TCN_ERROR_CLASS " class\n");
+        goto cleanup;
+    }
+    /* Create the APR Error object */
+    throwObj = (*e)->NewObject(e, aprErrorClass, constructorID,
+                               (jint)err, jdescription);
+    if (throwObj == NULL) {
+        fprintf(stderr,
+                "Cannot allocate new " TCN_ERROR_CLASS " object\n");
+        goto cleanup;
+    }
+
+    (*e)->Throw(e, throwObj);
+cleanup:
+    (*e)->DeleteLocalRef(e, aprErrorClass);
+}
+
+
+TCN_IMPLEMENT_CALL(jint, Error, osError)(TCN_STDARGS)
+{
+    UNREFERENCED_STDARGS;
+    return (jint)apr_get_os_error();
+}
+
+TCN_IMPLEMENT_CALL(jint, Error, netosError)(TCN_STDARGS)
+{
+    UNREFERENCED_STDARGS;
+    return (jint)apr_get_netos_error();
+}
+
+TCN_IMPLEMENT_CALL(jstring, Error, strerror)(TCN_STDARGS, jint err)
+{
+    char serr[512] = {0};
+    jstring jerr;
+
+    UNREFERENCED(o);
+    if (err >= TCN_TIMEUP && err <= TCN_ETIMEDOUT) {
+        err -= TCN_TIMEUP;
+        jerr = AJP_TO_JSTRING(tcn_errors[err + 1]);
+    }
+    else {
+        apr_strerror(err, serr, 512);
+        jerr = AJP_TO_JSTRING(serr);
+    }
+    return jerr;
+}
+
+TCN_IMPLEMENT_CALL(jboolean, Status, is)(TCN_STDARGS, jint err, jint idx)
+{
+#define APR_IS(I, E) case I: if (E(err)) return JNI_TRUE; break
+#define APR_ISX(I, E, T) case I: if (E(err) || (err == T)) return JNI_TRUE; break
+
+    UNREFERENCED_STDARGS;
+    switch (idx) {
+        APR_IS(1,  APR_STATUS_IS_ENOSTAT);
+        APR_IS(2,  APR_STATUS_IS_ENOPOOL);
+        /* empty slot: +3 */
+        APR_IS(4,  APR_STATUS_IS_EBADDATE);
+        APR_IS(5,  APR_STATUS_IS_EINVALSOCK);
+        APR_IS(6,  APR_STATUS_IS_ENOPROC);
+        APR_IS(7,  APR_STATUS_IS_ENOTIME);
+        APR_IS(8,  APR_STATUS_IS_ENODIR);
+        APR_IS(9,  APR_STATUS_IS_ENOLOCK);
+        APR_IS(10, APR_STATUS_IS_ENOPOLL);
+        APR_IS(11, APR_STATUS_IS_ENOSOCKET);
+        APR_IS(12, APR_STATUS_IS_ENOTHREAD);
+        APR_IS(13, APR_STATUS_IS_ENOTHDKEY);
+        APR_IS(14, APR_STATUS_IS_EGENERAL);
+        APR_IS(15, APR_STATUS_IS_ENOSHMAVAIL);
+        APR_IS(16, APR_STATUS_IS_EBADIP);
+        APR_IS(17, APR_STATUS_IS_EBADMASK);
+        /* empty slot: +18 */
+        APR_IS(19, APR_STATUS_IS_EDSOOPEN);
+        APR_IS(20, APR_STATUS_IS_EABSOLUTE);
+        APR_IS(21, APR_STATUS_IS_ERELATIVE);
+        APR_IS(22, APR_STATUS_IS_EINCOMPLETE);
+        APR_IS(23, APR_STATUS_IS_EABOVEROOT);
+        APR_IS(24, APR_STATUS_IS_EBADPATH);
+        APR_IS(25, APR_STATUS_IS_EPATHWILD);
+        APR_IS(26, APR_STATUS_IS_ESYMNOTFOUND);
+        APR_IS(27, APR_STATUS_IS_EPROC_UNKNOWN);
+        APR_IS(28, APR_STATUS_IS_ENOTENOUGHENTROPY);
+
+
+        /* APR_Error */
+        APR_IS(51, APR_STATUS_IS_INCHILD);
+        APR_IS(52, APR_STATUS_IS_INPARENT);
+        APR_IS(53, APR_STATUS_IS_DETACH);
+        APR_IS(54, APR_STATUS_IS_NOTDETACH);
+        APR_IS(55, APR_STATUS_IS_CHILD_DONE);
+        APR_IS(56, APR_STATUS_IS_CHILD_NOTDONE);
+        APR_ISX(57, APR_STATUS_IS_TIMEUP, TCN_TIMEUP);
+        APR_IS(58, APR_STATUS_IS_INCOMPLETE);
+        /* empty slot: +9 */
+        /* empty slot: +10 */
+        /* empty slot: +11 */
+        APR_IS(62, APR_STATUS_IS_BADCH);
+        APR_IS(63, APR_STATUS_IS_BADARG);
+        APR_IS(64, APR_STATUS_IS_EOF);
+        APR_IS(65, APR_STATUS_IS_NOTFOUND);
+        /* empty slot: +16 */
+        /* empty slot: +17 */
+        /* empty slot: +18 */
+        APR_IS(69, APR_STATUS_IS_ANONYMOUS);
+        APR_IS(70, APR_STATUS_IS_FILEBASED);
+        APR_IS(71, APR_STATUS_IS_KEYBASED);
+        APR_IS(72, APR_STATUS_IS_EINIT);
+        APR_IS(73, APR_STATUS_IS_ENOTIMPL);
+        APR_IS(74, APR_STATUS_IS_EMISMATCH);
+        APR_IS(75, APR_STATUS_IS_EBUSY);
+        /* Socket errors */
+        APR_ISX(90, APR_STATUS_IS_EAGAIN, TCN_EAGAIN);
+        APR_ISX(91, TCN_STATUS_IS_ETIMEDOUT, TCN_ETIMEDOUT);
+        APR_IS(92, APR_STATUS_IS_ECONNABORTED);
+        APR_IS(93, APR_STATUS_IS_ECONNRESET);
+        APR_ISX(94, APR_STATUS_IS_EINPROGRESS, TCN_EINPROGRESS);
+        APR_ISX(95, APR_STATUS_IS_EINTR, TCN_EINTR);
+        APR_IS(96, APR_STATUS_IS_ENOTSOCK);
+        APR_IS(97, APR_STATUS_IS_EINVAL);
+    }
+    return JNI_FALSE;
+}
diff --git a/connectors/jni/native/src/file.c b/connectors/jni/native/src/file.c
new file mode 100644
index 0000000..8c1d1d2
--- /dev/null
+++ b/connectors/jni/native/src/file.c
@@ -0,0 +1,601 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Mladen Turk
+ * @version $Revision$, $Date$
+ */
+ 
+#include "tcn.h"
+#include "apr_file_io.h"
+
+
+TCN_IMPLEMENT_CALL(jint, File, close)(TCN_STDARGS, jlong file)
+{
+    apr_file_t *f = J2P(file, apr_file_t *);
+    UNREFERENCED_STDARGS;
+    return (jint)apr_file_close(f);
+}
+
+TCN_IMPLEMENT_CALL(jint, File, eof)(TCN_STDARGS, jlong file)
+{
+    apr_file_t *f = J2P(file, apr_file_t *);
+    UNREFERENCED_STDARGS;
+    return (jint)apr_file_eof(f);
+}
+
+TCN_IMPLEMENT_CALL(jint, File, flush)(TCN_STDARGS, jlong file)
+{
+    apr_file_t *f = J2P(file, apr_file_t *);
+    UNREFERENCED_STDARGS;
+    return (jint)apr_file_flush(f);
+}
+
+TCN_IMPLEMENT_CALL(jint, File, unlock)(TCN_STDARGS, jlong file)
+{
+    apr_file_t *f = J2P(file, apr_file_t *);
+    UNREFERENCED_STDARGS;
+    return (jint)apr_file_unlock(f);
+}
+
+TCN_IMPLEMENT_CALL(jint, File, flagsGet)(TCN_STDARGS, jlong file)
+{
+    apr_file_t *f = J2P(file, apr_file_t *);
+    UNREFERENCED_STDARGS;
+    return (jint)apr_file_flags_get(f);
+}
+
+TCN_IMPLEMENT_CALL(jint, File, lock)(TCN_STDARGS, jlong file, jint flags)
+{
+    apr_file_t *f = J2P(file, apr_file_t *);
+    UNREFERENCED_STDARGS;
+    return (jint)apr_file_lock(f, (int)flags);
+}
+
+TCN_IMPLEMENT_CALL(jint, File, trunc)(TCN_STDARGS, jlong file, jlong off)
+{
+    apr_file_t *f = J2P(file, apr_file_t *);
+    UNREFERENCED_STDARGS;
+    return (jint)apr_file_trunc(f, (apr_off_t)off);
+}
+
+TCN_IMPLEMENT_CALL(jlong, File, open)(TCN_STDARGS, jstring fname,
+                                      jint flag, jint perm,
+                                      jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    apr_file_t *f = NULL;
+    TCN_ALLOC_CSTRING(fname);
+
+    UNREFERENCED(o);
+    TCN_THROW_IF_ERR(apr_file_open(&f, J2S(fname), (apr_int32_t)flag,
+                     (apr_fileperms_t)perm, p), f);
+
+cleanup:
+    TCN_FREE_CSTRING(fname);
+    return P2J(f);
+}
+
+TCN_IMPLEMENT_CALL(jlong, File, mktemp)(TCN_STDARGS, jstring templ,
+                                      jint flags,
+                                      jint pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    apr_file_t *f = NULL;
+    char *ctempl = tcn_strdup(e, templ);
+
+    UNREFERENCED(o);
+    if (!ctempl) {
+       TCN_THROW_OS_ERROR(e);
+       return 0;
+    }
+    TCN_THROW_IF_ERR(apr_file_mktemp(&f, ctempl,
+                     (apr_int32_t)flags, p), f);
+
+cleanup:
+    free(ctempl);
+    return P2J(f);
+}
+
+TCN_IMPLEMENT_CALL(jint, File, remove)(TCN_STDARGS, jstring path, jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    TCN_ALLOC_CSTRING(path);
+    apr_status_t rv;
+
+    UNREFERENCED(o);
+    rv = apr_file_remove(J2S(path), p);
+    TCN_FREE_CSTRING(path);
+    return (jint)rv;
+}
+
+TCN_IMPLEMENT_CALL(jint, File, rename)(TCN_STDARGS, jstring from,
+                                       jstring to, jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    TCN_ALLOC_CSTRING(from);
+    TCN_ALLOC_CSTRING(to);
+    apr_status_t rv;
+
+    UNREFERENCED(o);
+    rv = apr_file_rename(J2S(from), J2S(to), p);
+    TCN_FREE_CSTRING(from);
+    TCN_FREE_CSTRING(to);
+    return (jint)rv;
+}
+
+TCN_IMPLEMENT_CALL(jint, File, copy)(TCN_STDARGS, jstring from,
+                                     jstring to, jint perms, jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    TCN_ALLOC_CSTRING(from);
+    TCN_ALLOC_CSTRING(to);
+    apr_status_t rv;
+
+    UNREFERENCED(o);
+    rv = apr_file_copy(J2S(from), J2S(to), (apr_fileperms_t)perms, p);
+    TCN_FREE_CSTRING(from);
+    TCN_FREE_CSTRING(to);
+    return (jint)rv;
+}
+
+TCN_IMPLEMENT_CALL(jint, File, append)(TCN_STDARGS, jstring from,
+                                       jstring to, jint perms, jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    TCN_ALLOC_CSTRING(from);
+    TCN_ALLOC_CSTRING(to);
+    apr_status_t rv;
+
+    UNREFERENCED(o);
+    rv = apr_file_append(J2S(from), J2S(to), (apr_fileperms_t)perms, p);
+    TCN_FREE_CSTRING(from);
+    TCN_FREE_CSTRING(to);
+    return (jint)rv;
+}
+
+TCN_IMPLEMENT_CALL(jstring, File, nameGet)(TCN_STDARGS, jlong file)
+{
+    apr_file_t *f = J2P(file, apr_file_t *);
+    jstring name = NULL;
+    const char *fname;
+
+    UNREFERENCED(o);
+    if (apr_file_name_get(&fname, f) == APR_SUCCESS)
+        name = AJP_TO_JSTRING(fname);
+
+    return name;
+}
+
+TCN_IMPLEMENT_CALL(jint, File, permsSet)(TCN_STDARGS, jstring file, jint perms)
+{
+    TCN_ALLOC_CSTRING(file);
+    apr_status_t rv;
+
+    UNREFERENCED(o);
+    rv = apr_file_perms_set(J2S(file), (apr_fileperms_t)perms);
+    TCN_FREE_CSTRING(file);
+    return (jint)rv;
+}
+
+TCN_IMPLEMENT_CALL(jint, File, attrsSet)(TCN_STDARGS, jstring file, jint attrs,
+                                          jint mask, jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    TCN_ALLOC_CSTRING(file);
+    apr_status_t rv;
+
+    UNREFERENCED(o);
+    rv = apr_file_attrs_set(J2S(file), (apr_fileattrs_t)attrs,
+                            (apr_fileattrs_t)mask, p);
+    TCN_FREE_CSTRING(file);
+    return (jint)rv;
+}
+
+TCN_IMPLEMENT_CALL(jint, File, mtimeSet)(TCN_STDARGS, jstring file, jlong mtime,
+                                          jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    TCN_ALLOC_CSTRING(file);
+    apr_status_t rv;
+
+    UNREFERENCED(o);
+    rv = apr_file_mtime_set(J2S(file), J2T(mtime), p);
+    TCN_FREE_CSTRING(file);
+    return (jint)rv;
+}
+
+TCN_IMPLEMENT_CALL(jlong, File, seek)(TCN_STDARGS, jlong file,
+                                      jint where, jlong offset)
+{
+    apr_file_t *f = J2P(file, apr_file_t *);
+    apr_off_t pos = (apr_off_t)offset;
+    apr_seek_where_t w;
+    UNREFERENCED(o);
+    switch (where) {
+        case 1:
+            w = APR_CUR;
+            break;
+        case 2:
+            w = APR_END;
+            break;
+        default:
+            w = APR_SET;
+            break;
+    }
+    TCN_THROW_IF_ERR(apr_file_seek(f, w, &pos), pos);
+
+cleanup:
+    return (jlong)pos;
+}
+
+TCN_IMPLEMENT_CALL(jint, File, putc)(TCN_STDARGS, jbyte c, jlong file)
+{
+    apr_file_t *f = J2P(file, apr_file_t *);
+    UNREFERENCED_STDARGS;
+    return (jint)apr_file_putc((char)c, f);
+}
+
+TCN_IMPLEMENT_CALL(jint, File, getc)(TCN_STDARGS, jlong file)
+{
+    apr_file_t *f = J2P(file, apr_file_t *);
+    char ch;
+
+    UNREFERENCED_STDARGS;
+    TCN_THROW_IF_ERR(apr_file_getc(&ch, f), ch);
+
+cleanup:
+    return (jint)ch;
+}
+
+TCN_IMPLEMENT_CALL(jint, File, ungetc)(TCN_STDARGS, jbyte c, jlong file)
+{
+    apr_file_t *f = J2P(file, apr_file_t *);
+    UNREFERENCED_STDARGS;
+    return (jint)apr_file_ungetc((char)c, f);
+}
+
+TCN_IMPLEMENT_CALL(jint, File, puts)(TCN_STDARGS, jbyteArray str, jlong file)
+{
+    apr_status_t rv = APR_EINVAL;
+    apr_file_t *f = J2P(file, apr_file_t *);
+    jbyte *bytes = (*e)->GetPrimitiveArrayCritical(e, str, NULL);
+
+    UNREFERENCED(o);
+    if (bytes) {
+        rv = apr_file_puts((const char *)bytes, f);
+        (*e)->ReleasePrimitiveArrayCritical(e, str, bytes, JNI_ABORT);
+    }
+    return (jint)rv;
+}
+
+TCN_IMPLEMENT_CALL(jint, File, write)(TCN_STDARGS, jlong file,
+                                      jbyteArray buf, jint offset, jint towrite)
+{
+    apr_file_t *f = J2P(file, apr_file_t *);
+    apr_size_t nbytes = (apr_size_t)towrite;
+    jbyte *bytes = (*e)->GetPrimitiveArrayCritical(e, buf, NULL);
+    apr_status_t ss;
+
+    UNREFERENCED(o);
+    if (towrite < 0)
+        towrite = (*e)->GetArrayLength(e, buf);
+    ss = apr_file_write(f, bytes + offset, &nbytes);
+
+    (*e)->ReleasePrimitiveArrayCritical(e, buf, bytes, JNI_ABORT);
+    if (ss == APR_SUCCESS)
+        return (jint)nbytes;
+    else
+        return -(jint)ss;
+}
+
+TCN_IMPLEMENT_CALL(jint, File, writeb)(TCN_STDARGS, jlong file,
+                                       jobject buf, jint offset, jint towrite)
+{
+    apr_file_t *f = J2P(file, apr_file_t *);
+    apr_size_t nbytes = (apr_size_t)towrite;
+    char *bytes = (char *)(*e)->GetDirectBufferAddress(e, buf);
+    apr_status_t ss = APR_EINVAL;
+
+    UNREFERENCED(o);
+    if (bytes)
+        ss = apr_file_write(f, bytes + offset, &nbytes);
+
+    if (ss == APR_SUCCESS)
+        return (jint)nbytes;
+    else
+        return -(jint)ss;
+}
+
+TCN_IMPLEMENT_CALL(jint, File, writeFull)(TCN_STDARGS, jlong file,
+                                          jbyteArray buf, jint offset, jint towrite)
+{
+    apr_file_t *f = J2P(file, apr_file_t *);
+    apr_size_t nbytes = (apr_size_t)towrite;
+    apr_size_t written = 0;
+    apr_status_t ss;
+    jbyte *bytes = (*e)->GetByteArrayElements(e, buf, NULL);
+
+    UNREFERENCED(o);
+    if (towrite < 0)
+        towrite = (*e)->GetArrayLength(e, buf);
+    ss = apr_file_write_full(f, bytes + offset, nbytes, &written);
+
+    (*e)->ReleaseByteArrayElements(e, buf, bytes, JNI_ABORT);
+    if (ss == APR_SUCCESS)
+        return (jint)written;
+    else
+        return -(jint)ss;
+}
+
+TCN_IMPLEMENT_CALL(jint, File, writeFullb)(TCN_STDARGS, jlong file,
+                                           jobject buf, jint offset, jint towrite)
+{
+    apr_file_t *f = J2P(file, apr_file_t *);
+    apr_size_t nbytes = (apr_size_t)towrite;
+    apr_size_t written = 0;
+    apr_status_t ss = APR_EINVAL;
+    char *bytes = (char *)(*e)->GetDirectBufferAddress(e, buf);
+
+    UNREFERENCED(o);
+    if (bytes)
+        ss = apr_file_write_full(f, bytes + offset, nbytes, &written);
+
+    if (ss == APR_SUCCESS)
+        return (jint)written;
+    else
+        return -(jint)ss;
+}
+
+TCN_IMPLEMENT_CALL(jint, File, writev)(TCN_STDARGS, jlong file,
+                                       jobjectArray bufs)
+{
+    apr_file_t *f = J2P(file, apr_file_t *);
+    jsize nvec = (*e)->GetArrayLength(e, bufs);
+    jsize i;
+    struct iovec vec[APR_MAX_IOVEC_SIZE];
+    jobject ba[APR_MAX_IOVEC_SIZE];
+    apr_size_t written = 0;
+    apr_status_t ss;
+
+    UNREFERENCED(o);
+
+    if (nvec >= APR_MAX_IOVEC_SIZE) {
+        /* TODO: Throw something here */
+        return 0;
+    }
+    for (i = 0; i < nvec; i++) {
+        ba[i] = (*e)->GetObjectArrayElement(e, bufs, i);
+        vec[i].iov_len  = (*e)->GetArrayLength(e, ba[i]);
+        vec[i].iov_base = (*e)->GetByteArrayElements(e, ba[i], NULL);
+    }
+
+    ss = apr_file_writev(f, vec, nvec, &written);
+
+    for (i = 0; i < nvec; i++) {
+        (*e)->ReleaseByteArrayElements(e, ba[i], vec[i].iov_base, JNI_ABORT);
+    }
+    if (ss == APR_SUCCESS)
+        return (jint)written;
+    else
+        return -(jint)ss;
+}
+
+TCN_IMPLEMENT_CALL(jint, File, writevFull)(TCN_STDARGS, jlong file,
+                                           jobjectArray bufs)
+{
+    apr_file_t *f = J2P(file, apr_file_t *);
+    jsize nvec = (*e)->GetArrayLength(e, bufs);
+    jsize i;
+    struct iovec vec[APR_MAX_IOVEC_SIZE];
+    jobject ba[APR_MAX_IOVEC_SIZE];
+    apr_size_t written = 0;
+    apr_status_t ss;
+
+    UNREFERENCED(o);
+
+    if (nvec >= APR_MAX_IOVEC_SIZE) {
+        /* TODO: Throw something here */
+        return 0;
+    }
+    for (i = 0; i < nvec; i++) {
+        ba[i] = (*e)->GetObjectArrayElement(e, bufs, i);
+        vec[i].iov_len  = (*e)->GetArrayLength(e, ba[i]);
+        vec[i].iov_base = (*e)->GetByteArrayElements(e, ba[i], NULL);
+    }
+#if (APR_VERSION_MAJOR >= 1) && (APR_VERSION_MINOR >= 1)
+    ss = apr_file_writev_full(f, vec, nvec, &written);
+#else
+    ss = apr_file_writev(f, vec, nvec, &written);
+#endif
+
+    for (i = 0; i < nvec; i++) {
+        (*e)->ReleaseByteArrayElements(e, ba[i], vec[i].iov_base,
+                                       JNI_ABORT);
+    }
+    if (ss == APR_SUCCESS)
+        return (jint)written;
+    else
+        return -(jint)ss;
+}
+
+TCN_IMPLEMENT_CALL(jint, File, read)(TCN_STDARGS, jlong file,
+                                     jbyteArray buf, jint offset,
+                                     jint toread)
+{
+    apr_file_t *f = J2P(file, apr_file_t *);
+    apr_size_t nbytes = (apr_size_t)toread;
+    jbyte *bytes = (*e)->GetByteArrayElements(e, buf, NULL);
+    apr_status_t ss;
+
+    UNREFERENCED(o);
+    ss = apr_file_read(f, bytes + offset, &nbytes);
+
+    (*e)->ReleaseByteArrayElements(e, buf, bytes,
+                                   ss == APR_SUCCESS ? 0 : JNI_ABORT);
+    if (ss == APR_SUCCESS)
+        return (jint)nbytes;
+    else
+        return -(jint)ss;
+}
+
+TCN_IMPLEMENT_CALL(jint, File, readb)(TCN_STDARGS, jlong file,
+                                      jobject buf, jint offset,
+                                      jint toread)
+{
+    apr_file_t *f = J2P(file, apr_file_t *);
+    apr_size_t nbytes = (apr_size_t)toread;
+    char *bytes = (char *)(*e)->GetDirectBufferAddress(e, buf);
+    apr_status_t ss = APR_EINVAL;
+
+    UNREFERENCED(o);
+    if (bytes)
+        ss = apr_file_read(f, bytes + offset, &nbytes);
+
+    if (ss == APR_SUCCESS)
+        return (jint)nbytes;
+    else
+        return -(jint)ss;
+}
+
+TCN_IMPLEMENT_CALL(jint, File, readFull)(TCN_STDARGS, jlong file,
+                                         jbyteArray buf, jint offset,
+                                         jint toread)
+{
+    apr_file_t *f = J2P(file, apr_file_t *);
+    apr_size_t nbytes = (apr_size_t)toread;
+    apr_size_t nread  = 0;
+    apr_status_t ss;
+    jbyte *bytes = (*e)->GetByteArrayElements(e, buf, NULL);
+
+    UNREFERENCED(o);
+    ss = apr_file_read_full(f, bytes + offset, nbytes, &nread);
+
+    (*e)->ReleaseByteArrayElements(e, buf, bytes,
+                                   ss == APR_SUCCESS ? 0 : JNI_ABORT);
+    if (ss == APR_SUCCESS)
+        return (jint)nread;
+    else
+        return -(jint)ss;
+}
+
+TCN_IMPLEMENT_CALL(jint, File, readFullb)(TCN_STDARGS, jlong file,
+                                          jobject buf, jint offset,
+                                          jint toread)
+{
+    apr_file_t *f = J2P(file, apr_file_t *);
+    apr_size_t nbytes = (apr_size_t)toread;
+    apr_size_t nread  = 0;
+    char *bytes = (char *)(*e)->GetDirectBufferAddress(e, buf);
+    apr_status_t ss = APR_EINVAL;
+
+    UNREFERENCED(o);
+    if (bytes)
+        ss = apr_file_read_full(f, bytes + offset, nbytes, &nread);
+
+    if (ss == APR_SUCCESS)
+        return (jint)nread;
+    else
+        return -(jint)ss;
+}
+
+TCN_IMPLEMENT_CALL(jint, File, gets)(TCN_STDARGS, jbyteArray buf, jint offset,
+                                     jlong file)
+{
+    apr_status_t rv;
+    apr_file_t *f = J2P(file, apr_file_t *);
+    jsize nbytes = (*e)->GetArrayLength(e, buf);
+    jbyte *bytes = (*e)->GetByteArrayElements(e, buf, NULL);
+
+    UNREFERENCED(o);
+    rv = apr_file_gets((char*)(bytes + offset),nbytes - offset, f);
+    (*e)->ReleaseByteArrayElements(e, buf, bytes,
+                                   rv == APR_SUCCESS ? 0 : JNI_ABORT);
+    return (jint)rv;
+}
+
+TCN_IMPLEMENT_CALL(jint, File, pipeCreate)(TCN_STDARGS, jlongArray io, jlong pool)
+{
+    apr_status_t rv;
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    jsize npipes = (*e)->GetArrayLength(e, io);
+    jlong *pipes = (*e)->GetLongArrayElements(e, io, NULL);
+    apr_file_t *in;
+    apr_file_t *out;
+
+    UNREFERENCED(o);
+    if (npipes < 2) {
+        (*e)->ReleaseLongArrayElements(e, io, pipes, JNI_ABORT);
+        return APR_EINVAL;
+    }
+
+    rv = apr_file_pipe_create(&in, &out, p);
+    if (rv == APR_SUCCESS) {
+        pipes[0] = P2J(in);
+        pipes[1] = P2J(out);
+        (*e)->ReleaseLongArrayElements(e, io, pipes, 0);
+    }
+    else
+        (*e)->ReleaseLongArrayElements(e, io, pipes, JNI_ABORT);
+
+    return (jint)rv;
+}
+
+TCN_IMPLEMENT_CALL(jint, File, pipeTimeoutSet)(TCN_STDARGS, jlong pipe,
+                                               jlong timeout)
+{
+    apr_file_t *f = J2P(pipe, apr_file_t *);
+    UNREFERENCED_STDARGS;
+    return (jint)apr_file_pipe_timeout_set(f, J2T(timeout));
+}
+
+TCN_IMPLEMENT_CALL(jlong, File, pipeTimeoutGet)(TCN_STDARGS, jlong pipe)
+{
+    apr_file_t *f = J2P(pipe, apr_file_t *);
+    apr_interval_time_t timeout;
+
+    UNREFERENCED(o);
+    TCN_THROW_IF_ERR(apr_file_pipe_timeout_get(f, &timeout), timeout);
+
+cleanup:
+    return (jlong)timeout;
+}
+
+TCN_IMPLEMENT_CALL(jlong, File, dup)(TCN_STDARGS, jlong newf, jlong file,
+                                     jlong pool)
+{
+    apr_file_t *f = J2P(file, apr_file_t *);
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    apr_file_t *d = J2P(newf, apr_file_t *);
+
+    UNREFERENCED(o);
+    TCN_THROW_IF_ERR(apr_file_dup(&d, f, p), d);
+
+cleanup:
+    return P2J(d);
+}
+
+TCN_IMPLEMENT_CALL(jint, File, dup2)(TCN_STDARGS, jlong newf, jlong file,
+                                     jlong pool)
+{
+    apr_file_t *f = J2P(file, apr_file_t *);
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    apr_file_t *d = J2P(newf, apr_file_t *);
+
+    UNREFERENCED_STDARGS;
+    return (jint)apr_file_dup2(d, f, p);
+}
diff --git a/connectors/jni/native/src/info.c b/connectors/jni/native/src/info.c
new file mode 100644
index 0000000..257ff4e
--- /dev/null
+++ b/connectors/jni/native/src/info.c
@@ -0,0 +1,352 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Mladen Turk
+ * @version $Revision$, $Date$
+ */
+ 
+#include "tcn.h"
+#include "apr_file_io.h"
+
+#define DECLARE_FINFO_FIELD(name) static jfieldID _fid##name = NULL
+#define FINFO_FIELD(name)         _fid##name
+
+#define GET_FINFO_I(N)      \
+    _fid##N = (*e)->GetFieldID(e, finfo, #N, "I");  \
+    if (_fid##N == NULL) {                          \
+        (*e)->ExceptionClear(e);                    \
+        goto cleanup;                               \
+    } else (void)(0)
+
+#define GET_FINFO_J(N)      \
+    _fid##N = (*e)->GetFieldID(e, finfo, #N, "J");  \
+    if (_fid##N == NULL) {                          \
+        (*e)->ExceptionClear(e);                    \
+        goto cleanup;                               \
+    } else (void)(0)
+
+#define GET_FINFO_S(N)      \
+    _fid##N = (*e)->GetFieldID(e, finfo, #N,        \
+                             "Ljava/lang/String;"); \
+    if (_fid##N == NULL) {                          \
+        (*e)->ExceptionClear(e);                    \
+        goto cleanup;                               \
+    } else (void)(0)
+
+#define SET_FINFO_I(N, V)  \
+    (*e)->SetIntField(e, obj, _fid##N, (jint)(V))
+
+#define SET_FINFO_J(N, V)  \
+    (*e)->SetLongField(e, obj, _fid##N, (jlong)(V))
+
+#define SET_FINFO_S(N, V)                 \
+    (*e)->SetObjectField(e, obj, _fid##N, \
+        (V) ? AJP_TO_JSTRING((V)) : NULL)
+
+
+#define DECLARE_AINFO_FIELD(name) static jfieldID _aid##name = NULL
+#define AINFO_FIELD(name)         _aid##name
+
+#define GET_AINFO_I(N)      \
+    _aid##N = (*e)->GetFieldID(e, ainfo, #N, "I");  \
+    if (_aid##N == NULL) {                          \
+        (*e)->ExceptionClear(e);                    \
+        goto cleanup;                               \
+    } else (void)(0)
+
+#define GET_AINFO_J(N)      \
+    _aid##N = (*e)->GetFieldID(e, ainfo, #N, "J");  \
+    if (_aid##N == NULL) {                          \
+        (*e)->ExceptionClear(e);                    \
+        goto cleanup;                               \
+    } else (void)(0)
+
+#define GET_AINFO_S(N)      \
+    _aid##N = (*e)->GetFieldID(e, ainfo, #N,        \
+                             "Ljava/lang/String;"); \
+    if (_aid##N == NULL) {                          \
+        (*e)->ExceptionClear(e);                    \
+        goto cleanup;                               \
+    } else (void)(0)
+
+#define SET_AINFO_I(N, V)  \
+    (*e)->SetIntField(e, obj, _aid##N, (jint)(V))
+
+#define SET_AINFO_J(N, V)  \
+    (*e)->SetLongField(e, obj, _aid##N, (jlong)(V))
+
+#define SET_AINFO_S(N, V)                 \
+    (*e)->SetObjectField(e, obj, _aid##N, \
+        (V) ? AJP_TO_JSTRING((V)) : NULL)
+
+
+DECLARE_FINFO_FIELD(pool);
+DECLARE_FINFO_FIELD(valid);
+DECLARE_FINFO_FIELD(protection);
+DECLARE_FINFO_FIELD(filetype);
+DECLARE_FINFO_FIELD(user);
+DECLARE_FINFO_FIELD(group);
+DECLARE_FINFO_FIELD(inode);
+DECLARE_FINFO_FIELD(device);
+DECLARE_FINFO_FIELD(nlink);
+DECLARE_FINFO_FIELD(size);
+DECLARE_FINFO_FIELD(csize);
+DECLARE_FINFO_FIELD(atime);
+DECLARE_FINFO_FIELD(mtime);
+DECLARE_FINFO_FIELD(ctime);
+DECLARE_FINFO_FIELD(fname);
+DECLARE_FINFO_FIELD(name);
+DECLARE_FINFO_FIELD(filehand);
+
+DECLARE_AINFO_FIELD(pool);
+DECLARE_AINFO_FIELD(hostname);
+DECLARE_AINFO_FIELD(servname);
+DECLARE_AINFO_FIELD(port);
+DECLARE_AINFO_FIELD(family);
+DECLARE_AINFO_FIELD(next);
+
+static int finfo_class_initialized = 0;
+static int ainfo_class_initialized = 0;
+static jmethodID finfo_class_init = NULL;
+static jmethodID ainfo_class_init = NULL;
+static jclass finfo_class = NULL;
+static jclass ainfo_class = NULL;
+
+apr_status_t tcn_load_finfo_class(JNIEnv *e, jclass finfo)
+{
+    GET_FINFO_J(pool);
+    GET_FINFO_I(valid);
+    GET_FINFO_I(protection);
+    GET_FINFO_I(filetype);
+    GET_FINFO_I(user);
+    GET_FINFO_I(group);
+    GET_FINFO_I(inode);
+    GET_FINFO_I(device);
+    GET_FINFO_I(nlink);
+    GET_FINFO_J(size);
+    GET_FINFO_J(csize);
+    GET_FINFO_J(atime);
+    GET_FINFO_J(mtime);
+    GET_FINFO_J(ctime);
+    GET_FINFO_S(fname);
+    GET_FINFO_S(name);
+    GET_FINFO_J(filehand);
+    
+    finfo_class_init = (*e)->GetMethodID(e, finfo,
+                                      "<init>", "()V");
+    if (finfo_class_init == NULL)
+        goto cleanup;
+    finfo_class_initialized = 1;
+    finfo_class = finfo;
+cleanup:
+    return APR_SUCCESS;
+}
+
+apr_status_t tcn_load_ainfo_class(JNIEnv *e, jclass ainfo)
+{
+    GET_AINFO_J(pool);
+    GET_AINFO_S(hostname);
+    GET_AINFO_S(servname);
+    GET_AINFO_I(port);
+    GET_AINFO_I(family);
+    GET_AINFO_J(next);
+    ainfo_class_init = (*e)->GetMethodID(e, ainfo,
+                                      "<init>", "()V");
+
+    if (ainfo_class_init == NULL)
+        goto cleanup;
+    ainfo_class_initialized = 1;
+    ainfo_class = ainfo;
+cleanup:
+    return APR_SUCCESS;
+}
+
+static void fill_finfo(JNIEnv *e, jobject obj, apr_finfo_t *info)
+{
+
+    SET_FINFO_J(pool, P2J(info->pool));
+    SET_FINFO_I(valid, info->valid);
+    SET_FINFO_I(protection, info->protection);
+    SET_FINFO_I(filetype, info->filetype);
+    SET_FINFO_I(user, ((jlong)info->user));
+    SET_FINFO_I(group, ((jlong)info->group));
+    SET_FINFO_I(inode, info->inode);
+    SET_FINFO_I(device, info->device);
+    SET_FINFO_I(nlink, info->nlink);
+    SET_FINFO_J(size, info->size);
+    SET_FINFO_J(csize, info->csize);
+    SET_FINFO_J(atime, info->atime);
+    SET_FINFO_J(mtime, info->mtime);
+    SET_FINFO_J(ctime, info->ctime);
+    SET_FINFO_S(fname, info->fname);
+    SET_FINFO_S(name, info->name);
+    SET_FINFO_J(filehand, P2J(info->filehand));
+}
+
+static void fill_ainfo(JNIEnv *e, jobject obj, apr_sockaddr_t *info)
+{
+
+    SET_AINFO_J(pool, P2J(info->pool));
+    SET_AINFO_S(hostname, info->hostname);
+    SET_AINFO_S(servname, info->servname);
+    SET_AINFO_I(port, info->port);
+    SET_AINFO_I(family, info->family);
+    SET_AINFO_J(next, P2J(info->next));
+
+}
+
+TCN_IMPLEMENT_CALL(jint, File, stat)(TCN_STDARGS, jobject finfo,
+                                     jstring fname, jint wanted,
+                                     jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    TCN_ALLOC_CSTRING(fname);
+    apr_status_t rv;
+    apr_finfo_t info;
+
+    UNREFERENCED(o);
+
+    if ((rv =  apr_stat(&info, J2S(fname), wanted, p)) == APR_SUCCESS) {
+        jobject io = (*e)->NewLocalRef(e, finfo);
+        fill_finfo(e, io, &info);
+        (*e)->DeleteLocalRef(e, io);
+    }
+    TCN_FREE_CSTRING(fname);
+    return (jint)rv;
+}
+
+TCN_IMPLEMENT_CALL(jobject, File, getStat)(TCN_STDARGS, jstring fname,
+                                           jint wanted, jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    TCN_ALLOC_CSTRING(fname);
+    apr_status_t rv;
+    apr_finfo_t info;
+    jobject finfo = NULL;
+
+    UNREFERENCED(o);
+
+    if ((rv =  apr_stat(&info, J2S(fname), wanted, p)) == APR_SUCCESS) {
+        finfo = (*e)->NewObject(e, finfo_class, finfo_class_init);
+        if (finfo == NULL)
+            goto cleanup;
+        fill_finfo(e, finfo, &info);
+    }
+    else
+        tcn_ThrowAPRException(e, rv);
+cleanup:
+    TCN_FREE_CSTRING(fname);
+    return finfo;
+}
+
+TCN_IMPLEMENT_CALL(jint, File, infoGet)(TCN_STDARGS, jobject finfo,
+                                        jint wanted, jlong file)
+{
+    apr_file_t *f = J2P(file, apr_file_t *);
+    apr_status_t rv;
+    apr_finfo_t info;
+
+    UNREFERENCED(o);
+
+    if ((rv =  apr_file_info_get(&info, wanted, f)) == APR_SUCCESS) {
+        jobject io = (*e)->NewLocalRef(e, finfo);
+        fill_finfo(e, io, &info);
+        (*e)->DeleteLocalRef(e, io);
+    }
+    return (jint)rv;
+}
+
+TCN_IMPLEMENT_CALL(jobject, File, getInfo)(TCN_STDARGS, jint wanted, jlong file)
+{
+    apr_file_t *f = J2P(file, apr_file_t *);
+    apr_status_t rv;
+    apr_finfo_t  info;
+
+    UNREFERENCED(o);
+
+    if ((rv =  apr_file_info_get(&info, wanted, f)) == APR_SUCCESS) {
+        jobject finfo;
+        finfo = (*e)->NewObject(e, finfo_class, finfo_class_init);
+        if (finfo == NULL)
+            return NULL;
+        fill_finfo(e, finfo, &info);
+        return finfo;
+    }
+    else
+        tcn_ThrowAPRException(e, rv);
+    return NULL;
+}
+
+TCN_IMPLEMENT_CALL(jint, Directory, read)(TCN_STDARGS, jobject finfo,
+                                          jint wanted, jlong dir)
+{
+    apr_dir_t *d = J2P(dir, apr_dir_t *);
+    apr_status_t rv;
+    apr_finfo_t info;
+
+    UNREFERENCED(o);
+
+    if ((rv =  apr_dir_read(&info, wanted, d)) == APR_SUCCESS) {
+        jobject io = (*e)->NewLocalRef(e, finfo);
+        fill_finfo(e, io, &info);
+        if ((*e)->ExceptionCheck(e)) {
+            (*e)->ExceptionClear(e);
+        }
+        else
+            rv = APR_EGENERAL;
+        (*e)->DeleteLocalRef(e, io);
+    }
+    return (jint)rv;
+}
+
+TCN_IMPLEMENT_CALL(jboolean, Address, fill)(TCN_STDARGS,
+                                            jobject addr, jlong info)
+{
+    apr_sockaddr_t *i = J2P(info, apr_sockaddr_t *);
+    jobject ao;
+    jboolean rv = JNI_FALSE;
+
+    UNREFERENCED(o);
+
+    if (i) {
+        ao = (*e)->NewLocalRef(e, addr);
+        fill_ainfo(e, ao, i);
+        if ((*e)->ExceptionCheck(e)) {
+            (*e)->ExceptionClear(e);
+        }
+        else
+            rv = JNI_TRUE;
+        (*e)->DeleteLocalRef(e, ao);
+    }
+    return rv;
+}
+
+TCN_IMPLEMENT_CALL(jobject, Address, getInfo)(TCN_STDARGS, jlong info)
+{
+    apr_sockaddr_t *i = J2P(info, apr_sockaddr_t *);
+    jobject sockaddrObj = NULL;
+
+    UNREFERENCED(o);
+
+    /* Create the APR Error object */
+    sockaddrObj = (*e)->NewObject(e, ainfo_class, ainfo_class_init);
+    if (sockaddrObj == NULL)
+        return NULL;
+    fill_ainfo(e, sockaddrObj, i);
+    return sockaddrObj;
+}
diff --git a/connectors/jni/native/src/jnilib.c b/connectors/jni/native/src/jnilib.c
new file mode 100644
index 0000000..4ac556a
--- /dev/null
+++ b/connectors/jni/native/src/jnilib.c
@@ -0,0 +1,488 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+#include "tcn.h"
+#include "apr_version.h"
+#include "apr_file_io.h"
+#include "apr_mmap.h"
+#include "apr_atomic.h"
+
+#include "tcn_version.h"
+
+#ifdef TCN_DO_STATISTICS
+extern void sp_poll_dump_statistics();
+extern void sp_network_dump_statistics();
+extern void ssl_network_dump_statistics();
+#endif
+
+apr_pool_t *tcn_global_pool = NULL;
+static JavaVM     *tcn_global_vm = NULL;
+
+static jclass    jString_class;
+static jclass    jFinfo_class;
+static jclass    jAinfo_class;
+static jmethodID jString_init;
+static jmethodID jString_getBytes;
+
+int tcn_parent_pid = 0;
+
+/* Called by the JVM when APR_JAVA is loaded */
+JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
+{
+    JNIEnv *env;
+    apr_version_t apv;
+    int apvn;
+
+    UNREFERENCED(reserved);
+    if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_4)) {
+        return JNI_ERR;
+    }
+    tcn_global_vm = vm;
+
+    /* Before doing anything else check if we have a valid
+     * APR version. We need version 1.2.1 as minimum.
+     */
+    apr_version(&apv);
+    apvn = apv.major * 1000 + apv.minor * 100 + apv.patch;
+    if (apvn < 1201) {
+        tcn_Throw(env, "Unupported APR version (%s)",
+                  apr_version_string());
+        return JNI_ERR;
+    }
+
+
+    /* Initialize global java.lang.String class */
+    TCN_LOAD_CLASS(env, jString_class, "java/lang/String", JNI_ERR);
+    TCN_LOAD_CLASS(env, jFinfo_class, TCN_FINFO_CLASS, JNI_ERR);
+    TCN_LOAD_CLASS(env, jAinfo_class, TCN_AINFO_CLASS, JNI_ERR);
+
+    TCN_GET_METHOD(env, jString_class, jString_init,
+                   "<init>", "([B)V", JNI_ERR);
+    TCN_GET_METHOD(env, jString_class, jString_getBytes,
+                   "getBytes", "()[B", JNI_ERR);
+
+    if(tcn_load_finfo_class(env, jFinfo_class) != APR_SUCCESS)
+        return JNI_ERR;
+    if(tcn_load_ainfo_class(env, jAinfo_class) != APR_SUCCESS)
+        return JNI_ERR;
+#ifdef WIN32
+    {
+        char *ppid = getenv(TCN_PARENT_IDE);
+        if (ppid)
+            tcn_parent_pid = atoi(ppid);
+    }
+#else
+    tcn_parent_pid = getppid();
+#endif
+
+    return  JNI_VERSION_1_4;
+}
+
+
+/* Called by the JVM before the APR_JAVA is unloaded */
+JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved)
+{
+    JNIEnv *env;
+
+    UNREFERENCED(reserved);
+
+    if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_2)) {
+        return;
+    }
+    if (tcn_global_pool) {
+        TCN_UNLOAD_CLASS(env, jString_class);
+        TCN_UNLOAD_CLASS(env, jFinfo_class);
+        TCN_UNLOAD_CLASS(env, jAinfo_class);
+        apr_terminate();
+    }
+}
+
+jstring tcn_new_stringn(JNIEnv *env, const char *str, size_t l)
+{
+    jstring result;
+    jbyteArray bytes = 0;
+    size_t len = l;
+
+    if (!str)
+        return NULL;
+    if ((*env)->EnsureLocalCapacity(env, 2) < 0) {
+        return NULL; /* out of memory error */
+    }
+    if (l < 0)
+        len = strlen(str);
+    bytes = (*env)->NewByteArray(env, (jsize)len);
+    if (bytes != NULL) {
+        (*env)->SetByteArrayRegion(env, bytes, 0, (jint)len, (jbyte *)str);
+        result = (*env)->NewObject(env, jString_class, jString_init, bytes);
+        (*env)->DeleteLocalRef(env, bytes);
+        return result;
+    } /* else fall through */
+    return NULL;
+}
+
+jbyteArray tcn_new_arrayb(JNIEnv *env, const unsigned char *data, size_t len)
+{
+    jbyteArray bytes = (*env)->NewByteArray(env, (jsize)len);
+    if (bytes != NULL) {
+        (*env)->SetByteArrayRegion(env, bytes, 0, (jint)len, (jbyte *)data);
+    }
+    return bytes;
+}
+
+jobjectArray tcn_new_arrays(JNIEnv *env, size_t len)
+{
+    return (*env)->NewObjectArray(env, (jsize)len, jString_class, NULL);
+}
+
+jstring tcn_new_string(JNIEnv *env, const char *str)
+{
+    if (!str)
+        return NULL;
+    else
+        return (*env)->NewStringUTF(env, str);
+}
+
+char *tcn_get_string(JNIEnv *env, jstring jstr)
+{
+    jbyteArray bytes = NULL;
+    jthrowable exc;
+    char *result = NULL;
+
+    if ((*env)->EnsureLocalCapacity(env, 2) < 0) {
+        return NULL; /* out of memory error */
+    }
+    bytes = (*env)->CallObjectMethod(env, jstr, jString_getBytes);
+    exc = (*env)->ExceptionOccurred(env);
+    if (!exc) {
+        jint len = (*env)->GetArrayLength(env, bytes);
+        result = (char *)malloc(len + 1);
+        if (result == NULL) {
+            TCN_THROW_OS_ERROR(env);
+            (*env)->DeleteLocalRef(env, bytes);
+            return 0;
+        }
+        (*env)->GetByteArrayRegion(env, bytes, 0, len, (jbyte *)result);
+        result[len] = '\0'; /* NULL-terminate */
+    }
+    else {
+        (*env)->DeleteLocalRef(env, exc);
+    }
+    (*env)->DeleteLocalRef(env, bytes);
+
+    return result;
+}
+
+char *tcn_strdup(JNIEnv *env, jstring jstr)
+{
+    char *result = NULL;
+    const char *cjstr;
+
+    cjstr = (const char *)((*env)->GetStringUTFChars(env, jstr, 0));
+    if (cjstr) {
+        result = strdup(cjstr);
+        (*env)->ReleaseStringUTFChars(env, jstr, cjstr);
+    }
+    return result;
+}
+
+char *tcn_pstrdup(JNIEnv *env, jstring jstr, apr_pool_t *pool)
+{
+    char *result = NULL;
+    const char *cjstr;
+
+    cjstr = (const char *)((*env)->GetStringUTFChars(env, jstr, 0));
+    if (cjstr) {
+        result = apr_pstrdup(pool, cjstr);
+        (*env)->ReleaseStringUTFChars(env, jstr, cjstr);
+    }
+    return result;
+}
+
+TCN_IMPLEMENT_CALL(jboolean, Library, initialize)(TCN_STDARGS)
+{
+
+    UNREFERENCED_STDARGS;
+    if (!tcn_global_pool) {
+        apr_initialize();
+        if (apr_pool_create(&tcn_global_pool, NULL) != APR_SUCCESS) {
+            return JNI_FALSE;
+        }
+        apr_atomic_init(tcn_global_pool);
+    }
+    return JNI_TRUE;
+}
+
+TCN_IMPLEMENT_CALL(void, Library, terminate)(TCN_STDARGS)
+{
+
+    UNREFERENCED_STDARGS;
+    if (tcn_global_pool) {        
+        apr_pool_t *p = tcn_global_pool;
+        tcn_global_pool = NULL;
+#ifdef TCN_DO_STATISTICS
+        fprintf(stderr, "APR Statistical data ....\n");
+#endif
+        apr_pool_destroy(p);
+#ifdef TCN_DO_STATISTICS
+        sp_poll_dump_statistics();
+        sp_network_dump_statistics();
+        ssl_network_dump_statistics();
+        fprintf(stderr, "APR Terminated\n");
+#endif
+        apr_terminate();
+    }
+}
+
+TCN_IMPLEMENT_CALL(jlong, Library, globalPool)(TCN_STDARGS)
+{
+    UNREFERENCED_STDARGS;
+    return P2J(tcn_global_pool);
+}
+
+TCN_IMPLEMENT_CALL(jint, Library, version)(TCN_STDARGS, jint what)
+{
+    apr_version_t apv;
+
+    UNREFERENCED_STDARGS;
+    apr_version(&apv);
+
+    switch (what) {
+        case 0x01:
+            return TCN_MAJOR_VERSION;
+        break;
+        case 0x02:
+            return TCN_MINOR_VERSION;
+        break;
+        case 0x03:
+            return TCN_PATCH_VERSION;
+        break;
+        case 0x04:
+#ifdef TCN_IS_DEV_VERSION
+            return 1;
+#else
+            return 0;
+#endif
+        break;
+        case 0x11:
+            return apv.major;
+        break;
+        case 0x12:
+            return apv.minor;
+        break;
+        case 0x13:
+            return apv.patch;
+        break;
+        case 0x14:
+            return apv.is_dev;
+        break;
+    }
+    return 0;
+}
+
+TCN_IMPLEMENT_CALL(jstring, Library, versionString)(TCN_STDARGS)
+{
+    UNREFERENCED(o);
+    return AJP_TO_JSTRING(TCN_VERSION_STRING);
+}
+
+TCN_IMPLEMENT_CALL(jstring, Library, aprVersionString)(TCN_STDARGS)
+{
+    UNREFERENCED(o);
+    return AJP_TO_JSTRING(apr_version_string());
+}
+
+TCN_IMPLEMENT_CALL(jboolean, Library, has)(TCN_STDARGS, jint what)
+{
+    jboolean rv = JNI_FALSE;
+    UNREFERENCED_STDARGS;
+    switch (what) {
+        case 0:
+#if APR_HAVE_IPV6
+            rv = JNI_TRUE;
+#endif
+        break;
+        case 1:
+#if APR_HAS_SHARED_MEMORY
+            rv = JNI_TRUE;
+#endif
+        break;
+        case 2:
+#if APR_HAS_THREADS
+            rv = JNI_TRUE;
+#endif
+        break;
+        case 3:
+#if APR_HAS_SENDFILE
+            rv = JNI_TRUE;
+#endif
+        break;
+        case 4:
+#if APR_HAS_MMAP
+            rv = JNI_TRUE;
+#endif
+        break;
+        case 5:
+#if APR_HAS_FORK
+            rv = JNI_TRUE;
+#endif
+        break;
+        case 6:
+#if APR_HAS_RANDOM
+            rv = JNI_TRUE;
+#endif
+        break;
+        case 7:
+#if APR_HAS_OTHER_CHILD
+            rv = JNI_TRUE;
+#endif
+        break;
+        case 8:
+#if APR_HAS_DSO
+            rv = JNI_TRUE;
+#endif
+        break;
+        case 9:
+#if APR_HAS_SO_ACCEPTFILTER
+            rv = JNI_TRUE;
+#endif
+        break;
+        case 10:
+#if APR_HAS_UNICODE_FS
+            rv = JNI_TRUE;
+#endif
+        break;
+        case 11:
+#if APR_HAS_PROC_INVOKED
+            rv = JNI_TRUE;
+#endif
+        break;
+        case 12:
+#if APR_HAS_USER
+            rv = JNI_TRUE;
+#endif
+        break;
+        case 13:
+#if APR_HAS_LARGE_FILES
+            rv = JNI_TRUE;
+#endif
+        break;
+        case 14:
+#if APR_HAS_XTHREAD_FILES
+            rv = JNI_TRUE;
+#endif
+        break;
+        case 15:
+#if APR_HAS_OS_UUID
+            rv = JNI_TRUE;
+#endif
+        break;
+        case 16:
+#if APR_IS_BIGENDIAN
+            rv = JNI_TRUE;
+#endif
+        break;
+
+        case 17:
+#if APR_FILES_AS_SOCKETS
+            rv = JNI_TRUE;
+#endif
+        break;
+        case 18:
+#if APR_CHARSET_EBCDIC
+            rv = JNI_TRUE;
+#endif
+        break;
+        case 19:
+#if APR_TCP_NODELAY_INHERITED
+            rv = JNI_TRUE;
+#endif
+        break;
+        case 20:
+#if APR_O_NONBLOCK_INHERITED
+            rv = JNI_TRUE;
+#endif
+        break;
+    }
+    return rv;
+}
+
+TCN_IMPLEMENT_CALL(jint, Library, size)(TCN_STDARGS, jint what)
+{
+
+    UNREFERENCED_STDARGS;
+
+    switch (what) {
+        case 1:
+            return APR_SIZEOF_VOIDP;
+        break;
+        case 2:
+            return APR_PATH_MAX;
+        break;
+        case 3:
+            return APRMAXHOSTLEN;
+        break;
+        case 4:
+            return APR_MAX_IOVEC_SIZE;
+        break;
+        case 5:
+            return APR_MAX_SECS_TO_LINGER;
+        break;
+        case 6:
+            return APR_MMAP_THRESHOLD;
+        break;
+        case 7:
+            return APR_MMAP_LIMIT;
+        break;
+
+    }
+    return 0;
+}
+
+TCN_DECLARE(apr_pool_t *) tcn_get_global_pool()
+{
+    if (!tcn_global_pool) {
+        if (apr_pool_create(&tcn_global_pool, NULL) != APR_SUCCESS) {
+            return NULL;
+        }
+        apr_atomic_init(tcn_global_pool);
+    }
+    return tcn_global_pool;
+}
+
+TCN_DECLARE(jclass) tcn_get_string_class()
+{
+    return jString_class;
+}
+
+TCN_DECLARE(JavaVM *) tcn_get_java_vm()
+{
+    return tcn_global_vm;
+}
+
+TCN_DECLARE(jint) tcn_get_java_env(JNIEnv **env)
+{
+    if ((*tcn_global_vm)->GetEnv(tcn_global_vm, (void **)env,
+                                 JNI_VERSION_1_4)) {
+        return JNI_ERR;
+    }
+    return JNI_OK;
+}
diff --git a/connectors/jni/native/src/lock.c b/connectors/jni/native/src/lock.c
new file mode 100644
index 0000000..9580a9e
--- /dev/null
+++ b/connectors/jni/native/src/lock.c
@@ -0,0 +1,202 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Mladen Turk
+ * @version $Revision$, $Date$
+ */
+ 
+#include "tcn.h"
+#include "apr_proc_mutex.h"
+#include "apr_global_mutex.h"
+
+TCN_IMPLEMENT_CALL(jlong, Lock, create)(TCN_STDARGS,
+                                        jstring fname,
+                                        jint mech, jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    apr_proc_mutex_t *mutex;
+    TCN_ALLOC_CSTRING(fname);
+
+
+    UNREFERENCED(o);
+    TCN_THROW_IF_ERR(apr_proc_mutex_create(&mutex, J2S(fname),
+                                (apr_lockmech_e)mech, p), mutex);
+
+cleanup:
+    TCN_FREE_CSTRING(fname);
+    return P2J(mutex);
+}
+
+TCN_IMPLEMENT_CALL(jlong, Lock, childInit)(TCN_STDARGS,
+                                           jstring fname,
+                                           jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    apr_proc_mutex_t *mutex;
+    TCN_ALLOC_CSTRING(fname);
+
+
+    UNREFERENCED(o);
+    TCN_THROW_IF_ERR(apr_proc_mutex_child_init(&mutex,
+                                   J2S(fname), p), mutex);
+
+cleanup:
+    TCN_FREE_CSTRING(fname);
+    return P2J(mutex);
+}
+
+TCN_IMPLEMENT_CALL(jint, Lock, lock)(TCN_STDARGS, jlong mutex)
+{
+    apr_proc_mutex_t *m = J2P(mutex, apr_proc_mutex_t *);
+
+    UNREFERENCED_STDARGS;
+    return (jint)apr_proc_mutex_lock(m);
+}
+
+TCN_IMPLEMENT_CALL(jint, Lock, trylock)(TCN_STDARGS, jlong mutex)
+{
+    apr_proc_mutex_t *m = J2P(mutex, apr_proc_mutex_t *);
+
+    UNREFERENCED_STDARGS;
+    return (jint)apr_proc_mutex_trylock(m);
+}
+
+TCN_IMPLEMENT_CALL(jint, Lock, unlock)(TCN_STDARGS, jlong mutex)
+{
+    apr_proc_mutex_t *m = J2P(mutex, apr_proc_mutex_t *);
+
+    UNREFERENCED_STDARGS;
+    return (jint)apr_proc_mutex_unlock(m);
+}
+
+TCN_IMPLEMENT_CALL(jint, Lock, destoy)(TCN_STDARGS, jlong mutex)
+{
+    apr_proc_mutex_t *m = J2P(mutex, apr_proc_mutex_t *);
+
+    UNREFERENCED_STDARGS;
+    return (jint)apr_proc_mutex_destroy(m);
+}
+
+#if 0
+/* There is bug in APR implementing that function */
+TCN_IMPLEMENT_CALL(jint, Lock, cleanup)(TCN_STDARGS, jlong mutex)
+{
+   void *m = J2P(mutex, void *);
+
+    UNREFERENCED_STDARGS;
+    return (jint)apr_proc_mutex_cleanup(m);
+}
+#endif
+
+TCN_IMPLEMENT_CALL(jstring, Lock, lockfile)(TCN_STDARGS, jlong mutex)
+{
+    apr_proc_mutex_t *m = J2P(mutex, apr_proc_mutex_t *);
+    const char *s = apr_proc_mutex_lockfile(m);
+
+    UNREFERENCED_STDARGS;
+    if (s)
+        return AJP_TO_JSTRING(s);
+    else
+        return NULL;
+}
+
+TCN_IMPLEMENT_CALL(jstring, Lock, name)(TCN_STDARGS, jlong mutex)
+{
+    apr_proc_mutex_t *m = J2P(mutex, apr_proc_mutex_t *);
+
+    UNREFERENCED(o);
+    return AJP_TO_JSTRING(apr_proc_mutex_name(m));
+}
+
+TCN_IMPLEMENT_CALL(jstring, Lock, defname)(TCN_STDARGS)
+{
+
+    UNREFERENCED(o);
+    return AJP_TO_JSTRING(apr_proc_mutex_defname());
+}
+
+
+
+TCN_IMPLEMENT_CALL(jlong, Global, create)(TCN_STDARGS,
+                                          jstring fname,
+                                          jint mech, jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    apr_global_mutex_t *mutex;
+    TCN_ALLOC_CSTRING(fname);
+
+
+    UNREFERENCED(o);
+    TCN_THROW_IF_ERR(apr_global_mutex_create(&mutex, J2S(fname),
+                                (apr_lockmech_e)mech, p), mutex);
+
+cleanup:
+    TCN_FREE_CSTRING(fname);
+    return P2J(mutex);
+}
+
+TCN_IMPLEMENT_CALL(jlong, Global, childInit)(TCN_STDARGS,
+                                             jstring fname,
+                                             jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    apr_global_mutex_t *mutex;
+    TCN_ALLOC_CSTRING(fname);
+
+
+    UNREFERENCED(o);
+    TCN_THROW_IF_ERR(apr_global_mutex_child_init(&mutex,
+                                   J2S(fname), p), mutex);
+
+cleanup:
+    TCN_FREE_CSTRING(fname);
+    return P2J(mutex);
+}
+
+TCN_IMPLEMENT_CALL(jint, Global, lock)(TCN_STDARGS, jlong mutex)
+{
+    apr_global_mutex_t *m = J2P(mutex, apr_global_mutex_t *);
+
+    UNREFERENCED_STDARGS;
+    return (jint)apr_global_mutex_lock(m);
+}
+
+TCN_IMPLEMENT_CALL(jint, Global, trylock)(TCN_STDARGS, jlong mutex)
+{
+    apr_global_mutex_t *m = J2P(mutex, apr_global_mutex_t *);
+
+    UNREFERENCED_STDARGS;
+    return (jint)apr_global_mutex_trylock(m);
+}
+
+TCN_IMPLEMENT_CALL(jint, Global, unlock)(TCN_STDARGS, jlong mutex)
+{
+    apr_global_mutex_t *m = J2P(mutex, apr_global_mutex_t*);
+
+    UNREFERENCED_STDARGS;
+    return (jint)apr_global_mutex_unlock(m);
+}
+
+TCN_IMPLEMENT_CALL(jint, Global, destoy)(TCN_STDARGS, jlong mutex)
+{
+    apr_global_mutex_t *m = J2P(mutex, apr_global_mutex_t *);
+
+    UNREFERENCED_STDARGS;
+    return (jint)apr_global_mutex_destroy(m);
+}
+
diff --git a/connectors/jni/native/src/misc.c b/connectors/jni/native/src/misc.c
new file mode 100644
index 0000000..5fe7646
--- /dev/null
+++ b/connectors/jni/native/src/misc.c
@@ -0,0 +1,81 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Mladen Turk
+ * @version $Revision$, $Date$
+ */
+ 
+#include "tcn.h"
+#include "apr_time.h"
+
+TCN_IMPLEMENT_CALL(void, Time, sleep)(TCN_STDARGS, jlong t)
+{
+
+    UNREFERENCED_STDARGS;
+    apr_sleep((apr_interval_time_t)t);
+}
+
+TCN_IMPLEMENT_CALL(jint, OS, random)(TCN_STDARGS, jbyteArray buf,
+                                     jint len)
+{
+#if APR_HAS_RANDOM
+    apr_status_t rv;
+    jbyte *b = (*e)->GetPrimitiveArrayCritical(e, buf, NULL);
+
+    UNREFERENCED(o);
+    if ((rv = apr_generate_random_bytes((unsigned char *)b,
+            (apr_size_t)len)) == APR_SUCCESS)
+        (*e)->ReleasePrimitiveArrayCritical(e, buf, b, 0);
+    else
+        (*e)->ReleasePrimitiveArrayCritical(e, buf, b, JNI_ABORT);
+
+    if ((*e)->ExceptionCheck(e)) {
+        (*e)->ExceptionClear(e);
+        rv = APR_EGENERAL;
+    }
+    return (jint)rv;
+#else
+    return APR_ENOTIMPL;
+#endif
+}
+
+TCN_IMPLEMENT_CALL(jlong, Time, now)(TCN_STDARGS)
+{
+    UNREFERENCED_STDARGS;
+    return (jlong)apr_time_now();
+}
+
+TCN_IMPLEMENT_CALL(jstring, Time, rfc822)(TCN_STDARGS, jlong t)
+{
+    char ts[APR_RFC822_DATE_LEN];
+    UNREFERENCED(o);
+    if (apr_rfc822_date(ts, J2T(t)) == APR_SUCCESS)
+        return AJP_TO_JSTRING(ts);
+    else
+        return NULL;
+}
+
+TCN_IMPLEMENT_CALL(jstring, Time, ctime)(TCN_STDARGS, jlong t)
+{
+    char ts[APR_CTIME_LEN];
+    UNREFERENCED(o);
+    if (apr_ctime(ts, J2T(t)) == APR_SUCCESS)
+        return AJP_TO_JSTRING(ts);
+    else
+        return NULL;
+}
diff --git a/connectors/jni/native/src/mmap.c b/connectors/jni/native/src/mmap.c
new file mode 100644
index 0000000..9d45b6a
--- /dev/null
+++ b/connectors/jni/native/src/mmap.c
@@ -0,0 +1,102 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Mladen Turk
+ * @version $Revision$, $Date$
+ */
+ 
+#include "tcn.h"
+#include "apr_mmap.h"
+
+TCN_IMPLEMENT_CALL(jlong, Mmap, create)(TCN_STDARGS, jlong file,
+                                        jlong offset, jlong size,
+                                        jint flag, jlong pool)
+{
+#if APR_HAS_MMAP
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    apr_file_t *f = J2P(file, apr_file_t *);
+    apr_mmap_t *m = NULL;
+
+    UNREFERENCED(o);
+    TCN_THROW_IF_ERR(apr_mmap_create(&m, f, (apr_off_t)offset,
+                                     (apr_size_t)size,
+                                     (apr_uint32_t)flag, p), m);
+
+cleanup:
+    return P2J(m);
+#else
+    UNREFERENCED(o);
+    tcn_ThrowAPRException(e, APR_ENOTIMPL);
+    return 0;
+#endif
+}
+
+TCN_IMPLEMENT_CALL(jlong, Mmap, dup)(TCN_STDARGS, jlong mmap,
+                                     jlong pool)
+{
+#if APR_HAS_MMAP
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    apr_mmap_t *m = J2P(mmap, apr_mmap_t *);
+    apr_mmap_t *newm = NULL;
+
+    UNREFERENCED(o);
+    TCN_THROW_IF_ERR(apr_mmap_dup(&newm, m, p), newm);
+
+cleanup:
+    return P2J(newm);
+#else
+    UNREFERENCED(o);
+    tcn_ThrowAPRException(e, APR_ENOTIMPL);
+    return 0;
+#endif
+}
+
+TCN_IMPLEMENT_CALL(jint, Mmap, delete)(TCN_STDARGS, jlong mmap)
+{
+#if APR_HAS_MMAP
+    apr_mmap_t *m = J2P(mmap, apr_mmap_t *);
+
+    UNREFERENCED_STDARGS;
+    return apr_mmap_delete(m);
+
+#else
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(mmap);
+    return APR_ENOTIMPL;
+#endif
+}
+
+TCN_IMPLEMENT_CALL(jlong, Mmap, offset)(TCN_STDARGS, jlong mmap,
+                                        jlong offset)
+{
+#if APR_HAS_MMAP
+    apr_mmap_t *m = J2P(mmap, apr_mmap_t *);
+    void *r;
+
+    UNREFERENCED(o);
+    TCN_THROW_IF_ERR(apr_mmap_offset(&r, m, (apr_off_t)offset), r);
+
+cleanup:
+    return P2J(r);
+
+#else
+    UNREFERENCED(o);
+    tcn_ThrowAPRException(e, APR_ENOTIMPL);
+    return 0;
+#endif
+}
diff --git a/connectors/jni/native/src/multicast.c b/connectors/jni/native/src/multicast.c
new file mode 100644
index 0000000..d471477
--- /dev/null
+++ b/connectors/jni/native/src/multicast.c
@@ -0,0 +1,75 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+#include "tcn.h"
+
+TCN_IMPLEMENT_CALL(jint, Mulicast, join)(TCN_STDARGS,
+                                         jlong sock, jlong join,
+                                         jlong iface, jlong source)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    apr_sockaddr_t *ja = J2P(join, apr_sockaddr_t *);
+    apr_sockaddr_t *ia = J2P(iface, apr_sockaddr_t *);
+    apr_sockaddr_t *sa = J2P(source, apr_sockaddr_t *);
+    UNREFERENCED_STDARGS;
+    return (jint)apr_mcast_join(s->sock, ja, ia, sa);
+};
+
+TCN_IMPLEMENT_CALL(jint, Mulicast, leave)(TCN_STDARGS,
+                                          jlong sock, jlong addr,
+                                          jlong iface, jlong source)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    apr_sockaddr_t *aa = J2P(addr, apr_sockaddr_t *);
+    apr_sockaddr_t *ia = J2P(iface, apr_sockaddr_t *);
+    apr_sockaddr_t *sa = J2P(source, apr_sockaddr_t *);
+    UNREFERENCED_STDARGS;
+    return (jint)apr_mcast_leave(s->sock, aa, ia, sa);
+};
+
+TCN_IMPLEMENT_CALL(jint, Mulicast, hops)(TCN_STDARGS,
+                                         jlong sock, jint ttl)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    UNREFERENCED_STDARGS;
+    return (jint)apr_mcast_hops(s->sock, (apr_byte_t)ttl);
+};
+
+TCN_IMPLEMENT_CALL(jint, Mulicast, loopback)(TCN_STDARGS,
+                                             jlong sock, jboolean opt)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    apr_byte_t on = 0;
+    UNREFERENCED_STDARGS;
+    if (opt)
+        on = 1;
+    return (jint)apr_mcast_loopback(s->sock, on);
+};
+
+TCN_IMPLEMENT_CALL(jint, Mulicast, ointerface)(TCN_STDARGS,
+                                               jlong sock, jlong iface)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    apr_sockaddr_t *ia = J2P(iface, apr_sockaddr_t *);
+    UNREFERENCED_STDARGS;
+    return (jint)apr_mcast_interface(s->sock, ia);
+};
diff --git a/connectors/jni/native/src/network.c b/connectors/jni/native/src/network.c
new file mode 100644
index 0000000..b26d0cd
--- /dev/null
+++ b/connectors/jni/native/src/network.c
@@ -0,0 +1,1333 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+#include "tcn.h"
+
+#ifdef TCN_DO_STATISTICS
+
+#include "apr_atomic.h"
+
+static volatile apr_uint32_t sp_created  = 0;
+static volatile apr_uint32_t sp_closed   = 0;
+static volatile apr_uint32_t sp_cleared  = 0;
+static volatile apr_uint32_t sp_accepted = 0;
+static volatile apr_uint32_t sp_max_send = 0;
+static volatile apr_uint32_t sp_min_send = 10000000;
+static volatile apr_uint32_t sp_num_send = 0;
+static volatile apr_off_t    sp_tot_send = 0;
+static volatile apr_uint32_t sp_max_recv = 0;
+static volatile apr_uint32_t sp_min_recv = 10000000;
+static volatile apr_uint32_t sp_num_recv = 0;
+static volatile apr_off_t    sp_tot_recv = 0;
+static volatile apr_uint32_t sp_err_recv = 0;
+static volatile apr_uint32_t sp_tmo_recv = 0;
+static volatile apr_uint32_t sp_rst_recv = 0;
+static volatile apr_status_t sp_erl_recv = 0;
+
+static volatile apr_size_t   sf_max_send = 0;
+static volatile apr_size_t   sf_min_send = 10000000;
+static volatile apr_uint32_t sf_num_send = 0;
+static volatile apr_off_t    sf_tot_send = 0;
+
+void sp_network_dump_statistics()
+{
+    fprintf(stderr, "Network Statistics ......\n");
+    fprintf(stderr, "Sockets created         : %d\n", sp_created);
+    fprintf(stderr, "Sockets accepted        : %d\n", sp_accepted);
+    fprintf(stderr, "Sockets closed          : %d\n", sp_closed);
+    fprintf(stderr, "Sockets cleared         : %d\n", sp_cleared);
+    fprintf(stderr, "Total send calls        : %d\n", sp_num_send);
+    fprintf(stderr, "Minimum send length     : %d\n", sp_min_send);
+    fprintf(stderr, "Maximum send length     : %d\n", sp_max_send);
+    fprintf(stderr, "Average send length     : %.2f\n", (double)sp_tot_send/(double)sp_num_send);
+    fprintf(stderr, "Total recv calls        : %d\n", sp_num_recv);
+    fprintf(stderr, "Minimum recv length     : %d\n", sp_min_recv);
+    fprintf(stderr, "Maximum recv length     : %d\n", sp_max_recv);
+    fprintf(stderr, "Average recv length     : %.2f\n", (double)sp_tot_recv/(double)sp_num_recv);
+    fprintf(stderr, "Receive timeouts        : %d\n", sp_tmo_recv);
+    fprintf(stderr, "Receive errors          : %d\n", sp_err_recv);
+    fprintf(stderr, "Receive resets          : %d\n", sp_rst_recv);
+    fprintf(stderr, "Last receive error      : %d\n", sp_erl_recv);
+
+    fprintf(stderr, "Total sendfile calls    : %d\n", sf_num_send);
+    fprintf(stderr, "Minimum sendfile lenght : %" APR_SIZE_T_FMT "\n", sf_min_send);
+    fprintf(stderr, "Maximum sendfile lenght : %" APR_SIZE_T_FMT "\n", sf_max_send);
+
+}
+
+#endif /* TCN_DO_STATISTICS */
+
+static apr_status_t sp_socket_cleanup(void *data)
+{
+    tcn_socket_t *s = (tcn_socket_t *)data;
+
+    if (s->net && s->net->cleanup)
+        (*s->net->cleanup)(s->opaque);
+    if (s->sock) {
+        apr_socket_t *as = s->sock;
+        s->sock = NULL;
+        apr_socket_close(as);
+    }
+#ifdef TCN_DO_STATISTICS
+    apr_atomic_inc32(&sp_cleared);
+#endif
+    return APR_SUCCESS;
+}
+
+#if defined(DEBUG) || defined(_DEBUG)
+static APR_INLINE apr_status_t APR_THREAD_FUNC
+APR_socket_send(apr_socket_t *sock, const char *buf, apr_size_t *len)
+{
+    return apr_socket_send(sock, buf, len);
+}
+
+static APR_INLINE apr_status_t APR_THREAD_FUNC
+APR_socket_recv(apr_socket_t *sock, char *buf, apr_size_t *len)
+{
+    return apr_socket_recv(sock, buf, len);
+}
+
+static APR_INLINE apr_status_t APR_THREAD_FUNC
+APR_socket_sendv(apr_socket_t *sock, const struct iovec *vec,
+                 apr_int32_t nvec, apr_size_t *len)
+{
+    return apr_socket_sendv(sock, vec, nvec, len);
+}
+
+static APR_INLINE apr_status_t APR_THREAD_FUNC
+APR_socket_shutdown(apr_socket_t *sock, apr_shutdown_how_e how)
+{
+    return apr_socket_shutdown(sock, how);
+}
+
+static APR_INLINE apr_status_t APR_THREAD_FUNC
+APR_socket_timeout_set(apr_socket_t *sock, apr_interval_time_t t)
+{
+    return apr_socket_timeout_set(sock, t);
+}
+
+static APR_INLINE apr_status_t APR_THREAD_FUNC
+APR_socket_timeout_get(apr_socket_t *sock, apr_interval_time_t *t)
+{
+    return apr_socket_timeout_get(sock, t);
+}
+
+static APR_INLINE apr_status_t APR_THREAD_FUNC
+APR_socket_opt_set(apr_socket_t *sock, apr_int32_t opt, apr_int32_t on)
+{
+    return apr_socket_opt_set(sock, opt, on);
+}
+
+static APR_INLINE apr_status_t APR_THREAD_FUNC
+APR_socket_opt_get(apr_socket_t *sock, apr_int32_t opt, apr_int32_t *on)
+{
+    return apr_socket_opt_get(sock, opt, on);
+}
+
+#else
+#define APR_socket_send         apr_socket_send
+#define APR_socket_recv         apr_socket_recv
+#define APR_socket_sendv        apr_socket_sendv
+#define APR_socket_shutdown     apr_socket_shutdown
+#define APR_socket_timeout_set  apr_socket_timeout_set
+#define APR_socket_timeout_get  apr_socket_timeout_get
+#define APR_socket_opt_set      apr_socket_opt_set
+#define APR_socket_opt_get      apr_socket_opt_get
+#endif
+
+static tcn_nlayer_t apr_socket_layer = {
+    TCN_SOCKET_APR,
+    NULL,
+    NULL,
+    APR_socket_shutdown,
+    APR_socket_opt_get,
+    APR_socket_opt_set,
+    APR_socket_timeout_get,
+    APR_socket_timeout_set,
+    APR_socket_send,
+    APR_socket_sendv,
+    APR_socket_recv
+};
+
+TCN_IMPLEMENT_CALL(jlong, Socket, create)(TCN_STDARGS, jint family,
+                                          jint type, jint protocol,
+                                          jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    apr_pool_t *c = NULL;
+    apr_socket_t *s = NULL;
+    tcn_socket_t *a = NULL;
+    apr_int32_t f, t;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(pool != 0);
+    GET_S_FAMILY(f, family);
+    GET_S_TYPE(t, type);
+
+    TCN_THROW_IF_ERR(apr_pool_create(&c, p), c);
+
+    a = (tcn_socket_t *)apr_pcalloc(c, sizeof(tcn_socket_t));
+    TCN_CHECK_ALLOCATED(a);
+    TCN_THROW_IF_ERR(apr_pool_create(&a->child, c), a->child);
+    a->pool = c;
+
+    if (family >= 0) {
+        a->net = &apr_socket_layer;
+        TCN_THROW_IF_ERR(apr_socket_create(&s,
+                         f, t, protocol, c), a);
+    }
+    apr_pool_cleanup_register(c, (const void *)a,
+                              sp_socket_cleanup,
+                              apr_pool_cleanup_null);
+
+#ifdef TCN_DO_STATISTICS
+    sp_created++;
+#endif
+    a->sock = s;
+    if (family >= 0)
+        a->net = &apr_socket_layer;
+    a->opaque   = s;
+
+    return P2J(a);
+cleanup:
+    if (c)
+        apr_pool_destroy(c);
+    return 0;
+
+}
+
+TCN_IMPLEMENT_CALL(void, Socket, destroy)(TCN_STDARGS, jlong sock)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    apr_socket_t *as;
+    UNREFERENCED_STDARGS;
+    TCN_ASSERT(sock != 0);
+
+    as = s->sock;
+    s->sock = NULL;
+    apr_pool_cleanup_kill(s->pool, s, sp_socket_cleanup);
+    if (s->net && s->net->cleanup) {
+        (*s->net->cleanup)(s->opaque);
+        s->net = NULL;
+    }
+    if (as) {
+        apr_socket_close(as);
+    }
+
+    apr_pool_destroy(s->pool);
+}
+
+TCN_IMPLEMENT_CALL(jlong, Socket, pool)(TCN_STDARGS, jlong sock)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    apr_pool_t *n;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(sock != 0);
+    TCN_THROW_IF_ERR(apr_pool_create(&n, s->pool), n);
+cleanup:
+    return P2J(n);
+}
+
+TCN_IMPLEMENT_CALL(jlong, Socket, get)(TCN_STDARGS, jlong sock, jint what)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    UNREFERENCED_STDARGS;
+    TCN_ASSERT(sock != 0);
+
+    switch (what) {
+        case TCN_SOCKET_GET_POOL:
+            return P2J(s->pool);
+        break;
+        case TCN_SOCKET_GET_IMPL:
+            return P2J(s->opaque);
+        break;
+        case TCN_SOCKET_GET_APRS:
+            return P2J(s->sock);
+        break;
+        case TCN_SOCKET_GET_TYPE:
+            return (jlong)(s->net->type);
+        break;
+    }
+    return 0;
+}
+
+TCN_IMPLEMENT_CALL(jint, Socket, shutdown)(TCN_STDARGS, jlong sock,
+                                           jint how)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+
+    UNREFERENCED_STDARGS;
+    TCN_ASSERT(sock != 0);
+    return (jint)(*s->net->shutdown)(s->opaque, how);
+}
+
+TCN_IMPLEMENT_CALL(jint, Socket, close)(TCN_STDARGS, jlong sock)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    jint rv = APR_SUCCESS;
+    apr_socket_t *as;
+    UNREFERENCED_STDARGS;
+    TCN_ASSERT(sock != 0);
+
+    as = s->sock;
+    s->sock = NULL;
+    apr_pool_cleanup_kill(s->pool, s, sp_socket_cleanup);
+    if (s->child) {
+        apr_pool_clear(s->child);
+    }
+#ifdef TCN_DO_STATISTICS
+    apr_atomic_inc32(&sp_closed);
+#endif
+    if (s->net && s->net->close) {
+        rv = (*s->net->close)(s->opaque);
+        s->net = NULL;
+    }
+    if (as) {
+        rv = (jint)apr_socket_close(as);
+    }
+    return rv;
+}
+
+TCN_IMPLEMENT_CALL(jint, Socket, bind)(TCN_STDARGS, jlong sock,
+                                       jlong sa)
+{
+    jint rv = APR_SUCCESS;
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    apr_sockaddr_t *a = J2P(sa, apr_sockaddr_t *);
+
+    UNREFERENCED_STDARGS;
+    TCN_ASSERT(sock != 0);
+    TCN_ASSERT(s->sock != NULL);
+    rv = (jint)apr_socket_bind(s->sock, a);
+    return rv;
+}
+
+TCN_IMPLEMENT_CALL(jint, Socket, listen)(TCN_STDARGS, jlong sock,
+                                         jint backlog)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+
+    UNREFERENCED_STDARGS;
+    TCN_ASSERT(sock != 0);
+    TCN_ASSERT(s->sock != NULL);
+    return (jint)apr_socket_listen(s->sock, backlog);
+}
+
+TCN_IMPLEMENT_CALL(jlong, Socket, acceptx)(TCN_STDARGS, jlong sock,
+                                           jlong pool)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    apr_pool_t   *p = J2P(pool, apr_pool_t *);
+    apr_socket_t *n = NULL;
+    tcn_socket_t *a = NULL;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(sock != 0);
+
+    if (s->net->type == TCN_SOCKET_APR) {
+        TCN_ASSERT(s->sock != NULL);
+        a = (tcn_socket_t *)apr_pcalloc(p, sizeof(tcn_socket_t));
+        TCN_CHECK_ALLOCATED(a);
+        a->pool   = p;
+        apr_pool_cleanup_register(a->pool, (const void *)a,
+                                  sp_socket_cleanup,
+                                  apr_pool_cleanup_null);
+
+        TCN_THROW_IF_ERR(apr_socket_accept(&n, s->sock, p), n);
+    }
+    else {
+        tcn_ThrowAPRException(e, APR_ENOTIMPL);
+        goto cleanup;
+    }
+    if (n) {
+#ifdef TCN_DO_STATISTICS
+        apr_atomic_inc32(&sp_accepted);
+#endif
+        a->net    = &apr_socket_layer;
+        a->sock   = n;
+        a->opaque = n;
+    }
+
+cleanup:
+    return P2J(a);
+}
+
+TCN_IMPLEMENT_CALL(jlong, Socket, accept)(TCN_STDARGS, jlong sock)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    apr_pool_t   *p = NULL;
+    apr_socket_t *n = NULL;
+    tcn_socket_t *a = NULL;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(sock != 0);
+
+    TCN_THROW_IF_ERR(apr_pool_create(&p, s->child), p);
+    if (s->net->type == TCN_SOCKET_APR) {
+        TCN_ASSERT(s->sock != NULL);
+        a = (tcn_socket_t *)apr_pcalloc(p, sizeof(tcn_socket_t));
+        TCN_CHECK_ALLOCATED(a);
+        TCN_THROW_IF_ERR(apr_socket_accept(&n, s->sock, p), n);
+
+        a->pool = p;
+        apr_pool_cleanup_register(a->pool, (const void *)a,
+                                  sp_socket_cleanup,
+                                  apr_pool_cleanup_null);
+
+    }
+    else {
+        tcn_ThrowAPRException(e, APR_ENOTIMPL);
+        goto cleanup;
+    }
+    if (n) {
+#ifdef TCN_DO_STATISTICS
+        apr_atomic_inc32(&sp_accepted);
+#endif
+        a->net    = &apr_socket_layer;
+        a->sock   = n;
+        a->opaque = n;
+    }
+    return P2J(a);
+cleanup:
+    if (p && s->sock)
+        apr_pool_destroy(p);
+    return 0;
+}
+
+TCN_IMPLEMENT_CALL(jint, Socket, connect)(TCN_STDARGS, jlong sock,
+                                          jlong sa)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    apr_sockaddr_t *a = J2P(sa, apr_sockaddr_t *);
+
+    UNREFERENCED_STDARGS;
+    TCN_ASSERT(sock != 0);
+    TCN_ASSERT(s->sock != NULL);
+    return (jint)apr_socket_connect(s->sock, a);
+}
+
+TCN_IMPLEMENT_CALL(jint, Socket, send)(TCN_STDARGS, jlong sock,
+                                      jbyteArray buf, jint offset, jint tosend)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    apr_size_t nbytes = (apr_size_t)tosend;
+    apr_status_t ss;
+
+    UNREFERENCED(o);
+    if (!sock) {
+        tcn_ThrowAPRException(e, APR_ENOTSOCK);
+        return -(jint)APR_ENOTSOCK;
+    }
+    TCN_ASSERT(s->opaque != NULL);
+#ifdef TCN_DO_STATISTICS
+    sp_max_send = TCN_MAX(sp_max_send, nbytes);
+    sp_min_send = TCN_MIN(sp_min_send, nbytes);
+    sp_tot_send += nbytes;
+    sp_num_send++;
+#endif
+
+    if (tosend <= TCN_BUFFER_SZ) {
+        jbyte sb[TCN_BUFFER_SZ];
+        (*e)->GetByteArrayRegion(e, buf, offset, tosend, &sb[0]);
+        ss = (*s->net->send)(s->opaque, (const char *)&sb[0], &nbytes);
+    }
+    else {
+        jbyte *sb = (jbyte *)malloc(nbytes);
+        if (sb == NULL)
+            return -APR_ENOMEM;
+        (*e)->GetByteArrayRegion(e, buf, offset, tosend, sb);
+        ss = (*s->net->send)(s->opaque, (const char *)sb, &nbytes);
+        free(sb);
+    }
+    if (ss == APR_SUCCESS)
+        return (jint)nbytes;
+    else {
+        TCN_ERROR_WRAP(ss);
+        return -(jint)ss;
+    }
+}
+
+TCN_IMPLEMENT_CALL(void, Socket, setsbb)(TCN_STDARGS, jlong sock,
+                                         jobject buf)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    UNREFERENCED(o);
+    if (!sock) {
+        tcn_ThrowAPRException(e, APR_ENOTSOCK);
+        return;
+    }
+    TCN_ASSERT(s->opaque != NULL);
+    if (buf)
+        s->jsbbuff = (char *)(*e)->GetDirectBufferAddress(e, buf);
+    else
+        s->jsbbuff = NULL;
+}
+
+TCN_IMPLEMENT_CALL(void, Socket, setrbb)(TCN_STDARGS, jlong sock,
+                                         jobject buf)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    UNREFERENCED(o);
+    if (!sock) {
+        tcn_ThrowAPRException(e, APR_ENOTSOCK);
+        return;
+    }
+    TCN_ASSERT(s->opaque != NULL);
+    if (buf)
+        s->jrbbuff = (char *)(*e)->GetDirectBufferAddress(e, buf);
+    else
+        s->jrbbuff = NULL;
+}
+
+TCN_IMPLEMENT_CALL(jint, Socket, sendb)(TCN_STDARGS, jlong sock,
+                                        jobject buf, jint offset, jint len)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    apr_size_t nbytes = (apr_size_t)len;
+    apr_size_t sent = 0;
+    char *bytes;
+    apr_status_t ss = APR_SUCCESS;
+
+    UNREFERENCED(o);
+    if (!sock) {
+        tcn_ThrowAPRException(e, APR_ENOTSOCK);
+        return -(jint)APR_ENOTSOCK;
+    }
+    TCN_ASSERT(s->opaque != NULL);
+    TCN_ASSERT(buf != NULL);
+#ifdef TCN_DO_STATISTICS
+    sp_max_send = TCN_MAX(sp_max_send, nbytes);
+    sp_min_send = TCN_MIN(sp_min_send, nbytes);
+    sp_tot_send += nbytes;
+    sp_num_send++;
+#endif
+
+    bytes  = (char *)(*e)->GetDirectBufferAddress(e, buf);
+
+    while (sent < nbytes) {
+        apr_size_t wr = nbytes - sent;
+        ss = (*s->net->send)(s->opaque, bytes + offset + sent, &wr);
+        if (ss != APR_SUCCESS)
+            break;
+        sent += wr;
+    }
+
+    if (ss == APR_SUCCESS)
+        return (jint)sent;
+    else {
+        TCN_ERROR_WRAP(ss);
+        return -(jint)ss;
+    }
+}
+
+TCN_IMPLEMENT_CALL(jint, Socket, sendbb)(TCN_STDARGS, jlong sock,
+                                         jint offset, jint len)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    apr_size_t nbytes = (apr_size_t)len;
+    apr_size_t sent = 0;
+    apr_status_t ss = APR_SUCCESS;
+
+    UNREFERENCED(o);
+    if (!sock) {
+        tcn_ThrowAPRException(e, APR_ENOTSOCK);
+        return -(jint)APR_ENOTSOCK;
+    }
+    TCN_ASSERT(s->opaque != NULL);
+    TCN_ASSERT(s->jsbbuff != NULL);
+#ifdef TCN_DO_STATISTICS
+    sp_max_send = TCN_MAX(sp_max_send, nbytes);
+    sp_min_send = TCN_MIN(sp_min_send, nbytes);
+    sp_tot_send += nbytes;
+    sp_num_send++;
+#endif
+
+    while (sent < nbytes) {
+        apr_size_t wr = nbytes - sent;
+        ss = (*s->net->send)(s->opaque, s->jsbbuff + offset + sent, &wr);
+        if (ss != APR_SUCCESS)
+            break;
+        sent += wr;
+    }
+    if (ss == APR_SUCCESS)
+        return (jint)sent;
+    else {
+        TCN_ERROR_WRAP(ss);
+        return -(jint)ss;
+    }
+}
+
+TCN_IMPLEMENT_CALL(jint, Socket, sendv)(TCN_STDARGS, jlong sock,
+                                        jobjectArray bufs)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    jsize nvec;
+    jsize i;
+    struct iovec vec[APR_MAX_IOVEC_SIZE];
+    jobject ba[APR_MAX_IOVEC_SIZE];
+    apr_size_t written = 0;
+    apr_status_t ss;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(sock != 0);
+    TCN_ASSERT(s->opaque != NULL);
+
+    nvec = (*e)->GetArrayLength(e, bufs);
+    if (nvec >= APR_MAX_IOVEC_SIZE)
+        return (jint)(-APR_ENOMEM);
+
+    for (i = 0; i < nvec; i++) {
+        ba[i] = (*e)->GetObjectArrayElement(e, bufs, i);
+        vec[i].iov_len  = (*e)->GetArrayLength(e, ba[i]);
+        vec[i].iov_base = (*e)->GetByteArrayElements(e, ba[i], NULL);
+    }
+
+    ss = (*s->net->sendv)(s->opaque, vec, nvec, &written);
+
+    for (i = 0; i < nvec; i++) {
+        (*e)->ReleaseByteArrayElements(e, ba[i], vec[i].iov_base, JNI_ABORT);
+    }
+    if (ss == APR_SUCCESS)
+        return (jint)written;
+    else {
+        TCN_ERROR_WRAP(ss);
+        return -(jint)ss;
+    }
+}
+
+TCN_IMPLEMENT_CALL(jint, Socket, sendto)(TCN_STDARGS, jlong sock,
+                                         jlong where, jint flag,
+                                         jbyteArray buf, jint offset, jint tosend)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    apr_sockaddr_t *w = J2P(where, apr_sockaddr_t *);
+    apr_size_t nbytes = (apr_size_t)tosend;
+    jbyte *bytes;
+    apr_int32_t nb;
+    apr_status_t ss;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(sock != 0);
+    TCN_ASSERT(s->sock != NULL);
+
+    bytes = (*e)->GetByteArrayElements(e, buf, NULL);
+    TCN_ASSERT(bytes != NULL);
+    apr_socket_opt_get(s->sock, APR_SO_NONBLOCK, &nb);
+    if (nb)
+         bytes = (*e)->GetPrimitiveArrayCritical(e, buf, NULL);
+    else
+         bytes = (*e)->GetByteArrayElements(e, buf, NULL);
+    ss = apr_socket_sendto(s->sock, w, flag, (char *)(bytes + offset), &nbytes);
+
+    if (nb)
+        (*e)->ReleasePrimitiveArrayCritical(e, buf, bytes, 0);
+    else
+        (*e)->ReleaseByteArrayElements(e, buf, bytes, JNI_ABORT);
+    if (ss == APR_SUCCESS)
+        return (jint)nbytes;
+    else {
+        TCN_ERROR_WRAP(ss);
+        return -(jint)ss;
+    }
+}
+
+TCN_IMPLEMENT_CALL(jint, Socket, recv)(TCN_STDARGS, jlong sock,
+                                       jbyteArray buf, jint offset, jint toread)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    apr_size_t nbytes = (apr_size_t)toread;
+    apr_status_t ss;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(sock != 0);
+    TCN_ASSERT(s->opaque != NULL);
+
+    if (toread <= TCN_BUFFER_SZ) {
+        char sb[TCN_BUFFER_SZ];
+
+        if ((ss = (*s->net->recv)(s->opaque, sb, &nbytes)) == APR_SUCCESS)
+            (*e)->SetByteArrayRegion(e, buf, offset, (jsize)nbytes, (jbyte*)&sb[0]);
+    }
+    else {
+        jbyte *bytes = (*e)->GetByteArrayElements(e, buf, NULL);
+        if ((ss = (*s->net->recv)(s->opaque, (char*)(bytes + offset),
+                                  &nbytes)) == APR_SUCCESS)
+            (*e)->ReleaseByteArrayElements(e, buf, bytes,
+                                           nbytes ? 0 : JNI_ABORT);
+    }
+#ifdef TCN_DO_STATISTICS
+    if (ss == APR_SUCCESS) {
+        sp_max_recv = TCN_MAX(sp_max_recv, nbytes);
+        sp_min_recv = TCN_MIN(sp_min_recv, nbytes);
+        sp_tot_recv += nbytes;
+        sp_num_recv++;
+    }
+    else {
+        if (APR_STATUS_IS_ETIMEDOUT(ss) ||
+            APR_STATUS_IS_TIMEUP(ss))
+            sp_tmo_recv++;
+        else if (APR_STATUS_IS_ECONNABORTED(ss) ||
+                 APR_STATUS_IS_ECONNRESET(ss) ||
+                 APR_STATUS_IS_EOF(ss))
+            sp_rst_recv++;
+        else {
+            sp_err_recv++;
+            sp_erl_recv = ss;
+        }
+    }
+#endif
+    if (ss == APR_SUCCESS)
+        return (jint)nbytes;
+    else {
+        TCN_ERROR_WRAP(ss);
+        return -(jint)ss;
+    }
+}
+
+TCN_IMPLEMENT_CALL(jint, Socket, recvt)(TCN_STDARGS, jlong sock,
+                                        jbyteArray buf, jint offset,
+                                        jint toread, jlong timeout)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    apr_size_t nbytes = (apr_size_t)toread;
+    apr_status_t ss;
+    apr_interval_time_t pt;
+    apr_interval_time_t nt = J2T(timeout);
+
+    UNREFERENCED(o);
+    TCN_ASSERT(sock != 0);
+    TCN_ASSERT(s->opaque != NULL);
+    TCN_ASSERT(buf != NULL);
+
+    if ((ss = (*s->net->timeout_get)(s->opaque, &pt)) != APR_SUCCESS) {
+        TCN_ERROR_WRAP(ss);
+        return -(jint)ss;
+    }
+    if (pt != nt) {
+        if ((ss = (*s->net->timeout_set)(s->opaque, nt)) != APR_SUCCESS)
+            goto cleanup;
+    }
+    if (toread <= TCN_BUFFER_SZ) {
+        jbyte sb[TCN_BUFFER_SZ];
+        if ((ss = (*s->net->recv)(s->opaque, (char *)&sb[0], &nbytes)) == APR_SUCCESS)
+            (*e)->SetByteArrayRegion(e, buf, offset, (jsize)nbytes, &sb[0]);
+    }
+    else {
+        jbyte *sb = (jbyte *)malloc(nbytes);
+        if (sb == NULL)
+            return -APR_ENOMEM;
+        if ((ss = (*s->net->recv)(s->opaque, (char *)sb, &nbytes)) == APR_SUCCESS)
+            (*e)->SetByteArrayRegion(e, buf, offset, (jsize)nbytes, &sb[0]);
+        free(sb);
+    }
+    if (pt != nt) {
+        if ((ss = (*s->net->timeout_set)(s->opaque, pt)) != APR_SUCCESS)
+            goto cleanup;
+    }
+
+#ifdef TCN_DO_STATISTICS
+    if (ss == APR_SUCCESS) {
+        sp_max_recv = TCN_MAX(sp_max_recv, nbytes);
+        sp_min_recv = TCN_MIN(sp_min_recv, nbytes);
+        sp_tot_recv += nbytes;
+        sp_num_recv++;
+    }
+    else {
+        if (APR_STATUS_IS_ETIMEDOUT(ss) ||
+            APR_STATUS_IS_TIMEUP(ss))
+            sp_tmo_recv++;
+        else if (APR_STATUS_IS_ECONNABORTED(ss) ||
+                 APR_STATUS_IS_ECONNRESET(ss) ||
+                 APR_STATUS_IS_EOF(ss))
+            sp_rst_recv++;
+        else {
+            sp_err_recv++;
+            sp_erl_recv = ss;
+        }
+    }
+#endif
+cleanup:
+    if (ss == APR_SUCCESS)
+        return (jint)nbytes;
+    else {
+        TCN_ERROR_WRAP(ss);
+        return -(jint)ss;
+    }
+}
+
+TCN_IMPLEMENT_CALL(jint, Socket, recvb)(TCN_STDARGS, jlong sock,
+                                        jobject buf, jint offset, jint len)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    apr_status_t ss;
+    apr_size_t nbytes = (apr_size_t)len;
+    char *bytes;
+
+    UNREFERENCED(o);
+    if (!sock) {
+        tcn_ThrowAPRException(e, APR_ENOTSOCK);
+        return -(jint)APR_ENOTSOCK;
+    }
+    TCN_ASSERT(s->opaque != NULL);
+    TCN_ASSERT(buf != NULL);
+
+    bytes  = (char *)(*e)->GetDirectBufferAddress(e, buf);
+    TCN_ASSERT(bytes != NULL);
+    ss = (*s->net->recv)(s->opaque, bytes + offset, &nbytes);
+#ifdef TCN_DO_STATISTICS
+    if (ss == APR_SUCCESS) {
+        sp_max_recv = TCN_MAX(sp_max_recv, nbytes);
+        sp_min_recv = TCN_MIN(sp_min_recv, nbytes);
+        sp_tot_recv += nbytes;
+        sp_num_recv++;
+    }
+    else {
+        if (APR_STATUS_IS_ETIMEDOUT(ss) ||
+            APR_STATUS_IS_TIMEUP(ss))
+            sp_tmo_recv++;
+        else if (APR_STATUS_IS_ECONNABORTED(ss) ||
+                 APR_STATUS_IS_ECONNRESET(ss) ||
+                 APR_STATUS_IS_EOF(ss))
+            sp_rst_recv++;
+        else {
+            sp_err_recv++;
+            sp_erl_recv = ss;
+        }
+    }
+#endif
+    if (ss == APR_SUCCESS)
+        return (jint)nbytes;
+    else {
+        TCN_ERROR_WRAP(ss);
+        return -(jint)ss;
+    }
+}
+
+TCN_IMPLEMENT_CALL(jint, Socket, recvbb)(TCN_STDARGS, jlong sock,
+                                         jint offset, jint len)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    apr_status_t ss;
+    apr_size_t nbytes = (apr_size_t)len;
+
+    UNREFERENCED(o);
+    if (!sock) {
+        tcn_ThrowAPRException(e, APR_ENOTSOCK);
+        return -(jint)APR_ENOTSOCK;
+    }
+    TCN_ASSERT(sock != 0);
+    TCN_ASSERT(s->opaque != NULL);
+    TCN_ASSERT(s->jrbbuff != NULL);
+
+    ss = (*s->net->recv)(s->opaque, s->jrbbuff + offset, &nbytes);
+#ifdef TCN_DO_STATISTICS
+    if (ss == APR_SUCCESS) {
+        sp_max_recv = TCN_MAX(sp_max_recv, nbytes);
+        sp_min_recv = TCN_MIN(sp_min_recv, nbytes);
+        sp_tot_recv += nbytes;
+        sp_num_recv++;
+    }
+    else {
+        if (APR_STATUS_IS_ETIMEDOUT(ss) ||
+            APR_STATUS_IS_TIMEUP(ss))
+            sp_tmo_recv++;
+        else if (APR_STATUS_IS_ECONNABORTED(ss) ||
+                 APR_STATUS_IS_ECONNRESET(ss) ||
+                 APR_STATUS_IS_EOF(ss))
+            sp_rst_recv++;
+        else {
+            sp_err_recv++;
+            sp_erl_recv = ss;
+        }
+    }
+#endif
+    if (ss == APR_SUCCESS)
+        return (jint)nbytes;
+    else {
+        TCN_ERROR_WRAP(ss);
+        return -(jint)ss;
+    }
+}
+
+TCN_IMPLEMENT_CALL(jint, Socket, recvbt)(TCN_STDARGS, jlong sock,
+                                         jobject buf, jint offset,
+                                         jint len, jlong timeout)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    apr_status_t ss;
+    apr_size_t nbytes = (apr_size_t)len;
+    char *bytes;
+    apr_interval_time_t pt;
+    apr_interval_time_t nt = J2T(timeout);
+
+    UNREFERENCED(o);
+    if (!sock) {
+        tcn_ThrowAPRException(e, APR_ENOTSOCK);
+        return -(jint)APR_ENOTSOCK;
+    }
+    TCN_ASSERT(buf != NULL);
+    TCN_ASSERT(s->opaque != NULL);
+
+    bytes  = (char *)(*e)->GetDirectBufferAddress(e, buf);
+    TCN_ASSERT(bytes != NULL);
+
+    if ((ss = (*s->net->timeout_get)(s->opaque, &pt)) != APR_SUCCESS) {
+        TCN_ERROR_WRAP(ss);
+        return -(jint)ss;
+    }
+    if (pt != nt) {
+        if ((ss = (*s->net->timeout_set)(s->opaque, nt)) != APR_SUCCESS) {
+            TCN_ERROR_WRAP(ss);
+            return -(jint)ss;
+        }
+    }
+    ss = (*s->net->recv)(s->opaque, bytes + offset, &nbytes);
+    if (pt != nt) {
+        if ((ss = (*s->net->timeout_set)(s->opaque, pt)) != APR_SUCCESS) {
+            TCN_ERROR_WRAP(ss);
+            return -(jint)ss;
+        }
+    }
+
+#ifdef TCN_DO_STATISTICS
+    if (ss == APR_SUCCESS) {
+        sp_max_recv = TCN_MAX(sp_max_recv, nbytes);
+        sp_min_recv = TCN_MIN(sp_min_recv, nbytes);
+        sp_tot_recv += nbytes;
+        sp_num_recv++;
+    }
+    else {
+        if (APR_STATUS_IS_ETIMEDOUT(ss) ||
+            APR_STATUS_IS_TIMEUP(ss))
+            sp_tmo_recv++;
+        else if (APR_STATUS_IS_ECONNABORTED(ss) ||
+                 APR_STATUS_IS_ECONNRESET(ss) ||
+                 APR_STATUS_IS_EOF(ss))
+            sp_rst_recv++;
+        else {
+            sp_err_recv++;
+            sp_erl_recv = ss;
+        }
+    }
+#endif
+    if (ss == APR_SUCCESS)
+        return (jint)nbytes;
+    else {
+        TCN_ERROR_WRAP(ss);
+        return -(jint)ss;
+    }
+}
+
+TCN_IMPLEMENT_CALL(jint, Socket, recvbbt)(TCN_STDARGS, jlong sock,
+                                          jint offset,
+                                          jint len, jlong timeout)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    apr_status_t ss;
+    apr_size_t nbytes = (apr_size_t)len;
+    apr_interval_time_t pt;
+    apr_interval_time_t nt = J2T(timeout);
+
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(o);
+    if (!sock) {
+        tcn_ThrowAPRException(e, APR_ENOTSOCK);
+        return -(jint)APR_ENOTSOCK;
+    }
+    TCN_ASSERT(s->jrbbuff != NULL);
+    TCN_ASSERT(s->opaque != NULL);
+
+
+    if ((ss = (*s->net->timeout_get)(s->opaque, &pt)) != APR_SUCCESS) {
+        TCN_ERROR_WRAP(ss);
+        return -(jint)ss;
+    }
+    if (pt != nt) {
+        if ((ss = (*s->net->timeout_set)(s->opaque, nt)) != APR_SUCCESS) {
+            TCN_ERROR_WRAP(ss);
+            return -(jint)ss;
+        }
+    }
+    ss = (*s->net->recv)(s->opaque, s->jrbbuff + offset, &nbytes);
+    if (pt != nt) {
+        if ((ss = (*s->net->timeout_set)(s->opaque, pt)) != APR_SUCCESS) {
+            TCN_ERROR_WRAP(ss);
+            return -(jint)ss;
+        }
+    }
+
+#ifdef TCN_DO_STATISTICS
+    if (ss == APR_SUCCESS) {
+        sp_max_recv = TCN_MAX(sp_max_recv, nbytes);
+        sp_min_recv = TCN_MIN(sp_min_recv, nbytes);
+        sp_tot_recv += nbytes;
+        sp_num_recv++;
+    }
+    else {
+        if (APR_STATUS_IS_ETIMEDOUT(ss) ||
+            APR_STATUS_IS_TIMEUP(ss))
+            sp_tmo_recv++;
+        else if (APR_STATUS_IS_ECONNABORTED(ss) ||
+                 APR_STATUS_IS_ECONNRESET(ss) ||
+                 APR_STATUS_IS_EOF(ss))
+            sp_rst_recv++;
+        else {
+            sp_err_recv++;
+            sp_erl_recv = ss;
+        }
+    }
+#endif
+    if (ss == APR_SUCCESS)
+        return (jint)nbytes;
+    else {
+        TCN_ERROR_WRAP(ss);
+        return -(jint)ss;
+    }
+}
+
+TCN_IMPLEMENT_CALL(jint, Socket, recvfrom)(TCN_STDARGS, jlong from,
+                                          jlong sock, jint flags,
+                                          jbyteArray buf, jint offset, jint toread)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    apr_sockaddr_t *f = J2P(from, apr_sockaddr_t *);
+    apr_size_t nbytes = (apr_size_t)toread;
+    jbyte *bytes = (*e)->GetByteArrayElements(e, buf, NULL);
+    apr_status_t ss;
+
+    UNREFERENCED(o);
+    if (!sock) {
+        tcn_ThrowAPRException(e, APR_ENOTSOCK);
+        return -(jint)APR_ENOTSOCK;
+    }
+    TCN_ASSERT(s->sock != NULL);
+    TCN_ASSERT(buf != NULL);
+    ss = apr_socket_recvfrom(f, s->sock, (apr_int32_t)flags, (char*)(bytes + offset), &nbytes);
+
+    (*e)->ReleaseByteArrayElements(e, buf, bytes,
+                                   nbytes ? 0 : JNI_ABORT);
+    if (ss == APR_SUCCESS)
+        return (jint)nbytes;
+    else {
+        TCN_ERROR_WRAP(ss);
+        return -(jint)ss;
+    }
+}
+
+TCN_IMPLEMENT_CALL(jint, Socket, optSet)(TCN_STDARGS, jlong sock,
+                                         jint opt, jint on)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+
+    UNREFERENCED(o);
+    if (!s->sock) {
+        tcn_ThrowAPRException(e, APR_ENOTSOCK);
+        return APR_ENOTSOCK;
+    }
+    else
+        return (jint)(*s->net->opt_set)(s->opaque, (apr_int32_t)opt, (apr_int32_t)on);
+}
+
+TCN_IMPLEMENT_CALL(jint, Socket, optGet)(TCN_STDARGS, jlong sock,
+                                         jint opt)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    apr_int32_t on = 0;
+
+    UNREFERENCED(o);
+    if (s->sock)
+        tcn_ThrowAPRException(e, APR_ENOTSOCK);
+    else {
+        TCN_THROW_IF_ERR((*s->net->opt_get)(s->opaque, (apr_int32_t)opt,
+                                            &on), on);
+    }
+cleanup:
+    return (jint)on;
+}
+
+TCN_IMPLEMENT_CALL(jint, Socket, timeoutSet)(TCN_STDARGS, jlong sock,
+                                             jlong timeout)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+
+    UNREFERENCED(o);
+    TCN_ASSERT(s->opaque != NULL);
+    if (!sock) {
+        tcn_ThrowAPRException(e, APR_ENOTSOCK);
+        return APR_ENOTSOCK;
+    }
+    return (jint)(*s->net->timeout_set)(s->opaque, J2T(timeout));
+}
+
+TCN_IMPLEMENT_CALL(jlong, Socket, timeoutGet)(TCN_STDARGS, jlong sock)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    apr_interval_time_t timeout;
+
+    UNREFERENCED(o);
+    if (!sock) {
+        tcn_ThrowAPRException(e, APR_ENOTSOCK);
+        return 0;
+    }
+    TCN_ASSERT(s->opaque != NULL);
+
+    TCN_THROW_IF_ERR((*s->net->timeout_get)(s->opaque, &timeout), timeout);
+cleanup:
+    return (jlong)timeout;
+}
+
+TCN_IMPLEMENT_CALL(jboolean, Socket, atmark)(TCN_STDARGS, jlong sock)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    apr_int32_t mark;
+
+    UNREFERENCED_STDARGS;
+    TCN_ASSERT(sock != 0);
+    TCN_ASSERT(s->sock != NULL);
+
+    if (apr_socket_atmark(s->sock, &mark) != APR_SUCCESS)
+        return JNI_FALSE;
+    return mark ? JNI_TRUE : JNI_FALSE;
+}
+
+#if APR_HAS_SENDFILE
+
+TCN_IMPLEMENT_CALL(jlong, Socket, sendfile)(TCN_STDARGS, jlong sock,
+                                            jlong file,
+                                            jobjectArray headers,
+                                            jobjectArray trailers,
+                                            jlong offset, jlong len,
+                                            jint flags)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    apr_file_t *f = J2P(file, apr_file_t *);
+    jsize nh = 0;
+    jsize nt = 0;
+    jsize i;
+    struct iovec hvec[APR_MAX_IOVEC_SIZE];
+    struct iovec tvec[APR_MAX_IOVEC_SIZE];
+    jobject hba[APR_MAX_IOVEC_SIZE];
+    jobject tba[APR_MAX_IOVEC_SIZE];
+    apr_off_t off = (apr_off_t)offset;
+    apr_size_t written = (apr_size_t)len;
+    apr_hdtr_t hdrs;
+    apr_status_t ss;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(sock != 0);
+    TCN_ASSERT(file != 0);
+
+    if (s->net->type != TCN_SOCKET_APR)
+        return (jint)(-APR_ENOTIMPL);
+    if (headers)
+        nh = (*e)->GetArrayLength(e, headers);
+    if (trailers)
+        nt = (*e)->GetArrayLength(e, trailers);
+    /* Check for overflow */
+    if (nh >= APR_MAX_IOVEC_SIZE || nt >= APR_MAX_IOVEC_SIZE)
+        return (jint)(-APR_ENOMEM);
+
+    for (i = 0; i < nh; i++) {
+        hba[i] = (*e)->GetObjectArrayElement(e, headers, i);
+        hvec[i].iov_len  = (*e)->GetArrayLength(e, hba[i]);
+        hvec[i].iov_base = (*e)->GetByteArrayElements(e, hba[i], NULL);
+    }
+    for (i = 0; i < nt; i++) {
+        tba[i] = (*e)->GetObjectArrayElement(e, trailers, i);
+        tvec[i].iov_len  = (*e)->GetArrayLength(e, tba[i]);
+        tvec[i].iov_base = (*e)->GetByteArrayElements(e, tba[i], NULL);
+    }
+    hdrs.headers = &hvec[0];
+    hdrs.numheaders = nh;
+    hdrs.trailers = &tvec[0];
+    hdrs.numtrailers = nt;
+
+
+    ss = apr_socket_sendfile(s->sock, f, &hdrs, &off, &written, (apr_int32_t)flags);
+
+#ifdef TCN_DO_STATISTICS
+    sf_max_send = TCN_MAX(sf_max_send, written);
+    sf_min_send = TCN_MIN(sf_min_send, written);
+    sf_tot_send += written;
+    sf_num_send++;
+#endif
+
+    for (i = 0; i < nh; i++) {
+        (*e)->ReleaseByteArrayElements(e, hba[i], hvec[i].iov_base, JNI_ABORT);
+    }
+
+    for (i = 0; i < nt; i++) {
+        (*e)->ReleaseByteArrayElements(e, tba[i], tvec[i].iov_base, JNI_ABORT);
+    }
+    /* Return Number of bytes actually sent,
+     * including headers, file, and trailers
+     */
+    if (ss == APR_SUCCESS)
+        return (jlong)written;
+    else {
+        TCN_ERROR_WRAP(ss);
+        return -(jlong)ss;
+    }
+}
+
+TCN_IMPLEMENT_CALL(jlong, Socket, sendfilen)(TCN_STDARGS, jlong sock,
+                                             jlong file,
+                                             jlong offset, jlong len,
+                                             jint flags)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    apr_file_t *f = J2P(file, apr_file_t *);
+    apr_off_t off = (apr_off_t)offset;
+    apr_size_t written = (apr_size_t)len;
+    apr_hdtr_t hdrs;
+    apr_status_t ss;
+
+    UNREFERENCED_STDARGS;
+    TCN_ASSERT(sock != 0);
+    TCN_ASSERT(file != 0);
+
+    if (s->net->type != TCN_SOCKET_APR)
+        return (jint)(-APR_ENOTIMPL);
+
+    hdrs.headers = NULL;
+    hdrs.numheaders = 0;
+    hdrs.trailers = NULL;
+    hdrs.numtrailers = 0;
+
+
+    ss = apr_socket_sendfile(s->sock, f, &hdrs, &off, &written, (apr_int32_t)flags);
+
+#ifdef TCN_DO_STATISTICS
+    sf_max_send = TCN_MAX(sf_max_send, written);
+    sf_min_send = TCN_MIN(sf_min_send, written);
+    sf_tot_send += written;
+    sf_num_send++;
+#endif
+
+    /* Return Number of bytes actually sent,
+     * including headers, file, and trailers
+     */
+    if (ss == APR_SUCCESS)
+        return (jlong)written;
+    else {
+        TCN_ERROR_WRAP(ss);
+        return -(jlong)ss;
+    }
+}
+
+#else /* APR_HAS_SENDIFLE */
+
+TCN_IMPLEMENT_CALL(jlong, Socket, sendfile)(TCN_STDARGS, jlong sock,
+                                            jlong file,
+                                            jobjectArray headers,
+                                            jobjectArray trailers,
+                                            jlong offset, jlong len,
+                                            jint flags)
+{
+
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(sock);
+    UNREFERENCED(file);
+    UNREFERENCED(headers);
+    UNREFERENCED(trailers);
+    UNREFERENCED(offset);
+    UNREFERENCED(len);
+    UNREFERENCED(flags);
+    return -(jlong)APR_ENOTIMPL;
+}
+
+TCN_IMPLEMENT_CALL(jlong, Socket, sendfilen)(TCN_STDARGS, jlong sock,
+                                             jlong file,
+                                             jlong offset, jlong len,
+                                             jint flags)
+{
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(sock);
+    UNREFERENCED(file);
+    UNREFERENCED(offset);
+    UNREFERENCED(len);
+    UNREFERENCED(flags);
+    return -(jlong)APR_ENOTIMPL;
+}
+
+#endif  /* APR_HAS_SENDIFLE */
+
+
+TCN_IMPLEMENT_CALL(jint, Socket, acceptfilter)(TCN_STDARGS,
+                                               jlong sock,
+                                               jstring name,
+                                               jstring args)
+{
+#if APR_HAS_SO_ACCEPTFILTER
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    TCN_ALLOC_CSTRING(name);
+    TCN_ALLOC_CSTRING(args);
+    apr_status_t rv;
+
+
+    UNREFERENCED(o);
+    rv = apr_socket_accept_filter(s->sock, J2S(name),
+                                  J2S(args) ? J2S(args) : "");
+    TCN_FREE_CSTRING(name);
+    TCN_FREE_CSTRING(args);
+    return (jint)rv;
+#else
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(sock);
+    UNREFERENCED(name);
+    UNREFERENCED(args);
+    return (jint)APR_ENOTIMPL;
+#endif
+}
+
+TCN_IMPLEMENT_CALL(jint, Socket, dataSet)(TCN_STDARGS, jlong sock,
+                                          jstring key, jobject data)
+{
+    tcn_socket_t *s = J2P(sock, tcn_socket_t *);
+    apr_status_t rv = APR_SUCCESS;
+    TCN_ALLOC_CSTRING(key);
+
+    UNREFERENCED(o);
+    TCN_ASSERT(sock != 0);
+
+    rv = apr_socket_data_set(s->sock, data, J2S(key), NULL);
+    TCN_FREE_CSTRING(key);
+    return rv;
+}
+
+TCN_IMPLEMENT_CALL(jobject, Socket, dataGet)(TCN_STDARGS, jlong socket,
+                                             jstring key)
+{
+    tcn_socket_t *s = J2P(socket, tcn_socket_t *);
+    TCN_ALLOC_CSTRING(key);
+    void *rv = NULL;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(socket != 0);
+
+    if (apr_socket_data_get(&rv, J2S(key), s->sock) != APR_SUCCESS) {
+        rv = NULL;
+    }
+    TCN_FREE_CSTRING(key);
+    return rv;
+}
diff --git a/connectors/jni/native/src/os.c b/connectors/jni/native/src/os.c
new file mode 100644
index 0000000..dc57fac
--- /dev/null
+++ b/connectors/jni/native/src/os.c
@@ -0,0 +1,40 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+#include "tcn.h"
+
+TCN_IMPLEMENT_CALL(jstring, OS, defaultEncoding)(TCN_STDARGS, jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+
+    UNREFERENCED(o);
+    return AJP_TO_JSTRING(apr_os_default_encoding(p));
+}
+
+TCN_IMPLEMENT_CALL(jstring, OS, localeEncoding)(TCN_STDARGS, jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+
+    UNREFERENCED(o);
+    return AJP_TO_JSTRING(apr_os_locale_encoding(p));
+}
+
diff --git a/connectors/jni/native/src/poll.c b/connectors/jni/native/src/poll.c
new file mode 100644
index 0000000..c7d456d
--- /dev/null
+++ b/connectors/jni/native/src/poll.c
@@ -0,0 +1,417 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+#include "tcn.h"
+#include "apr_poll.h"
+
+
+#ifdef TCN_DO_STATISTICS
+static int sp_created       = 0;
+static int sp_destroyed     = 0;
+static int sp_cleared       = 0;
+#endif
+
+/* Internal poll structure for queryset
+ */
+
+typedef struct tcn_pollset {
+    apr_pool_t    *pool;
+    apr_int32_t   nelts;
+    apr_int32_t   nalloc;
+    apr_pollset_t *pollset;
+    jlong         *set;
+    apr_pollfd_t  *socket_set;
+    apr_interval_time_t *socket_ttl;
+    apr_interval_time_t max_ttl;
+#ifdef TCN_DO_STATISTICS
+    int sp_added;
+    int sp_max_count;
+    int sp_poll;
+    int sp_polled;
+    int sp_max_polled;
+    int sp_remove;
+    int sp_removed;
+    int sp_maintained;
+    int sp_max_maintained;
+    int sp_err_poll;
+    int sp_poll_timeout;
+    int sp_overflow;
+    int sp_equals;
+    int sp_eintr;
+#endif
+} tcn_pollset_t;
+
+#ifdef TCN_DO_STATISTICS
+static void sp_poll_statistics(tcn_pollset_t *p)
+{
+    fprintf(stderr, "Pollset Statistics ......\n");
+    fprintf(stderr, "Number of added sockets : %d\n", p->sp_added);
+    fprintf(stderr, "Max. number of sockets  : %d\n", p->sp_max_count);
+    fprintf(stderr, "Poll calls              : %d\n", p->sp_poll);
+    fprintf(stderr, "Poll timeouts           : %d\n", p->sp_poll_timeout);
+    fprintf(stderr, "Poll errors             : %d\n", p->sp_err_poll);
+    fprintf(stderr, "Poll overflows          : %d\n", p->sp_overflow);
+    fprintf(stderr, "Polled sockets          : %d\n", p->sp_polled);
+    fprintf(stderr, "Max. Polled sockets     : %d\n", p->sp_max_polled);
+    fprintf(stderr, "Poll remove             : %d\n", p->sp_remove);
+    fprintf(stderr, "Total removed           : %d\n", p->sp_removed);
+    fprintf(stderr, "Maintained              : %d\n", p->sp_maintained);
+    fprintf(stderr, "Max. maintained         : %d\n", p->sp_max_maintained);
+    fprintf(stderr, "Number of duplicates    : %d\n", p->sp_equals);
+    fprintf(stderr, "Number of interrupts    : %d\n", p->sp_eintr);
+
+}
+
+static apr_status_t sp_poll_cleanup(void *data)
+{
+    sp_cleared++;
+    sp_poll_statistics(data);
+    return APR_SUCCESS;
+}
+
+void sp_poll_dump_statistics()
+{
+    fprintf(stderr, "Poll Statistics .........\n");
+    fprintf(stderr, "Polls created           : %d\n", sp_created);
+    fprintf(stderr, "Polls destroyed         : %d\n", sp_destroyed);
+    fprintf(stderr, "Polls cleared           : %d\n", sp_cleared);
+}
+#endif
+
+TCN_IMPLEMENT_CALL(jlong, Poll, create)(TCN_STDARGS, jint size,
+                                        jlong pool, jint flags,
+                                        jlong ttl)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    apr_pollset_t *pollset = NULL;
+    tcn_pollset_t *tps = NULL;
+    apr_uint32_t f = (apr_uint32_t)flags;
+    UNREFERENCED(o);
+    TCN_ASSERT(pool != 0);
+
+    if (f & APR_POLLSET_THREADSAFE) {
+        apr_status_t rv = apr_pollset_create(&pollset, (apr_uint32_t)size, p, f);
+        if (rv == APR_ENOTIMPL)
+            f &= ~APR_POLLSET_THREADSAFE;
+        else if (rv != APR_SUCCESS) {
+            tcn_ThrowAPRException(e, rv);
+            goto cleanup;
+        }
+    }
+    if (pollset == NULL) {
+        TCN_THROW_IF_ERR(apr_pollset_create(&pollset,
+                         (apr_uint32_t)size, p, f), pollset);
+    }
+    tps = apr_pcalloc(p, sizeof(tcn_pollset_t));
+    TCN_CHECK_ALLOCATED(tps);
+    tps->pollset = pollset;
+    tps->set        = apr_palloc(p, size * sizeof(jlong) * 2);
+    TCN_CHECK_ALLOCATED(tps->set);
+    tps->socket_set = apr_palloc(p, size * sizeof(apr_pollfd_t));
+    TCN_CHECK_ALLOCATED(tps->socket_set);
+    tps->socket_ttl = apr_palloc(p, size * sizeof(apr_interval_time_t));
+    TCN_CHECK_ALLOCATED(tps->socket_ttl);
+    tps->nelts  = 0;
+    tps->nalloc = size;
+    tps->pool   = p;
+    tps->max_ttl = J2T(ttl);
+#ifdef TCN_DO_STATISTICS
+    sp_created++;
+    apr_pool_cleanup_register(p, (const void *)tps,
+                              sp_poll_cleanup,
+                              apr_pool_cleanup_null);
+#endif
+cleanup:
+    return P2J(tps);
+}
+
+TCN_IMPLEMENT_CALL(jint, Poll, destroy)(TCN_STDARGS, jlong pollset)
+{
+    tcn_pollset_t *p = J2P(pollset,  tcn_pollset_t *);
+
+    UNREFERENCED_STDARGS;
+    TCN_ASSERT(pollset != 0);
+#ifdef TCN_DO_STATISTICS
+    sp_destroyed++;
+    apr_pool_cleanup_kill(p->pool, p, sp_poll_cleanup);
+    sp_poll_statistics(p);
+#endif
+    return (jint)apr_pollset_destroy(p->pollset);
+}
+
+TCN_IMPLEMENT_CALL(jint, Poll, add)(TCN_STDARGS, jlong pollset,
+                                    jlong socket, jint reqevents)
+{
+    tcn_pollset_t *p = J2P(pollset,  tcn_pollset_t *);
+    tcn_socket_t *s  = J2P(socket, tcn_socket_t *);
+    apr_pollfd_t fd;
+
+    UNREFERENCED_STDARGS;
+    TCN_ASSERT(socket != 0);
+
+    if (p->nelts == p->nalloc) {
+#ifdef TCN_DO_STATISTICS
+        p->sp_overflow++;
+#endif
+        return APR_ENOMEM;
+    }
+    memset(&fd, 0, sizeof(apr_pollfd_t));
+    fd.desc_type = APR_POLL_SOCKET;
+    fd.reqevents = (apr_int16_t)reqevents;
+    fd.desc.s    = s->sock;
+    fd.client_data = s;
+    if (p->max_ttl > 0)
+        p->socket_ttl[p->nelts] = apr_time_now();
+    else
+        p->socket_ttl[p->nelts] = 0;
+
+    p->socket_set[p->nelts] = fd;
+    p->nelts++;
+#ifdef TCN_DO_STATISTICS
+    p->sp_added++;
+    p->sp_max_count = TCN_MAX(p->sp_max_count, p->sp_added);
+#endif
+    return (jint)apr_pollset_add(p->pollset, &fd);
+}
+
+static apr_status_t do_remove(tcn_pollset_t *p, const apr_pollfd_t *fd)
+{
+    apr_int32_t i;
+
+    for (i = 0; i < p->nelts; i++) {
+        if (fd->desc.s == p->socket_set[i].desc.s) {
+            /* Found an instance of the fd: remove this and any other copies */
+            apr_int32_t dst = i;
+            apr_int32_t old_nelts = p->nelts;
+            p->nelts--;
+#ifdef TCN_DO_STATISTICS
+            p->sp_removed++;
+#endif
+            for (i++; i < old_nelts; i++) {
+                if (fd->desc.s == p->socket_set[i].desc.s) {
+#ifdef TCN_DO_STATISTICS
+                    p->sp_equals++;
+#endif
+                    p->nelts--;
+                }
+                else {
+                    p->socket_set[dst] = p->socket_set[i];
+                    dst++;
+                }
+            }
+            break;
+        }
+    }
+    return apr_pollset_remove(p->pollset, fd);
+}
+
+static void remove_all(tcn_pollset_t *p)
+{
+    apr_int32_t i;
+    for (i = 0; i < p->nelts; i++) {
+        apr_pollset_remove(p->pollset, &(p->socket_set[i]));
+#ifdef TCN_DO_STATISTICS
+        p->sp_removed++;
+#endif
+    }
+    p->nelts = 0;
+}
+
+TCN_IMPLEMENT_CALL(jint, Poll, remove)(TCN_STDARGS, jlong pollset,
+                                       jlong socket)
+{
+    tcn_pollset_t *p = J2P(pollset,  tcn_pollset_t *);
+    tcn_socket_t *s  = J2P(socket, tcn_socket_t *);
+    apr_pollfd_t fd;
+
+    UNREFERENCED_STDARGS;
+    TCN_ASSERT(socket != 0);
+
+    memset(&fd, 0, sizeof(apr_pollfd_t));
+    fd.desc_type = APR_POLL_SOCKET;
+    fd.desc.s    = s->sock;
+    fd.reqevents = APR_POLLIN | APR_POLLOUT;
+#ifdef TCN_DO_STATISTICS
+    p->sp_remove++;
+#endif
+
+    return (jint)do_remove(p, &fd);
+}
+
+
+TCN_IMPLEMENT_CALL(jint, Poll, poll)(TCN_STDARGS, jlong pollset,
+                                     jlong timeout, jlongArray set,
+                                     jboolean remove)
+{
+    const apr_pollfd_t *fd = NULL;
+    tcn_pollset_t *p = J2P(pollset,  tcn_pollset_t *);
+    apr_int32_t  i, num = 0;
+    apr_status_t rv = APR_SUCCESS;
+    apr_interval_time_t ptime = J2T(timeout);
+    UNREFERENCED(o);
+    TCN_ASSERT(pollset != 0);
+
+#ifdef TCN_DO_STATISTICS
+     p->sp_poll++;
+#endif
+
+    if (ptime > 0 && p->max_ttl >= 0) {
+        apr_time_t now = apr_time_now();
+
+        /* Find the minimum timeout */
+        for (i = 0; i < p->nelts; i++) {
+            apr_interval_time_t t = now - p->socket_ttl[i];
+            if (t >= p->max_ttl) {
+                ptime = 0;
+                break;
+            }
+            else {
+                ptime = TCN_MIN(p->max_ttl - t, ptime);
+            }
+        }
+    }
+    else if (ptime < 0)
+        ptime = 0;
+    for (;;) {
+        rv = apr_pollset_poll(p->pollset, ptime, &num, &fd);
+        if (rv != APR_SUCCESS) {
+            if (APR_STATUS_IS_EINTR(rv)) {
+#ifdef TCN_DO_STATISTICS
+                p->sp_eintr++;
+#endif
+                continue;
+            }
+            TCN_ERROR_WRAP(rv);
+#ifdef TCN_DO_STATISTICS
+            if (rv == TCN_TIMEUP)
+                p->sp_poll_timeout++;
+            else
+                p->sp_err_poll++;
+#endif
+            num = (apr_int32_t)(-rv);
+        }
+        break;
+    }
+    if (num > 0) {
+#ifdef TCN_DO_STATISTICS
+         p->sp_polled += num;
+         p->sp_max_polled = TCN_MAX(p->sp_max_polled, num);
+#endif
+        for (i = 0; i < num; i++) {
+            p->set[i*2+0] = (jlong)(fd->rtnevents);
+            p->set[i*2+1] = P2J(fd->client_data);
+            if (remove)
+                do_remove(p, fd);
+            fd ++;
+        }
+        (*e)->SetLongArrayRegion(e, set, 0, num * 2, p->set);
+    }
+
+    return (jint)num;
+}
+
+TCN_IMPLEMENT_CALL(jint, Poll, maintain)(TCN_STDARGS, jlong pollset,
+                                         jlongArray set, jboolean remove)
+{
+    tcn_pollset_t *p = J2P(pollset,  tcn_pollset_t *);
+    apr_int32_t  i = 0, num = 0;
+    apr_time_t now = apr_time_now();
+    apr_pollfd_t fd;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(pollset != 0);
+
+    /* Check for timeout sockets */
+    if (p->max_ttl > 0) {
+        for (i = 0; i < p->nelts; i++) {
+            if ((now - p->socket_ttl[i]) >= p->max_ttl) {
+                fd = p->socket_set[i];
+                p->set[num++] = P2J(fd.client_data);
+            }
+        }
+        if (remove && num) {
+            memset(&fd, 0, sizeof(apr_pollfd_t));
+#ifdef TCN_DO_STATISTICS
+             p->sp_maintained += num;
+             p->sp_max_maintained = TCN_MAX(p->sp_max_maintained, num);
+#endif
+            for (i = 0; i < num; i++) {
+                fd.desc_type = APR_POLL_SOCKET;
+                fd.reqevents = APR_POLLIN | APR_POLLOUT;
+                fd.desc.s = (J2P(p->set[i], tcn_socket_t *))->sock;
+                do_remove(p, &fd);
+            }
+        }
+    }
+    else if (p->max_ttl == 0) {
+        for (i = 0; i < p->nelts; i++) {
+            fd = p->socket_set[i];
+            p->set[num++] = P2J(fd.client_data);
+        }
+        if (remove) {
+            remove_all(p);
+#ifdef TCN_DO_STATISTICS
+            p->sp_maintained += num;
+            p->sp_max_maintained = TCN_MAX(p->sp_max_maintained, num);
+#endif
+        }
+    }
+    if (num)
+        (*e)->SetLongArrayRegion(e, set, 0, num, p->set);
+    return (jint)num;
+}
+
+TCN_IMPLEMENT_CALL(void, Poll, setTtl)(TCN_STDARGS, jlong pollset,
+                                       jlong ttl)
+{
+    tcn_pollset_t *p = J2P(pollset,  tcn_pollset_t *);
+    UNREFERENCED_STDARGS;
+    p->max_ttl = J2T(ttl);
+}
+
+TCN_IMPLEMENT_CALL(jlong, Poll, getTtl)(TCN_STDARGS, jlong pollset)
+{
+    tcn_pollset_t *p = J2P(pollset,  tcn_pollset_t *);
+    UNREFERENCED_STDARGS;
+    return (jlong)p->max_ttl;
+}
+
+TCN_IMPLEMENT_CALL(jint, Poll, pollset)(TCN_STDARGS, jlong pollset,
+                                        jlongArray set)
+{
+    tcn_pollset_t *p = J2P(pollset,  tcn_pollset_t *);
+    apr_int32_t  i = 0;
+    apr_pollfd_t fd;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(pollset != 0);
+
+    for (i = 0; i < p->nelts; i++) {
+        p->socket_set[i].rtnevents = APR_POLLHUP | APR_POLLIN;
+        fd = p->socket_set[i];
+        p->set[i*2+0] = (jlong)(fd.rtnevents);
+        p->set[i*2+1] = P2J(fd.client_data);
+    }
+    if (p->nelts)
+        (*e)->SetLongArrayRegion(e, set, 0, p->nelts * 2, p->set);
+    return (jint)p->nelts;
+}
diff --git a/connectors/jni/native/src/pool.c b/connectors/jni/native/src/pool.c
new file mode 100644
index 0000000..707337f
--- /dev/null
+++ b/connectors/jni/native/src/pool.c
@@ -0,0 +1,250 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+#include "tcn.h"
+
+extern apr_pool_t *tcn_global_pool;
+
+static apr_status_t generic_pool_cleanup(void *data)
+{
+    apr_status_t rv = APR_SUCCESS;
+    tcn_callback_t *cb = (tcn_callback_t *)data;
+
+    if (data) {
+        JNIEnv *env;
+        tcn_get_java_env(&env);
+        if (!TCN_IS_NULL(env, cb->obj)) {
+            rv = (*(env))->CallIntMethod(env, cb->obj, cb->mid[0],
+                                         NULL);
+            TCN_UNLOAD_CLASS(env, cb->obj);
+        }
+        free(cb);
+    }
+    return rv;
+}
+
+TCN_IMPLEMENT_CALL(jlong, Pool, create)(TCN_STDARGS, jlong parent)
+{
+    apr_pool_t *p = J2P(parent, apr_pool_t *);
+    apr_pool_t *n;
+
+    UNREFERENCED(o);
+    /* Make sure our global pool is accessor for all pools */
+    if (p == NULL)
+        p = tcn_global_pool;
+    TCN_THROW_IF_ERR(apr_pool_create(&n, p), n);
+cleanup:
+    return P2J(n);
+}
+
+TCN_IMPLEMENT_CALL(void, Pool, clear)(TCN_STDARGS, jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    UNREFERENCED_STDARGS;
+    TCN_ASSERT(pool != 0);
+    apr_pool_clear(p);
+}
+
+TCN_IMPLEMENT_CALL(void, Pool, destroy)(TCN_STDARGS, jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    UNREFERENCED_STDARGS;
+    TCN_ASSERT(pool != 0);
+    apr_pool_destroy(p);
+}
+
+TCN_IMPLEMENT_CALL(jlong, Pool, parentGet)(TCN_STDARGS, jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    UNREFERENCED_STDARGS;
+    TCN_ASSERT(pool != 0);
+    return P2J(apr_pool_parent_get(p));
+}
+
+TCN_IMPLEMENT_CALL(jboolean, Pool, isAncestor)(TCN_STDARGS, jlong a, jlong b)
+{
+    apr_pool_t *pa = J2P(a, apr_pool_t *);
+    apr_pool_t *pb = J2P(b, apr_pool_t *);
+    UNREFERENCED_STDARGS;
+    return apr_pool_is_ancestor(pa, pb) ? JNI_TRUE : JNI_FALSE;
+}
+
+TCN_IMPLEMENT_CALL(jlong, Pool, palloc)(TCN_STDARGS, jlong pool, jint size)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    UNREFERENCED_STDARGS;
+    return P2J(apr_palloc(p, (apr_size_t)size));
+}
+
+TCN_IMPLEMENT_CALL(jlong, Pool, pcalloc)(TCN_STDARGS, jlong pool, jint size)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    UNREFERENCED_STDARGS;
+    return P2J(apr_pcalloc(p, (apr_size_t)size));
+}
+
+TCN_IMPLEMENT_CALL(jlong, Pool, cleanupRegister)(TCN_STDARGS, jlong pool,
+                                                 jobject obj)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    tcn_callback_t *cb = (tcn_callback_t *)malloc(sizeof(tcn_callback_t));
+    jclass cls;
+
+    UNREFERENCED(o);
+
+    if (cb == NULL) {
+       TCN_THROW_OS_ERROR(e);
+       return 0;
+    }
+    cls = (*e)->GetObjectClass(e, obj);
+    cb->obj    = (*e)->NewGlobalRef(e, obj);
+    cb->mid[0] = (*e)->GetMethodID(e, cls, "callback", "()I");
+
+    apr_pool_cleanup_register(p, (const void *)cb,
+                              generic_pool_cleanup,
+                              apr_pool_cleanup_null);
+
+    return P2J(cb);
+}
+
+TCN_IMPLEMENT_CALL(void, Pool, cleanupKill)(TCN_STDARGS, jlong pool,
+                                            jlong data)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    tcn_callback_t *cb = J2P(data, tcn_callback_t *);
+
+    UNREFERENCED(o);
+    TCN_ASSERT(pool != 0);
+    apr_pool_cleanup_kill(p, cb, generic_pool_cleanup);
+    (*e)->DeleteGlobalRef(e, cb->obj);
+    free(cb);
+}
+
+TCN_IMPLEMENT_CALL(jobject, Pool, alloc)(TCN_STDARGS, jlong pool,
+                                         jint size)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    apr_size_t sz = (apr_size_t)size;
+    void *mem;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(pool != 0);
+
+    if ((mem = apr_palloc(p, sz)) != NULL)
+        return (*e)->NewDirectByteBuffer(e, mem, (jlong)sz);
+    else
+        return NULL;
+}
+
+TCN_IMPLEMENT_CALL(jobject, Pool, calloc)(TCN_STDARGS, jlong pool,
+                                          jint size)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    apr_size_t sz = (apr_size_t)size;
+    void *mem;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(pool != 0);
+
+    if ((mem = apr_pcalloc(p, sz)) != NULL)
+        return (*e)->NewDirectByteBuffer(e, mem, (jlong)sz);
+    else
+        return NULL;
+}
+
+static apr_status_t generic_pool_data_cleanup(void *data)
+{
+    apr_status_t rv = APR_SUCCESS;
+    tcn_callback_t *cb = (tcn_callback_t *)data;
+
+    if (data) {
+        JNIEnv *env;
+        tcn_get_java_env(&env);
+        
+        if (!TCN_IS_NULL(env, cb->obj)) {
+            TCN_UNLOAD_CLASS(env, cb->obj);
+        }
+        free(cb);
+    }
+    return rv;
+}
+
+TCN_IMPLEMENT_CALL(jint, Pool, dataSet)(TCN_STDARGS, jlong pool,
+                                        jstring key, jobject data)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    apr_status_t rv = APR_SUCCESS;
+    void *old = NULL;
+    TCN_ALLOC_CSTRING(key);
+
+    UNREFERENCED(o);
+    TCN_ASSERT(pool != 0);
+
+    if (apr_pool_userdata_get(&old, J2S(key), p) == APR_SUCCESS) {
+        if (old)
+            apr_pool_cleanup_run(p, old, generic_pool_data_cleanup);
+    }
+    if (data) {
+        JNIEnv *e;        
+        tcn_callback_t *cb = (tcn_callback_t *)malloc(sizeof(tcn_callback_t));
+        tcn_get_java_env(&e);
+        cb->obj = (*e)->NewGlobalRef(e, data);
+        if ((rv = apr_pool_userdata_set(cb, J2S(key), generic_pool_data_cleanup,
+                                        p)) != APR_SUCCESS) {
+            (*e)->DeleteGlobalRef(e, cb->obj);
+            free(cb);
+        }
+    }
+    else {
+        /* Clear the exiting user data */
+        rv = apr_pool_userdata_set(NULL, J2S(key), NULL, p);
+    }
+    TCN_FREE_CSTRING(key);
+    return rv;
+}
+
+TCN_IMPLEMENT_CALL(jobject, Pool, dataGet)(TCN_STDARGS, jlong pool,
+                                           jstring key)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    void *old = NULL;
+    TCN_ALLOC_CSTRING(key);
+    jobject rv = NULL;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(pool != 0);
+
+    if (apr_pool_userdata_get(&old, J2S(key), p) == APR_SUCCESS) {
+        if (old) {
+            tcn_callback_t *cb = (tcn_callback_t *)old;
+            rv = cb->obj;
+        }
+    }
+    TCN_FREE_CSTRING(key);
+    return rv;
+}
+
+TCN_IMPLEMENT_CALL(void, Pool, cleanupForExec)(TCN_STDARGS)
+{
+    UNREFERENCED_STDARGS;
+    apr_pool_cleanup_for_exec();
+}
diff --git a/connectors/jni/native/src/proc.c b/connectors/jni/native/src/proc.c
new file mode 100644
index 0000000..7f93768
--- /dev/null
+++ b/connectors/jni/native/src/proc.c
@@ -0,0 +1,453 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Mladen Turk
+ * @version $Revision$, $Date$
+ */
+ 
+#include "tcn.h"
+#include "apr_thread_proc.h"
+#include "apr_version.h"
+
+#define ERRFN_USERDATA_KEY    "TCNATIVECHILDERRFN"
+
+static void generic_child_errfn(apr_pool_t *pool, apr_status_t err,
+                                const char *description)
+{
+    void *data;
+    tcn_callback_t *cb;
+
+    apr_pool_userdata_get(&data, ERRFN_USERDATA_KEY, pool);
+    cb = (tcn_callback_t *)data;
+    if (cb) {
+        JNIEnv *env;
+        tcn_get_java_env(&env);
+        if (!TCN_IS_NULL(env, cb->obj)) {
+            (*(env))->CallVoidMethod(env, cb->obj, cb->mid[0],
+                                P2J(pool), (jint)err,
+                                (*(env))->NewStringUTF(env, description),
+                                NULL);
+        }
+    }
+}
+
+static apr_status_t child_errfn_pool_cleanup(void *data)
+{
+    tcn_callback_t *cb = (tcn_callback_t *)data;
+
+    if (data) {
+        JNIEnv *env;
+        tcn_get_java_env(&env);
+        if (!TCN_IS_NULL(env, cb->obj)) {
+            TCN_UNLOAD_CLASS(env, cb->obj);
+        }
+        free(cb);
+    }
+    return APR_SUCCESS;
+}
+
+TCN_IMPLEMENT_CALL(jlong, Procattr, create)(TCN_STDARGS,
+                                            jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    apr_procattr_t *attr;
+
+
+    UNREFERENCED(o);
+    TCN_THROW_IF_ERR(apr_procattr_create(&attr, p), attr);
+
+cleanup:
+    return P2J(attr);
+}
+
+TCN_IMPLEMENT_CALL(jint, Procattr, ioSet)(TCN_STDARGS,
+                                          jlong attr, jint in,
+                                          jint out, jint err)
+{
+    apr_procattr_t *a = J2P(attr, apr_procattr_t *);
+
+    UNREFERENCED_STDARGS;
+    return (jint)apr_procattr_io_set(a, (apr_int32_t)in,
+                     (apr_int32_t)out, (apr_int32_t)err);
+}
+
+TCN_IMPLEMENT_CALL(jint, Procattr, childInSet)(TCN_STDARGS,
+                                          jlong attr, jlong in,
+                                          jlong parent)
+{
+    apr_procattr_t *a = J2P(attr, apr_procattr_t *);
+    apr_file_t *f = J2P(in, apr_file_t *);
+    apr_file_t *p = J2P(parent, apr_file_t *);
+
+    UNREFERENCED_STDARGS;
+    return (jint)apr_procattr_child_in_set(a, f, p);
+}
+
+TCN_IMPLEMENT_CALL(jint, Procattr, childOutSet)(TCN_STDARGS,
+                                          jlong attr, jlong out,
+                                          jlong parent)
+{
+    apr_procattr_t *a = J2P(attr, apr_procattr_t *);
+    apr_file_t *f = J2P(out, apr_file_t *);
+    apr_file_t *p = J2P(parent, apr_file_t *);
+
+    UNREFERENCED_STDARGS;
+    return (jint)apr_procattr_child_out_set(a, f, p);
+}
+
+TCN_IMPLEMENT_CALL(jint, Procattr, childErrSet)(TCN_STDARGS,
+                                          jlong attr, jlong err,
+                                          jlong parent)
+{
+    apr_procattr_t *a = J2P(attr, apr_procattr_t *);
+    apr_file_t *f = J2P(err, apr_file_t *);
+    apr_file_t *p = J2P(parent, apr_file_t *);
+
+    UNREFERENCED_STDARGS;
+    return (jint)apr_procattr_child_in_set(a, f, p);
+}
+
+TCN_IMPLEMENT_CALL(jint, Procattr, dirSet)(TCN_STDARGS,
+                                           jlong attr,
+                                           jstring dir)
+{
+    apr_status_t rv;
+    apr_procattr_t *a = J2P(attr, apr_procattr_t *);
+    TCN_ALLOC_CSTRING(dir);
+
+    UNREFERENCED(o);
+
+    rv = apr_procattr_dir_set(a, J2S(dir));
+    TCN_FREE_CSTRING(dir);
+    return (jint) rv;
+}
+
+TCN_IMPLEMENT_CALL(jint, Procattr, cmdtypeSet)(TCN_STDARGS,
+                                          jlong attr, jint cmd)
+{
+    apr_procattr_t *a = J2P(attr, apr_procattr_t *);
+
+    UNREFERENCED_STDARGS;
+    return (jint)apr_procattr_cmdtype_set(a, (apr_int32_t)cmd);
+}
+
+TCN_IMPLEMENT_CALL(jint, Procattr, detachSet)(TCN_STDARGS,
+                                          jlong attr, jint detach)
+{
+    apr_procattr_t *a = J2P(attr, apr_procattr_t *);
+
+    UNREFERENCED_STDARGS;
+    return (jint)apr_procattr_detach_set(a, (apr_int32_t)detach);
+}
+
+TCN_IMPLEMENT_CALL(jint, Procattr, errorCheckSet)(TCN_STDARGS,
+                                          jlong attr, jint chk)
+{
+    apr_procattr_t *a = J2P(attr, apr_procattr_t *);
+
+    UNREFERENCED_STDARGS;
+    return (jint)apr_procattr_error_check_set(a, (apr_int32_t)chk);
+}
+
+TCN_IMPLEMENT_CALL(jint, Procattr, addrspaceSet)(TCN_STDARGS,
+                                          jlong attr, jint addr)
+{
+    apr_procattr_t *a = J2P(attr, apr_procattr_t *);
+
+    UNREFERENCED_STDARGS;
+    return (jint)apr_procattr_addrspace_set(a, (apr_int32_t)addr);
+}
+
+TCN_IMPLEMENT_CALL(jlong, Proc, alloc)(TCN_STDARGS,
+                                       jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    apr_proc_t *proc;
+
+    UNREFERENCED_STDARGS;
+    proc = (apr_proc_t *)apr_pcalloc(p, sizeof(apr_proc_t));
+
+    return P2J(proc);
+}
+
+#define MAX_ARGS_SIZE 1024
+#define MAX_ENV_SIZE  1024
+
+TCN_IMPLEMENT_CALL(jint, Proc, create)(TCN_STDARGS, jlong proc,
+                                       jstring progname,
+                                       jobjectArray args,
+                                       jobjectArray env,
+                                       jlong attr, jlong pool)
+{
+    apr_status_t rv;
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    apr_procattr_t *a = J2P(attr, apr_procattr_t *);
+    apr_proc_t *np = J2P(proc, apr_proc_t *);
+    TCN_ALLOC_CSTRING(progname);
+    char *s_args[MAX_ARGS_SIZE];
+    char *s_env[MAX_ENV_SIZE];
+    const char * const *pargs = NULL;
+    const char * const *penv  = NULL;
+    jsize as = 0;
+    jsize es = 0;
+    jsize i;
+
+    UNREFERENCED(o);
+    if (args)
+        as = (*e)->GetArrayLength(e, args);
+    if (env)
+        es = (*e)->GetArrayLength(e, args);
+    if (as > (MAX_ARGS_SIZE - 1) || es > (MAX_ENV_SIZE - 2)) {
+        TCN_FREE_CSTRING(progname);
+        return APR_EINVAL;
+    }
+    if (as) {
+        for (i = 0; i < as; i++) {
+            jstring str = (*e)->GetObjectArrayElement(e, args, i);
+            s_args[i] = tcn_get_string(e, str);
+            (*e)->DeleteLocalRef(e, str);
+        }
+        s_args[i] = NULL;
+        pargs = (const char * const *)&s_args[0];
+    }
+    if (es) {
+        for (i = 0; i < es; i++) {
+            jstring str = (*e)->GetObjectArrayElement(e, env, i);
+            s_env[i+1] = tcn_get_string(e, str);
+            (*e)->DeleteLocalRef(e, str);
+        }
+#ifdef WIN32
+        s_env[i++] = apr_psprintf(p, TCN_PARENT_IDE "=%d", getpid());
+#endif
+        s_env[i] = NULL;
+        penv = (const char * const *)&s_env[0];
+    }
+#ifdef WIN32
+    else {
+        char pps[32];
+        itoa(getpid(), pps, 10);
+        SetEnvironmentVariable(TCN_PARENT_IDE, pps);
+    }
+#endif
+    rv = apr_proc_create(np, J2S(progname), pargs,
+                         penv, a, p);
+#ifdef WIN32
+    if (!es)
+        SetEnvironmentVariable(TCN_PARENT_IDE, NULL);
+#endif
+
+    /* Free local resources */
+    TCN_FREE_CSTRING(progname);
+    for (i = 0; i < as; i++) {
+        if (s_args[i])
+            free(s_args[i]);
+    }
+    for (i = 0; i < es; i++) {
+        if (s_env[i])
+            free(s_env[i]);
+    }
+    return rv;
+}
+
+TCN_IMPLEMENT_CALL(jint, Proc, wait)(TCN_STDARGS, jlong proc,
+                                     jintArray rvals, jint waithow)
+{
+    apr_status_t rv;
+    apr_proc_t *p = J2P(proc, apr_proc_t *);
+    int exitcode;
+    apr_exit_why_e exitwhy;
+
+    UNREFERENCED(o);
+
+    rv = apr_proc_wait(p, &exitcode, &exitwhy, (apr_wait_how_e)waithow);
+    if (rv == APR_SUCCESS && rvals) {
+        jsize n = (*e)->GetArrayLength(e, rvals);
+        if (n > 1) {
+            jint *ints = (*e)->GetIntArrayElements(e, rvals, NULL);
+            ints[0] = exitcode;
+            ints[1] = exitwhy;
+            (*e)->ReleaseIntArrayElements(e, rvals, ints, 0);
+        }
+    }
+    return rv;
+}
+
+TCN_IMPLEMENT_CALL(jint, Proc, waitAllProcs)(TCN_STDARGS,
+                                             jlong proc, jintArray rvals,
+                                             jint waithow, jlong pool)
+{
+    apr_status_t rv;
+    apr_proc_t *p = J2P(proc, apr_proc_t *);
+    apr_pool_t *c = J2P(pool, apr_pool_t *);
+    int exitcode;
+    apr_exit_why_e exitwhy;
+
+    UNREFERENCED(o);
+
+    rv = apr_proc_wait_all_procs(p, &exitcode, &exitwhy,
+                                 (apr_wait_how_e)waithow, c);
+    if (rv == APR_SUCCESS && rvals) {
+        jsize n = (*e)->GetArrayLength(e, rvals);
+        if (n > 1) {
+            jint *ints = (*e)->GetIntArrayElements(e, rvals, NULL);
+            ints[0] = exitcode;
+            ints[1] = exitwhy;
+            (*e)->ReleaseIntArrayElements(e, rvals, ints, 0);
+        }
+    }
+    return rv;
+}
+
+TCN_IMPLEMENT_CALL(jint, Proc, detach)(TCN_STDARGS, jint daemonize)
+{
+
+    UNREFERENCED_STDARGS;
+#if defined(WIN32) || defined (NETWARE)
+    UNREFERENCED(daemonize);
+    return APR_ENOTIMPL;
+#else
+    return (jint)apr_proc_detach(daemonize);
+#endif
+}
+
+TCN_IMPLEMENT_CALL(jint, Proc, kill)(TCN_STDARGS, jlong proc, jint sig)
+{
+    apr_proc_t *p = J2P(proc, apr_proc_t *);
+
+    UNREFERENCED_STDARGS;
+    return (jint)apr_proc_kill(p, (int)sig);
+}
+
+TCN_IMPLEMENT_CALL(void, Pool, noteSubprocess)(TCN_STDARGS, jlong pool,
+                                               jlong proc, jint how)
+{
+    apr_proc_t *p = J2P(proc, apr_proc_t *);
+    apr_pool_t *a = J2P(pool, apr_pool_t *);
+
+    UNREFERENCED_STDARGS;
+    apr_pool_note_subprocess(a, p, (apr_kill_conditions_e)how);
+}
+
+TCN_IMPLEMENT_CALL(jint, Proc, fork)(TCN_STDARGS,
+                                     jlongArray proc,
+                                     jlong pool)
+{
+    apr_status_t rv = APR_EINVAL;
+
+#if APR_HAS_FORK
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    apr_proc_t *f = apr_pcalloc(p, sizeof(apr_proc_t));
+
+    UNREFERENCED(o);
+
+    rv = apr_proc_fork(f, p);
+    if (rv == APR_SUCCESS && proc) {
+        jsize n = (*e)->GetArrayLength(e, proc);
+        if (n > 0) {
+            jlong *rp = (*e)->GetLongArrayElements(e, proc, NULL);
+            rp[0] = P2J(f);
+            (*e)->ReleaseLongArrayElements(e, proc, rp, 0);
+        }
+    }
+#else
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(proc);
+    UNREFERENCED(pool);
+
+#endif
+    return rv;
+}
+
+TCN_IMPLEMENT_CALL(void, Procattr, errfnSet)(TCN_STDARGS, jlong attr,
+                                             jlong pool, jobject obj)
+{
+    apr_procattr_t *a = J2P(attr, apr_procattr_t *);
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    tcn_callback_t *cb = (tcn_callback_t *)malloc(sizeof(tcn_callback_t));
+    jclass cls;
+
+    UNREFERENCED(o);
+
+    if (cb == NULL) {
+       return;
+    }
+    cls = (*e)->GetObjectClass(e, obj);
+    cb->obj    = (*e)->NewGlobalRef(e, obj);
+    cb->mid[0] = (*e)->GetMethodID(e, cls, "callback", "(JILjava/lang/String;)V");
+
+    apr_pool_userdata_setn(cb, ERRFN_USERDATA_KEY, child_errfn_pool_cleanup, p);
+    apr_procattr_child_errfn_set(a, generic_child_errfn);
+
+}
+
+TCN_IMPLEMENT_CALL(jint, Procattr, userSet)(TCN_STDARGS,
+                                            jlong attr,
+                                            jstring username,
+                                            jstring password)
+{
+
+#if ((APR_MAJOR_VERSION >= 1) && (APR_MINOR_VERSION >= 1))
+    apr_status_t rv;
+    apr_procattr_t *a = J2P(attr, apr_procattr_t *);
+    TCN_ALLOC_CSTRING(username);
+#if APR_PROCATTR_USER_SET_REQUIRES_PASSWORD
+    TCN_ALLOC_CSTRING(password);
+#else
+    const char *cpassword = NULL;
+#endif
+    UNREFERENCED(o);
+
+    rv = apr_procattr_user_set(a, J2S(username), J2S(password));
+    TCN_FREE_CSTRING(username);
+#if APR_PROCATTR_USER_SET_REQUIRES_PASSWORD
+    TCN_FREE_CSTRING(password);
+#endif
+    return (jint) rv;
+#else
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(attr);
+    UNREFERENCED(username);
+    UNREFERENCED(password);
+
+    return APR_ENOTIMPL;
+#endif
+}
+
+TCN_IMPLEMENT_CALL(jint, Procattr, groupSet)(TCN_STDARGS,
+                                             jlong attr,
+                                             jstring group)
+{
+
+#if ((APR_MAJOR_VERSION >= 1) && (APR_MINOR_VERSION >= 1))
+    apr_status_t rv;
+    apr_procattr_t *a = J2P(attr, apr_procattr_t *);
+    TCN_ALLOC_CSTRING(group);
+
+    UNREFERENCED(o);
+
+    rv = apr_procattr_group_set(a, J2S(group));
+    TCN_FREE_CSTRING(group);
+    return (jint) rv;
+#else
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(attr);
+    UNREFERENCED(group);
+
+    return APR_ENOTIMPL;
+#endif
+}
diff --git a/connectors/jni/native/src/shm.c b/connectors/jni/native/src/shm.c
new file mode 100644
index 0000000..42ceefa
--- /dev/null
+++ b/connectors/jni/native/src/shm.c
@@ -0,0 +1,127 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Mladen Turk
+ * @version $Revision$, $Date$
+ */
+ 
+#include "tcn.h"
+#include "apr_shm.h"
+
+TCN_IMPLEMENT_CALL(jlong, Shm, create)(TCN_STDARGS, jlong reqsize,
+                                       jstring filename,
+                                       jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    const char *fname = NULL;
+    apr_shm_t *shm;
+
+
+    UNREFERENCED(o);
+    if (filename)
+        fname = (const char *)((*e)->GetStringUTFChars(e, filename, 0));
+    TCN_THROW_IF_ERR(apr_shm_create(&shm, (apr_size_t)reqsize,
+                                    fname, p), shm);
+
+cleanup:
+    if (fname)
+        (*e)->ReleaseStringUTFChars(e, filename, fname);
+    return P2J(shm);
+}
+
+TCN_IMPLEMENT_CALL(jint, Shm, remove)(TCN_STDARGS,
+                                      jstring filename,
+                                      jlong pool)
+{
+    apr_status_t rv;
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    TCN_ALLOC_CSTRING(filename);
+
+
+    UNREFERENCED(o);
+    rv = apr_shm_remove(J2S(filename), p);
+    TCN_FREE_CSTRING(filename);
+
+    return (jint)rv;
+}
+
+TCN_IMPLEMENT_CALL(jint, Shm, destroy)(TCN_STDARGS, jlong shm)
+{
+    apr_shm_t *s = J2P(shm, apr_shm_t *);
+
+    UNREFERENCED_STDARGS;
+    return (jint)apr_shm_destroy(s);
+}
+
+TCN_IMPLEMENT_CALL(jlong, Shm, attach)(TCN_STDARGS,
+                                       jstring filename,
+                                       jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    const char *fname = NULL;
+    apr_shm_t *shm;
+
+
+    UNREFERENCED(o);
+    if (filename)
+        fname = (const char *)((*e)->GetStringUTFChars(e, filename, 0));
+    TCN_THROW_IF_ERR(apr_shm_attach(&shm, fname, p), shm);
+
+cleanup:
+    if (fname)
+        (*e)->ReleaseStringUTFChars(e, filename, fname);
+    return P2J(shm);
+}
+
+TCN_IMPLEMENT_CALL(jint, Shm, detach)(TCN_STDARGS, jlong shm)
+{
+    apr_shm_t *s = J2P(shm, apr_shm_t *);
+
+    UNREFERENCED_STDARGS;
+    return (jint)apr_shm_detach(s);
+}
+
+TCN_IMPLEMENT_CALL(jlong, Shm, baseaddr)(TCN_STDARGS, jlong shm)
+{
+    apr_shm_t *s = J2P(shm, apr_shm_t *);
+
+    UNREFERENCED_STDARGS;
+    return P2J(apr_shm_baseaddr_get(s));
+}
+
+TCN_IMPLEMENT_CALL(jlong, Shm, size)(TCN_STDARGS, jlong shm)
+{
+    apr_shm_t *s = J2P(shm, apr_shm_t *);
+
+    UNREFERENCED_STDARGS;
+    return (jlong)apr_shm_size_get(s);
+}
+
+TCN_IMPLEMENT_CALL(jobject, Shm, buffer)(TCN_STDARGS, jlong shm)
+{
+    apr_shm_t *s = J2P(shm, apr_shm_t *);
+    jlong sz = (jlong)apr_shm_size_get(s);
+    void *a;
+
+    UNREFERENCED(o);
+
+    if ((a = apr_shm_baseaddr_get(s)) != NULL)
+        return (*e)->NewDirectByteBuffer(e, a, sz);
+    else
+        return NULL;
+}
diff --git a/connectors/jni/native/src/ssl.c b/connectors/jni/native/src/ssl.c
new file mode 100644
index 0000000..d908611
--- /dev/null
+++ b/connectors/jni/native/src/ssl.c
@@ -0,0 +1,913 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+#include "tcn.h"
+#include "apr_file_io.h"
+#include "apr_thread_mutex.h"
+#include "apr_atomic.h"
+#include "apr_poll.h"
+
+#ifdef HAVE_OPENSSL
+#include "ssl_private.h"
+
+static int ssl_initialized = 0;
+static char *ssl_global_rand_file = NULL;
+extern apr_pool_t *tcn_global_pool;
+
+ENGINE *tcn_ssl_engine = NULL;
+void *SSL_temp_keys[SSL_TMP_KEY_MAX];
+tcn_pass_cb_t tcn_password_callback;
+
+/*
+ * Handle the Temporary RSA Keys and DH Params
+ */
+
+#define SSL_TMP_KEY_FREE(type, idx)                     \
+    if (SSL_temp_keys[idx]) {                           \
+        type##_free((type *)SSL_temp_keys[idx]);        \
+        SSL_temp_keys[idx] = NULL;                      \
+    } else (void)(0)
+
+#define SSL_TMP_KEYS_FREE(type) \
+    SSL_TMP_KEY_FREE(type, SSL_TMP_KEY_##type##_512);   \
+    SSL_TMP_KEY_FREE(type, SSL_TMP_KEY_##type##_1024);  \
+    SSL_TMP_KEY_FREE(type, SSL_TMP_KEY_##type##_2048);  \
+    SSL_TMP_KEY_FREE(type, SSL_TMP_KEY_##type##_4096)
+
+#define SSL_TMP_KEY_INIT_RSA(bits) \
+    ssl_tmp_key_init_rsa(bits, SSL_TMP_KEY_RSA_##bits)
+
+#define SSL_TMP_KEY_INIT_DH(bits)  \
+    ssl_tmp_key_init_dh(bits, SSL_TMP_KEY_DH_##bits)
+
+#define SSL_TMP_KEYS_INIT(R)                    \
+    SSL_temp_keys[SSL_TMP_KEY_RSA_2048] = NULL; \
+    SSL_temp_keys[SSL_TMP_KEY_RSA_4096] = NULL; \
+    R |= SSL_TMP_KEY_INIT_RSA(512);             \
+    R |= SSL_TMP_KEY_INIT_RSA(1024);            \
+    R |= SSL_TMP_KEY_INIT_DH(512);              \
+    R |= SSL_TMP_KEY_INIT_DH(1024);             \
+    R |= SSL_TMP_KEY_INIT_DH(2048);             \
+    R |= SSL_TMP_KEY_INIT_DH(4096)
+
+static int ssl_tmp_key_init_rsa(int bits, int idx)
+{
+    if (!(SSL_temp_keys[idx] =
+          RSA_generate_key(bits, RSA_F4, NULL, NULL)))
+        return 1;
+    else
+        return 0;
+}
+
+static int ssl_tmp_key_init_dh(int bits, int idx)
+{
+    if (!(SSL_temp_keys[idx] =
+          SSL_dh_get_tmp_param(bits)))
+        return 1;
+    else
+        return 0;
+}
+
+
+TCN_IMPLEMENT_CALL(jint, SSL, version)(TCN_STDARGS)
+{
+    UNREFERENCED_STDARGS;
+    return OPENSSL_VERSION_NUMBER;
+}
+
+TCN_IMPLEMENT_CALL(jstring, SSL, versionString)(TCN_STDARGS)
+{
+    UNREFERENCED(o);
+    return AJP_TO_JSTRING(OPENSSL_VERSION_TEXT);
+}
+
+/*
+ *  the various processing hooks
+ */
+static apr_status_t ssl_init_cleanup(void *data)
+{
+    UNREFERENCED(data);
+
+    if (!ssl_initialized)
+        return APR_SUCCESS;
+    ssl_initialized = 0;
+
+    if (tcn_password_callback.cb.obj) {
+        JNIEnv *env;
+        tcn_get_java_env(&env);
+        TCN_UNLOAD_CLASS(env,
+                         tcn_password_callback.cb.obj);
+    }
+
+    SSL_TMP_KEYS_FREE(RSA);
+    SSL_TMP_KEYS_FREE(DH);
+    /*
+     * Try to kill the internals of the SSL library.
+     */
+#if OPENSSL_VERSION_NUMBER >= 0x00907001
+    /* Corresponds to OPENSSL_load_builtin_modules():
+     * XXX: borrowed from apps.h, but why not CONF_modules_free()
+     * which also invokes CONF_modules_finish()?
+     */
+    CONF_modules_unload(1);
+#endif
+    /* Corresponds to SSL_library_init: */
+    EVP_cleanup();
+#if HAVE_ENGINE_LOAD_BUILTIN_ENGINES
+    ENGINE_cleanup();
+#endif
+#if OPENSSL_VERSION_NUMBER >= 0x00907001
+    CRYPTO_cleanup_all_ex_data();
+#endif
+    ERR_remove_state(0);
+
+    /* Don't call ERR_free_strings here; ERR_load_*_strings only
+     * actually load the error strings once per process due to static
+     * variable abuse in OpenSSL. */
+
+    /*
+     * TODO: determine somewhere we can safely shove out diagnostics
+     *       (when enabled) at this late stage in the game:
+     * CRYPTO_mem_leaks_fp(stderr);
+     */
+    return APR_SUCCESS;
+}
+
+#ifndef OPENSSL_NO_ENGINE
+/* Try to load an engine in a shareable library */
+static ENGINE *ssl_try_load_engine(const char *engine)
+{
+    ENGINE *e = ENGINE_by_id("dynamic");
+    if (e) {
+        if (!ENGINE_ctrl_cmd_string(e, "SO_PATH", engine, 0)
+            || !ENGINE_ctrl_cmd_string(e, "LOAD", NULL, 0)) {
+            ENGINE_free(e);
+            e = NULL;
+        }
+    }
+    return e;
+}
+#endif
+
+/*
+ * To ensure thread-safetyness in OpenSSL
+ */
+
+static apr_thread_mutex_t **ssl_lock_cs;
+static int                  ssl_lock_num_locks;
+
+static void ssl_thread_lock(int mode, int type,
+                            const char *file, int line)
+{
+    UNREFERENCED(file);
+    UNREFERENCED(line);
+    if (type < ssl_lock_num_locks) {
+        if (mode & CRYPTO_LOCK) {
+            apr_thread_mutex_lock(ssl_lock_cs[type]);
+        }
+        else {
+            apr_thread_mutex_unlock(ssl_lock_cs[type]);
+        }
+    }
+}
+
+static unsigned long ssl_thread_id(void)
+{
+    /* OpenSSL needs this to return an unsigned long.  On OS/390, the pthread
+     * id is a structure twice that big.  Use the TCB pointer instead as a
+     * unique unsigned long.
+     */
+#ifdef __MVS__
+    struct PSA {
+        char unmapped[540];
+        unsigned long PSATOLD;
+    } *psaptr = 0;
+
+    return psaptr->PSATOLD;
+#else
+    return (unsigned long)(apr_os_thread_current());
+#endif
+}
+
+static apr_status_t ssl_thread_cleanup(void *data)
+{
+    UNREFERENCED(data);
+    CRYPTO_set_locking_callback(NULL);
+    CRYPTO_set_id_callback(NULL);
+    /* Let the registered mutex cleanups do their own thing
+     */
+    return APR_SUCCESS;
+}
+
+static void ssl_thread_setup(apr_pool_t *p)
+{
+    int i;
+
+    ssl_lock_num_locks = CRYPTO_num_locks();
+    ssl_lock_cs = apr_palloc(p, ssl_lock_num_locks * sizeof(*ssl_lock_cs));
+
+    for (i = 0; i < ssl_lock_num_locks; i++) {
+        apr_thread_mutex_create(&(ssl_lock_cs[i]),
+                                APR_THREAD_MUTEX_DEFAULT, p);
+    }
+
+    CRYPTO_set_id_callback(ssl_thread_id);
+    CRYPTO_set_locking_callback(ssl_thread_lock);
+
+    apr_pool_cleanup_register(p, NULL, ssl_thread_cleanup,
+                              apr_pool_cleanup_null);
+}
+
+static int ssl_rand_choosenum(int l, int h)
+{
+    int i;
+    char buf[50];
+
+    apr_snprintf(buf, sizeof(buf), "%.0f",
+                 (((double)(rand()%RAND_MAX)/RAND_MAX)*(h-l)));
+    i = atoi(buf)+1;
+    if (i < l) i = l;
+    if (i > h) i = h;
+    return i;
+}
+
+static int ssl_rand_load_file(const char *file)
+{
+    char buffer[APR_PATH_MAX];
+    int n;
+
+    if (file == NULL)
+        file = ssl_global_rand_file;
+    if (file && (strcmp(file, "builtin") == 0))
+        return -1;
+    if (file == NULL)
+        file = RAND_file_name(buffer, sizeof(buffer));
+    if (file) {
+        if (strncmp(file, "egd:", 4) == 0) {
+            if ((n = RAND_egd(file + 4)) > 0)
+                return n;
+            else
+                return -1;
+        }
+        if ((n = RAND_load_file(file, -1)) > 0)
+            return n;
+    }
+    return -1;
+}
+
+/*
+ * writes a number of random bytes (currently 1024) to
+ * file which can be used to initialize the PRNG by calling
+ * RAND_load_file() in a later session
+ */
+static int ssl_rand_save_file(const char *file)
+{
+    char buffer[APR_PATH_MAX];
+    int n;
+
+    if (file == NULL)
+        file = RAND_file_name(buffer, sizeof(buffer));
+    else if ((n = RAND_egd(file)) > 0) {
+        return 0;
+    }
+    if (file == NULL || !RAND_write_file(file))
+        return 0;
+    else
+        return 1;
+}
+
+int SSL_rand_seed(const char *file)
+{
+    unsigned char stackdata[256];
+    static volatile apr_uint32_t counter = 0;
+
+    if (ssl_rand_load_file(file) < 0) {
+        int n;
+        struct {
+            apr_time_t    t;
+            pid_t         p;
+            unsigned long i;
+            apr_uint32_t  u;
+        } _ssl_seed;
+        if (counter == 0) {
+            apr_generate_random_bytes(stackdata, 256);
+            RAND_seed(stackdata, 128);
+        }
+        _ssl_seed.t = apr_time_now();
+        _ssl_seed.p = getpid();
+        _ssl_seed.i = ssl_thread_id();
+        apr_atomic_inc32(&counter);
+        _ssl_seed.u = counter;
+        RAND_seed((unsigned char *)&_ssl_seed, sizeof(_ssl_seed));
+        /*
+         * seed in some current state of the run-time stack (128 bytes)
+         */
+        n = ssl_rand_choosenum(0, sizeof(stackdata)-128-1);
+        RAND_seed(stackdata + n, 128);
+    }
+    return RAND_status();
+}
+
+static int ssl_rand_make(const char *file, int len, int base64)
+{
+    int r;
+    int num = len;
+    BIO *out = NULL;
+
+    out = BIO_new(BIO_s_file());
+    if (out == NULL)
+        return 0;
+    if ((r = BIO_write_filename(out, (char *)file)) < 0) {
+        BIO_free_all(out);
+        return 0;
+    }
+    if (base64) {
+        BIO *b64 = BIO_new(BIO_f_base64());
+        if (b64 == NULL) {
+            BIO_free_all(out);
+            return 0;
+        }
+        out = BIO_push(b64, out);
+    }
+    while (num > 0) {
+        unsigned char buf[4096];
+        int len = num;
+        if (len > sizeof(buf))
+            len = sizeof(buf);
+        r = RAND_bytes(buf, len);
+        if (r <= 0) {
+            BIO_free_all(out);
+            return 0;
+        }
+        BIO_write(out, buf, len);
+        num -= len;
+    }
+    BIO_flush(out);
+    BIO_free_all(out);
+    return 1;
+}
+
+TCN_IMPLEMENT_CALL(jint, SSL, initialize)(TCN_STDARGS, jstring engine)
+{
+    int r = 0;
+    TCN_ALLOC_CSTRING(engine);
+
+    UNREFERENCED(o);
+    if (!tcn_global_pool) {
+        TCN_FREE_CSTRING(engine);
+        tcn_ThrowAPRException(e, APR_EINVAL);
+        return (jint)APR_EINVAL;
+    }
+    /* Check if already initialized */
+    if (ssl_initialized++) {
+        TCN_FREE_CSTRING(engine);
+        return (jint)APR_SUCCESS;
+    }
+    if (SSLeay() < 0x0090700L) {
+        TCN_FREE_CSTRING(engine);
+        tcn_ThrowAPRException(e, APR_EINVAL);
+        ssl_initialized = 0;
+        return (jint)APR_EINVAL;
+    }
+    /* We must register the library in full, to ensure our configuration
+     * code can successfully test the SSL environment.
+     */
+    CRYPTO_malloc_init();
+    ERR_load_crypto_strings();
+    SSL_load_error_strings();
+    SSL_library_init();
+#if HAVE_ENGINE_LOAD_BUILTIN_ENGINES
+    ENGINE_load_builtin_engines();
+#endif
+#if OPENSSL_VERSION_NUMBER >= 0x00907001
+    OPENSSL_load_builtin_modules();
+#endif
+
+#ifndef OPENSSL_NO_ENGINE
+    if (J2S(engine)) {
+        ENGINE *ee = NULL;
+        apr_status_t err = APR_SUCCESS;
+        if(strcmp(J2S(engine), "auto") == 0) {
+            ENGINE_register_all_complete();
+        }
+        else {
+            if ((ee = ENGINE_by_id(J2S(engine))) == NULL
+                && (ee = ssl_try_load_engine(J2S(engine))) == NULL)
+                err = APR_ENOTIMPL;
+            else {
+                if (strcmp(J2S(engine), "chil") == 0)
+                    ENGINE_ctrl(ee, ENGINE_CTRL_CHIL_SET_FORKCHECK, 1, 0, 0);
+                if (!ENGINE_set_default(ee, ENGINE_METHOD_ALL))
+                    err = APR_ENOTIMPL;
+            }
+            /* Free our "structural" reference. */
+            if (ee)
+                ENGINE_free(ee);
+        }
+        if (err != APR_SUCCESS) {
+            TCN_FREE_CSTRING(engine);
+            ssl_init_cleanup(NULL);
+            tcn_ThrowAPRException(e, err);
+            return (jint)err;
+        }
+        tcn_ssl_engine = ee;
+    }
+#endif
+
+    memset(&tcn_password_callback, 0, sizeof(tcn_pass_cb_t));
+    /* Initialize PRNG
+     * This will in most cases call the builtin
+     * low entropy seed.
+     */
+    SSL_rand_seed(NULL);
+    /* For SSL_get_app_data2() at request time */
+    SSL_init_app_data2_idx();
+
+    SSL_TMP_KEYS_INIT(r);
+    if (r) {
+        TCN_FREE_CSTRING(engine);
+        ssl_init_cleanup(NULL);
+        tcn_ThrowAPRException(e, APR_ENOTIMPL);
+        return APR_ENOTIMPL;
+    }
+    /*
+     * Let us cleanup the ssl library when the library is unloaded
+     */
+    apr_pool_cleanup_register(tcn_global_pool, NULL,
+                              ssl_init_cleanup,
+                              apr_pool_cleanup_null);
+    /* Initialize thread support */
+    ssl_thread_setup(tcn_global_pool);
+    TCN_FREE_CSTRING(engine);
+    return (jint)APR_SUCCESS;
+}
+
+TCN_IMPLEMENT_CALL(jboolean, SSL, randLoad)(TCN_STDARGS, jstring file)
+{
+    TCN_ALLOC_CSTRING(file);
+    int r;
+    UNREFERENCED(o);
+    r = SSL_rand_seed(J2S(file));
+    TCN_FREE_CSTRING(file);
+    return r ? JNI_TRUE : JNI_FALSE;
+}
+
+TCN_IMPLEMENT_CALL(jboolean, SSL, randSave)(TCN_STDARGS, jstring file)
+{
+    TCN_ALLOC_CSTRING(file);
+    int r;
+    UNREFERENCED(o);
+    r = ssl_rand_save_file(J2S(file));
+    TCN_FREE_CSTRING(file);
+    return r ? JNI_TRUE : JNI_FALSE;
+}
+
+TCN_IMPLEMENT_CALL(jboolean, SSL, randMake)(TCN_STDARGS, jstring file,
+                                            jint length, jboolean base64)
+{
+    TCN_ALLOC_CSTRING(file);
+    int r;
+    UNREFERENCED(o);
+    r = ssl_rand_make(J2S(file), length, base64);
+    TCN_FREE_CSTRING(file);
+    return r ? JNI_TRUE : JNI_FALSE;
+}
+
+TCN_IMPLEMENT_CALL(void, SSL, randSet)(TCN_STDARGS, jstring file)
+{
+    TCN_ALLOC_CSTRING(file);
+    UNREFERENCED(o);
+    if (J2S(file)) {
+        ssl_global_rand_file = apr_pstrdup(tcn_global_pool, J2S(file));
+    }
+    TCN_FREE_CSTRING(file);
+}
+/* OpenSSL Java Stream BIO */
+
+typedef struct  {
+    int            refcount;
+    apr_pool_t     *pool;
+    tcn_callback_t cb;
+} BIO_JAVA;
+
+
+static apr_status_t generic_bio_cleanup(void *data)
+{
+    BIO *b = (BIO *)data;
+
+    if (b) {
+        BIO_free(b);
+    }
+    return APR_SUCCESS;
+}
+
+void SSL_BIO_close(BIO *bi)
+{
+    if (bi == NULL)
+        return;
+    if (bi->ptr != NULL && (bi->flags & SSL_BIO_FLAG_CALLBACK)) {
+        BIO_JAVA *j = (BIO_JAVA *)bi->ptr;
+        j->refcount--;
+        if (j->refcount == 0) {
+            if (j->pool)
+                apr_pool_cleanup_run(j->pool, bi, generic_bio_cleanup);
+            else
+                BIO_free(bi);
+        }
+    }
+    else
+        BIO_free(bi);
+}
+
+void SSL_BIO_doref(BIO *bi)
+{
+    if (bi == NULL)
+        return;
+    if (bi->ptr != NULL && (bi->flags & SSL_BIO_FLAG_CALLBACK)) {
+        BIO_JAVA *j = (BIO_JAVA *)bi->ptr;
+        j->refcount++;
+    }
+}
+
+
+static int jbs_new(BIO *bi)
+{
+    BIO_JAVA *j;
+
+    if ((j = OPENSSL_malloc(sizeof(BIO_JAVA))) == NULL)
+        return 0;
+    j->pool      = NULL;
+    j->refcount  = 1;
+    bi->shutdown = 1;
+    bi->init     = 0;
+    bi->num      = -1;
+    bi->ptr      = (char *)j;
+
+    return 1;
+}
+
+static int jbs_free(BIO *bi)
+{
+    if (bi == NULL)
+        return 0;
+    if (bi->ptr != NULL) {
+        BIO_JAVA *j = (BIO_JAVA *)bi->ptr;
+        if (bi->init) {
+            JNIEnv   *e = NULL;
+            bi->init = 0;
+            tcn_get_java_env(&e);
+            TCN_UNLOAD_CLASS(e, j->cb.obj);
+        }
+        OPENSSL_free(bi->ptr);
+    }
+    bi->ptr = NULL;
+    return 1;
+}
+
+static int jbs_write(BIO *b, const char *in, int inl)
+{
+    jint ret = 0;
+    if (b->init && in != NULL) {
+        BIO_JAVA *j = (BIO_JAVA *)b->ptr;
+        JNIEnv   *e = NULL;
+        jbyteArray jb = (*e)->NewByteArray(e, inl);
+        tcn_get_java_env(&e);
+        if (!(*e)->ExceptionOccurred(e)) {
+            (*e)->SetByteArrayRegion(e, jb, 0, inl, (jbyte *)in);
+            ret = (*e)->CallIntMethod(e, j->cb.obj,
+                                      j->cb.mid[0], jb);
+            (*e)->ReleaseByteArrayElements(e, jb, (jbyte *)in, JNI_ABORT);
+            (*e)->DeleteLocalRef(e, jb);
+        }
+    }
+    return ret;
+}
+
+static int jbs_read(BIO *b, char *out, int outl)
+{
+    jint ret = 0;
+    if (b->init && out != NULL) {
+        BIO_JAVA *j = (BIO_JAVA *)b->ptr;
+        JNIEnv   *e = NULL;
+        jbyteArray jb = (*e)->NewByteArray(e, outl);
+        tcn_get_java_env(&e);
+        if (!(*e)->ExceptionOccurred(e)) {
+            ret = (*e)->CallIntMethod(e, j->cb.obj,
+                                      j->cb.mid[1], jb);
+            if (ret > 0) {
+                jbyte *jout = (*e)->GetPrimitiveArrayCritical(e, jb, NULL);
+                memcpy(out, jout, ret);
+                (*e)->ReleasePrimitiveArrayCritical(e, jb, jout, 0);
+            }
+            (*e)->DeleteLocalRef(e, jb);
+        }
+    }
+    return ret;
+}
+
+static int jbs_puts(BIO *b, const char *in)
+{
+    int ret = 0;
+    if (b->init && in != NULL) {
+        BIO_JAVA *j = (BIO_JAVA *)b->ptr;
+        JNIEnv   *e = NULL;
+        tcn_get_java_env(&e);
+        ret = (*e)->CallIntMethod(e, j->cb.obj,
+                                  j->cb.mid[2],
+                                  tcn_new_string(e, in));
+    }
+    return ret;
+}
+
+static int jbs_gets(BIO *b, char *out, int outl)
+{
+    int ret = 0;
+    if (b->init && out != NULL) {
+        BIO_JAVA *j = (BIO_JAVA *)b->ptr;
+        JNIEnv   *e = NULL;
+        jobject  o;
+        tcn_get_java_env(&e);
+        if ((o = (*e)->CallObjectMethod(e, j->cb.obj,
+                            j->cb.mid[3], (jint)(outl - 1)))) {
+            TCN_ALLOC_CSTRING(o);
+            if (J2S(o)) {
+                int l = (int)strlen(J2S(o));
+                if (l < outl) {
+                    strcpy(out, J2S(o));
+                    ret = outl;
+                }
+            }
+            TCN_FREE_CSTRING(o);
+        }
+    }
+    return ret;
+}
+
+static long jbs_ctrl(BIO *b, int cmd, long num, void *ptr)
+{
+    return 0;
+}
+
+static BIO_METHOD jbs_methods = {
+    BIO_TYPE_FILE,
+    "Java Callback",
+    jbs_write,
+    jbs_read,
+    jbs_puts,
+    jbs_gets,
+    jbs_ctrl,
+    jbs_new,
+    jbs_free,
+    NULL
+};
+
+static BIO_METHOD *BIO_jbs()
+{
+    return(&jbs_methods);
+}
+
+TCN_IMPLEMENT_CALL(jlong, SSL, newBIO)(TCN_STDARGS, jlong pool,
+                                       jobject callback)
+{
+    BIO *bio = NULL;
+    BIO_JAVA *j;
+    jclass cls;
+
+    UNREFERENCED(o);
+
+    if ((bio = BIO_new(BIO_jbs())) == NULL) {
+        tcn_ThrowException(e, "Create BIO failed");
+        goto init_failed;
+    }
+    j = (BIO_JAVA *)bio->ptr;
+    if ((j = (BIO_JAVA *)bio->ptr) == NULL) {
+        tcn_ThrowException(e, "Create BIO failed");
+        goto init_failed;
+    }
+    j->pool = J2P(pool, apr_pool_t *);
+    if (j->pool) {
+        apr_pool_cleanup_register(j->pool, (const void *)bio,
+                                  generic_bio_cleanup,
+                                  apr_pool_cleanup_null);
+    }
+
+    cls = (*e)->GetObjectClass(e, callback);
+    j->cb.mid[0] = (*e)->GetMethodID(e, cls, "write", "([B)I");
+    j->cb.mid[1] = (*e)->GetMethodID(e, cls, "read",  "([B)I");
+    j->cb.mid[2] = (*e)->GetMethodID(e, cls, "puts",  "(Ljava/lang/String;)I");
+    j->cb.mid[3] = (*e)->GetMethodID(e, cls, "gets",  "(I)Ljava/lang/String;");
+    /* TODO: Check if method id's are valid */
+    j->cb.obj    = (*e)->NewGlobalRef(e, callback);
+
+    bio->init  = 1;
+    bio->flags = SSL_BIO_FLAG_CALLBACK;
+    return P2J(bio);
+init_failed:
+    return 0;
+}
+
+TCN_IMPLEMENT_CALL(jint, SSL, closeBIO)(TCN_STDARGS, jlong bio)
+{
+    BIO *b = J2P(bio, BIO *);
+    UNREFERENCED_STDARGS;
+    SSL_BIO_close(b);
+    return APR_SUCCESS;
+}
+
+TCN_IMPLEMENT_CALL(void, SSL, setPasswordCallback)(TCN_STDARGS,
+                                                   jobject callback)
+{
+    jclass cls;
+
+    UNREFERENCED(o);
+    if (tcn_password_callback.cb.obj) {
+        TCN_UNLOAD_CLASS(e,
+                         tcn_password_callback.cb.obj);
+    }
+    cls = (*e)->GetObjectClass(e, callback);
+    tcn_password_callback.cb.mid[0] = (*e)->GetMethodID(e, cls, "callback",
+                           "(Ljava/lang/String;)Ljava/lang/String;");
+    /* TODO: Check if method id is valid */
+    tcn_password_callback.cb.obj    = (*e)->NewGlobalRef(e, callback);
+
+}
+
+TCN_IMPLEMENT_CALL(void, SSL, setPassword)(TCN_STDARGS, jstring password)
+{
+    TCN_ALLOC_CSTRING(password);
+    UNREFERENCED(o);
+    if (J2S(password)) {
+        strncpy(tcn_password_callback.password, J2S(password), SSL_MAX_PASSWORD_LEN);
+        tcn_password_callback.password[SSL_MAX_PASSWORD_LEN-1] = '\0';
+    }
+    TCN_FREE_CSTRING(password);
+}
+
+TCN_IMPLEMENT_CALL(jboolean, SSL, generateRSATempKey)(TCN_STDARGS, jint idx)
+{
+    int r = 1;
+    UNREFERENCED_STDARGS;
+    SSL_TMP_KEY_FREE(RSA, idx);
+    switch (idx) {
+        case SSL_TMP_KEY_RSA_512:
+            r = SSL_TMP_KEY_INIT_RSA(512);
+        break;
+        case SSL_TMP_KEY_RSA_1024:
+            r = SSL_TMP_KEY_INIT_RSA(1024);
+        break;
+        case SSL_TMP_KEY_RSA_2048:
+            r = SSL_TMP_KEY_INIT_RSA(2048);
+        break;
+        case SSL_TMP_KEY_RSA_4096:
+            r = SSL_TMP_KEY_INIT_RSA(4096);
+        break;
+    }
+    return r ? JNI_FALSE : JNI_TRUE;
+}
+
+TCN_IMPLEMENT_CALL(jboolean, SSL, loadDSATempKey)(TCN_STDARGS, jint idx,
+                                                  jstring file)
+{
+    jboolean r = JNI_FALSE;
+    TCN_ALLOC_CSTRING(file);
+    DH *dh;
+    UNREFERENCED(o);
+
+    if (!J2S(file))
+        return JNI_FALSE;
+    SSL_TMP_KEY_FREE(DSA, idx);
+    if ((dh = SSL_dh_get_param_from_file(J2S(file)))) {
+        SSL_temp_keys[idx] = dh;
+        r = JNI_TRUE;
+    }
+    TCN_FREE_CSTRING(file);
+    return r;
+}
+
+TCN_IMPLEMENT_CALL(jstring, SSL, getLastError)(TCN_STDARGS)
+{
+    char buf[256];
+    UNREFERENCED(o);
+    ERR_error_string(ERR_get_error(), buf);
+    return tcn_new_string(e, buf);
+}
+
+#else
+/* OpenSSL is not supported.
+ * Create empty stubs.
+ */
+
+TCN_IMPLEMENT_CALL(jint, SSL, version)(TCN_STDARGS)
+{
+    UNREFERENCED_STDARGS;
+    return 0;
+}
+
+TCN_IMPLEMENT_CALL(jstring, SSL, versionString)(TCN_STDARGS)
+{
+    UNREFERENCED_STDARGS;
+    return NULL;
+}
+
+TCN_IMPLEMENT_CALL(jint, SSL, initialize)(TCN_STDARGS, jstring engine)
+{
+    UNREFERENCED(o);
+    UNREFERENCED(engine);
+    tcn_ThrowAPRException(e, APR_ENOTIMPL);
+    return (jint)APR_ENOTIMPL;
+}
+
+TCN_IMPLEMENT_CALL(jboolean, SSL, randLoad)(TCN_STDARGS, jstring file)
+{
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(file);
+    return JNI_FALSE;
+}
+
+TCN_IMPLEMENT_CALL(jboolean, SSL, randSave)(TCN_STDARGS, jstring file)
+{
+    UNREFERENCED_STDARGS;
+    return JNI_FALSE;
+}
+
+TCN_IMPLEMENT_CALL(jboolean, SSL, randMake)(TCN_STDARGS, jstring file,
+                                            jint length, jboolean base64)
+{
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(file);
+    UNREFERENCED(length);
+    UNREFERENCED(base64);
+    return JNI_FALSE;
+}
+
+TCN_IMPLEMENT_CALL(jlong, SSL, newBIO)(TCN_STDARGS, jlong pool,
+                                       jobject callback)
+{
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(pool);
+    UNREFERENCED(callback);
+    return 0;
+}
+
+TCN_IMPLEMENT_CALL(jint, SSL, closeBIO)(TCN_STDARGS, jlong bio)
+{
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(bio);
+    return (jint)APR_ENOTIMPL;
+}
+
+TCN_IMPLEMENT_CALL(void, SSL, setPasswordCallback)(TCN_STDARGS,
+                                                   jobject callback)
+{
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(callback);
+}
+
+TCN_IMPLEMENT_CALL(void, SSL, setPassword)(TCN_STDARGS, jstring password)
+{
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(password);
+}
+
+TCN_IMPLEMENT_CALL(jboolean, SSL, generateRSATempKey)(TCN_STDARGS, jint idx)
+{
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(idx);
+    return JNI_FALSE;
+}
+
+TCN_IMPLEMENT_CALL(jboolean, SSL, loadDSATempKey)(TCN_STDARGS, jint idx,
+                                                  jstring file)
+{
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(idx);
+    UNREFERENCED(file);
+    return JNI_FALSE;
+}
+
+TCN_IMPLEMENT_CALL(jstring, SSL, getLastError)(TCN_STDARGS)
+{
+    UNREFERENCED_STDARGS;
+    return NULL;
+}
+
+#endif
diff --git a/connectors/jni/native/src/sslcontext.c b/connectors/jni/native/src/sslcontext.c
new file mode 100644
index 0000000..c1ff8fe
--- /dev/null
+++ b/connectors/jni/native/src/sslcontext.c
@@ -0,0 +1,717 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** SSL Context wrapper
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+#include "tcn.h"
+
+#include "apr_file_io.h"
+#include "apr_thread_mutex.h"
+#include "apr_poll.h"
+
+#ifdef HAVE_OPENSSL
+#include "ssl_private.h"
+
+static apr_status_t ssl_context_cleanup(void *data)
+{
+    tcn_ssl_ctxt_t *c = (tcn_ssl_ctxt_t *)data;
+    if (c) {
+        int i;
+        if (c->crl)
+            X509_STORE_free(c->crl);
+        c->crl = NULL;
+        if (c->ctx)
+            SSL_CTX_free(c->ctx);
+        c->ctx = NULL;
+        for (i = 0; i < SSL_AIDX_MAX; i++) {
+            if (c->certs[i]) {
+                X509_free(c->certs[i]);
+                c->certs[i] = NULL;
+            }
+            if (c->keys[i]) {
+                EVP_PKEY_free(c->keys[i]);
+                c->keys[i] = NULL;
+            }
+        }
+        if (c->bio_is) {
+            SSL_BIO_close(c->bio_is);
+            c->bio_is = NULL;
+        }
+        if (c->bio_os) {
+            SSL_BIO_close(c->bio_os);
+            c->bio_os = NULL;
+        }
+    }
+    return APR_SUCCESS;
+}
+
+/* Initialize server context */
+TCN_IMPLEMENT_CALL(jlong, SSLContext, make)(TCN_STDARGS, jlong pool,
+                                            jint protocol, jint mode)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    tcn_ssl_ctxt_t *c = NULL;
+    SSL_CTX *ctx = NULL;
+    UNREFERENCED(o);
+
+    switch (protocol) {
+        case SSL_PROTOCOL_SSLV2:
+        case SSL_PROTOCOL_SSLV2 | SSL_PROTOCOL_TLSV1:
+            if (mode == SSL_MODE_CLIENT)
+                ctx = SSL_CTX_new(SSLv2_client_method());
+            else if (mode == SSL_MODE_SERVER)
+                ctx = SSL_CTX_new(SSLv2_server_method());
+            else
+                ctx = SSL_CTX_new(SSLv2_method());
+        break;
+        case SSL_PROTOCOL_SSLV3:
+        case SSL_PROTOCOL_SSLV3 | SSL_PROTOCOL_TLSV1:
+            if (mode == SSL_MODE_CLIENT)
+                ctx = SSL_CTX_new(SSLv3_client_method());
+            else if (mode == SSL_MODE_SERVER)
+                ctx = SSL_CTX_new(SSLv3_server_method());
+            else
+                ctx = SSL_CTX_new(SSLv3_method());
+        break;
+        case SSL_PROTOCOL_SSLV2 | SSL_PROTOCOL_SSLV3:
+        case SSL_PROTOCOL_ALL:
+            if (mode == SSL_MODE_CLIENT)
+                ctx = SSL_CTX_new(SSLv23_client_method());
+            else if (mode == SSL_MODE_SERVER)
+                ctx = SSL_CTX_new(SSLv23_server_method());
+            else
+                ctx = SSL_CTX_new(SSLv23_method());
+        break;
+        case SSL_PROTOCOL_TLSV1:
+            if (mode == SSL_MODE_CLIENT)
+                ctx = SSL_CTX_new(TLSv1_client_method());
+            else if (mode == SSL_MODE_SERVER)
+                ctx = SSL_CTX_new(TLSv1_server_method());
+            else
+                ctx = SSL_CTX_new(TLSv1_method());
+        break;
+    }
+    if (!ctx) {
+        tcn_ThrowException(e, "Invalid Server SSL Protocol");
+        goto init_failed;
+    }
+    if ((c = apr_pcalloc(p, sizeof(tcn_ssl_ctxt_t))) == NULL) {
+        tcn_ThrowAPRException(e, apr_get_os_error());
+        goto init_failed;
+    }
+
+    c->protocol = protocol;
+    c->mode     = mode;
+    c->ctx      = ctx;
+    c->pool     = p;
+    c->bio_os   = BIO_new(BIO_s_file());
+    if (c->bio_os != NULL)
+        BIO_set_fp(c->bio_os, stderr, BIO_NOCLOSE | BIO_FP_TEXT);
+    SSL_CTX_set_options(c->ctx, SSL_OP_ALL);
+    if (!(protocol & SSL_PROTOCOL_SSLV2))
+        SSL_CTX_set_options(c->ctx, SSL_OP_NO_SSLv2);
+    if (!(protocol & SSL_PROTOCOL_SSLV3))
+        SSL_CTX_set_options(c->ctx, SSL_OP_NO_SSLv3);
+    if (!(protocol & SSL_PROTOCOL_TLSV1))
+        SSL_CTX_set_options(c->ctx, SSL_OP_NO_TLSv1);
+    /*
+     * Configure additional context ingredients
+     */
+    SSL_CTX_set_options(c->ctx, SSL_OP_SINGLE_DH_USE);
+
+#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
+    /*
+     * Disallow a session from being resumed during a renegotiation,
+     * so that an acceptable cipher suite can be negotiated.
+     */
+    SSL_CTX_set_options(c->ctx, SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
+#endif
+    /* Default session context id and cache size */
+    SSL_CTX_sess_set_cache_size(c->ctx, SSL_DEFAULT_CACHE_SIZE);
+    MD5((const unsigned char *)SSL_DEFAULT_VHOST_NAME,
+        (unsigned long)(sizeof(SSL_DEFAULT_VHOST_NAME) - 1),
+        &(c->context_id[0]));
+    if (mode) {
+        SSL_CTX_set_tmp_rsa_callback(c->ctx, SSL_callback_tmp_RSA);
+        SSL_CTX_set_tmp_dh_callback(c->ctx,  SSL_callback_tmp_DH);
+    }
+    /* Set default Certificate verification level
+     * and depth for the Client Authentication
+     */
+    c->verify_depth  = 1;
+    c->verify_mode   = SSL_CVERIFY_UNSET;
+    c->shutdown_type = SSL_SHUTDOWN_TYPE_UNSET;
+
+    /* Set default password callback */
+    SSL_CTX_set_default_passwd_cb(c->ctx, (pem_password_cb *)SSL_password_callback);
+    SSL_CTX_set_default_passwd_cb_userdata(c->ctx, (void *)(&tcn_password_callback));
+    /*
+     * Let us cleanup the ssl context when the pool is destroyed
+     */
+    apr_pool_cleanup_register(p, (const void *)c,
+                              ssl_context_cleanup,
+                              apr_pool_cleanup_null);
+
+    return P2J(c);
+init_failed:
+    return 0;
+}
+
+TCN_IMPLEMENT_CALL(jint, SSLContext, free)(TCN_STDARGS, jlong ctx)
+{
+    tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
+    UNREFERENCED_STDARGS;
+    TCN_ASSERT(ctx != 0);
+    /* Run and destroy the cleanup callback */
+    return apr_pool_cleanup_run(c->pool, c, ssl_context_cleanup);
+}
+
+TCN_IMPLEMENT_CALL(void, SSLContext, setContextId)(TCN_STDARGS, jlong ctx,
+                                                   jstring id)
+{
+    tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
+    TCN_ALLOC_CSTRING(id);
+
+    TCN_ASSERT(ctx != 0);
+    UNREFERENCED(o);
+    if (J2S(id)) {
+        MD5((const unsigned char *)J2S(id),
+            (unsigned long)strlen(J2S(id)),
+            &(c->context_id[0]));
+    }
+    TCN_FREE_CSTRING(id);
+}
+
+TCN_IMPLEMENT_CALL(void, SSLContext, setBIO)(TCN_STDARGS, jlong ctx,
+                                             jlong bio, jint dir)
+{
+    tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
+    BIO *bio_handle   = J2P(bio, BIO *);
+
+    UNREFERENCED_STDARGS;
+    TCN_ASSERT(ctx != 0);
+    if (dir == 0) {
+        if (c->bio_os && c->bio_os != bio_handle)
+            SSL_BIO_close(c->bio_os);
+        c->bio_os = bio_handle;
+    }
+    else if (dir == 1) {
+        if (c->bio_is && c->bio_is != bio_handle)
+            SSL_BIO_close(c->bio_is);
+        c->bio_is = bio_handle;
+    }
+    else
+        return;
+    SSL_BIO_doref(bio_handle);
+}
+
+TCN_IMPLEMENT_CALL(void, SSLContext, setOptions)(TCN_STDARGS, jlong ctx,
+                                                 jint opt)
+{
+    tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
+
+    UNREFERENCED_STDARGS;
+    TCN_ASSERT(ctx != 0);
+    SSL_CTX_set_options(c->ctx, opt);
+}
+
+TCN_IMPLEMENT_CALL(void, SSLContext, setQuietShutdown)(TCN_STDARGS, jlong ctx,
+                                                       jboolean mode)
+{
+    tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
+
+    UNREFERENCED_STDARGS;
+    TCN_ASSERT(ctx != 0);
+    SSL_CTX_set_quiet_shutdown(c->ctx, mode ? 1 : 0);
+}
+
+TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCipherSuite)(TCN_STDARGS, jlong ctx,
+                                                         jstring ciphers)
+{
+    tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
+    TCN_ALLOC_CSTRING(ciphers);
+    jboolean rv = JNI_TRUE;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(ctx != 0);
+    if (!J2S(ciphers))
+        return JNI_FALSE;
+
+    if (!SSL_CTX_set_cipher_list(c->ctx, J2S(ciphers))) {
+        char err[256];
+        ERR_error_string(ERR_get_error(), err);
+        tcn_Throw(e, "Unable to configure permitted SSL ciphers (%s)", err);
+        rv = JNI_FALSE;
+    }
+    TCN_FREE_CSTRING(ciphers);
+    return rv;
+}
+
+TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCARevocation)(TCN_STDARGS, jlong ctx,
+                                                          jstring file,
+                                                          jstring path)
+{
+    tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
+    TCN_ALLOC_CSTRING(file);
+    TCN_ALLOC_CSTRING(path);
+    jboolean rv = JNI_FALSE;
+    X509_LOOKUP *lookup;
+    char err[256];
+
+    UNREFERENCED(o);
+    TCN_ASSERT(ctx != 0);
+    if (J2S(file) == NULL && J2S(path) == NULL)
+        return JNI_FALSE;
+
+    if (!c->crl) {
+        if ((c->crl = X509_STORE_new()) == NULL)
+            goto cleanup;
+    }
+    if (J2S(file)) {
+        lookup = X509_STORE_add_lookup(c->crl, X509_LOOKUP_file());
+        if (lookup == NULL) {
+            ERR_error_string(ERR_get_error(), err);
+            X509_STORE_free(c->crl);
+            c->crl = NULL;
+            tcn_Throw(e, "Lookup failed for file %s (%s)", J2S(file), err);
+            goto cleanup;
+        }
+        X509_LOOKUP_load_file(lookup, J2S(file), X509_FILETYPE_PEM);
+    }
+    if (J2S(path)) {
+        lookup = X509_STORE_add_lookup(c->crl, X509_LOOKUP_hash_dir());
+        if (lookup == NULL) {
+            ERR_error_string(ERR_get_error(), err);
+            X509_STORE_free(c->crl);
+            c->crl = NULL;
+            tcn_Throw(e, "Lookup failed for path %s (%s)", J2S(file), err);
+            goto cleanup;
+        }
+        X509_LOOKUP_add_dir(lookup, J2S(path), X509_FILETYPE_PEM);
+    }
+    rv = JNI_TRUE;
+cleanup:
+    TCN_FREE_CSTRING(file);
+    TCN_FREE_CSTRING(path);
+    return rv;
+}
+
+TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCertificateChainFile)(TCN_STDARGS, jlong ctx,
+                                                                  jstring file,
+                                                                  jboolean skipfirst)
+{
+    tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
+    jboolean rv = JNI_FALSE;
+    TCN_ALLOC_CSTRING(file);
+
+    UNREFERENCED(o);
+    TCN_ASSERT(ctx != 0);
+    if (!J2S(file))
+        return JNI_FALSE;
+    if (SSL_CTX_use_certificate_chain(c->ctx, J2S(file), skipfirst) > 0)
+        rv = JNI_TRUE;
+    TCN_FREE_CSTRING(file);
+    return rv;
+}
+
+TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCACertificate)(TCN_STDARGS,
+                                                           jlong ctx,
+                                                           jstring file,
+                                                           jstring path)
+{
+    tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
+    jboolean rv = JNI_TRUE;
+    TCN_ALLOC_CSTRING(file);
+    TCN_ALLOC_CSTRING(path);
+
+    UNREFERENCED(o);
+    TCN_ASSERT(ctx != 0);
+    if (file == NULL && path == NULL)
+        return JNI_FALSE;
+
+   /*
+     * Configure Client Authentication details
+     */
+    if (!SSL_CTX_load_verify_locations(c->ctx,
+                                       J2S(file), J2S(path))) {
+        char err[256];
+        ERR_error_string(ERR_get_error(), err);
+        tcn_Throw(e, "Unable to configure locations "
+                  "for client authentication (%s)", err);
+        rv = JNI_FALSE;
+        goto cleanup;
+    }
+    c->store = SSL_CTX_get_cert_store(c->ctx);
+    if (c->mode) {
+        STACK_OF(X509_NAME) *ca_certs;
+        c->ca_certs++;
+        ca_certs = SSL_CTX_get_client_CA_list(c->ctx);
+        if (ca_certs == NULL) {
+            SSL_load_client_CA_file(J2S(file));
+            if (ca_certs != NULL)
+                SSL_CTX_set_client_CA_list(c->ctx, (STACK *)ca_certs);
+        }
+        else {
+            if (!SSL_add_file_cert_subjects_to_stack((STACK *)ca_certs, J2S(file)))
+                ca_certs = NULL;
+        }
+        if (ca_certs == NULL && c->verify_mode == SSL_CVERIFY_REQUIRE) {
+            /*
+             * Give a warning when no CAs were configured but client authentication
+             * should take place. This cannot work.
+            */
+            BIO_printf(c->bio_os,
+                        "[WARN] Oops, you want to request client "
+                        "authentication, but no CAs are known for "
+                        "verification!?");
+        }
+    }
+cleanup:
+    TCN_FREE_CSTRING(file);
+    TCN_FREE_CSTRING(path);
+    return rv;
+}
+
+TCN_IMPLEMENT_CALL(void, SSLContext, setShutdownType)(TCN_STDARGS, jlong ctx,
+                                                      jint type)
+{
+    tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
+
+    UNREFERENCED_STDARGS;
+    TCN_ASSERT(ctx != 0);
+    c->shutdown_type = type;
+}
+
+TCN_IMPLEMENT_CALL(void, SSLContext, setVerify)(TCN_STDARGS, jlong ctx,
+                                                jint level, jint depth)
+{
+    tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
+    int verify = SSL_VERIFY_NONE;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(ctx != 0);
+    c->verify_mode = level;
+
+    if (c->verify_mode == SSL_CVERIFY_UNSET)
+        c->verify_mode = SSL_CVERIFY_NONE;
+    if (depth > 0)
+        c->verify_depth = depth;
+    /*
+     *  Configure callbacks for SSL context
+     */
+    if (c->verify_mode == SSL_CVERIFY_REQUIRE)
+        verify |= SSL_VERIFY_PEER_STRICT;
+    if ((c->verify_mode == SSL_CVERIFY_OPTIONAL) ||
+        (c->verify_mode == SSL_CVERIFY_OPTIONAL_NO_CA))
+        verify |= SSL_VERIFY_PEER;
+    if (!c->store) {
+        if (SSL_CTX_set_default_verify_paths(c->ctx)) {
+            c->store = SSL_CTX_get_cert_store(c->ctx);
+            X509_STORE_set_flags(c->store, 0);
+        }
+        else {
+            /* XXX: See if this is fatal */ 
+        }
+    }
+
+    SSL_CTX_set_verify(c->ctx, verify, SSL_callback_SSL_verify);
+}
+
+static EVP_PKEY *load_pem_key(tcn_ssl_ctxt_t *c, const char *file)
+{
+    BIO *bio = NULL;
+    EVP_PKEY *key = NULL;
+    tcn_pass_cb_t *cb_data = c->cb_data;
+    int i;
+
+    if ((bio = BIO_new(BIO_s_file())) == NULL) {
+        return NULL;
+    }
+    if (BIO_read_filename(bio, file) <= 0) {
+        BIO_free(bio);
+        return NULL;
+    }
+    if (!cb_data)
+        cb_data = &tcn_password_callback;
+    for (i = 0; i < 3; i++) {
+        key = PEM_read_bio_PrivateKey(bio, NULL,
+                    (pem_password_cb *)SSL_password_callback,
+                    (void *)cb_data);
+        if (key)
+            break;
+        cb_data->password[0] = '\0';
+        BIO_ctrl(bio, BIO_CTRL_RESET, 0, NULL);
+    }
+    BIO_free(bio);
+    return key;
+}
+
+static X509 *load_pem_cert(tcn_ssl_ctxt_t *c, const char *file)
+{
+    BIO *bio = NULL;
+    X509 *cert = NULL;
+    tcn_pass_cb_t *cb_data = c->cb_data;
+    int i;
+
+    if ((bio = BIO_new(BIO_s_file())) == NULL) {
+        return NULL;
+    }
+    if (BIO_read_filename(bio, file) <= 0) {
+        BIO_free(bio);
+        return NULL;
+    }
+    for (i = 0; i < 3; i++) {
+        cert = PEM_read_bio_X509_AUX(bio, NULL,
+                    (pem_password_cb *)SSL_password_callback,
+                    (void *)cb_data);
+        if (cert)
+            break;
+        cb_data->password[0] = '\0';
+        BIO_ctrl(bio, BIO_CTRL_RESET, 0, NULL);
+    }
+    BIO_free(bio);
+    return cert;
+}
+
+TCN_IMPLEMENT_CALL(void, SSLContext, setRandom)(TCN_STDARGS, jlong ctx,
+                                                jstring file)
+{
+    tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
+    TCN_ALLOC_CSTRING(file);    
+
+    TCN_ASSERT(ctx != 0);
+    UNREFERENCED(o);
+    if (J2S(file))
+        c->rand_file = apr_pstrdup(c->pool, J2S(file));
+    TCN_FREE_CSTRING(file);
+}
+
+TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCertificate)(TCN_STDARGS, jlong ctx,
+                                                         jstring cert, jstring key,
+                                                         jstring password, jint idx)
+{
+    tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
+    jboolean rv = JNI_TRUE;
+    TCN_ALLOC_CSTRING(cert);
+    TCN_ALLOC_CSTRING(key);
+    TCN_ALLOC_CSTRING(password);
+    const char *key_file, *cert_file;
+    char err[256];
+
+    UNREFERENCED(o);
+    TCN_ASSERT(ctx != 0);
+
+    if (idx < 0 || idx >= SSL_AIDX_MAX) {
+        /* TODO: Throw something */
+        rv = JNI_FALSE;
+        goto cleanup;
+    }
+    if (J2S(password)) {
+        if (!c->cb_data)
+            c->cb_data = &tcn_password_callback;
+        strncpy(c->cb_data->password, J2S(password), SSL_MAX_PASSWORD_LEN);
+        c->cb_data->password[SSL_MAX_PASSWORD_LEN-1] = '\0';
+    }
+    key_file  = J2S(key);
+    cert_file = J2S(cert);
+    if (!key_file)
+        key_file = cert_file;
+    if (!key_file) {
+        tcn_Throw(e, "No Certificate file specified");
+        rv = JNI_FALSE;
+        goto cleanup;
+    }
+    if ((c->keys[idx] = load_pem_key(c, key_file)) == NULL) {
+        ERR_error_string(ERR_get_error(), err);
+        tcn_Throw(e, "Unable to load certificate key %s (%s)",
+                  key_file, err);
+        rv = JNI_FALSE;
+        goto cleanup;
+    }
+    if ((c->certs[idx] = load_pem_cert(c, cert_file)) == NULL) {
+        ERR_error_string(ERR_get_error(), err);
+        tcn_Throw(e, "Unable to load certificate %s (%s)",
+                  cert_file, err);
+        rv = JNI_FALSE;
+        goto cleanup;
+    }
+    if (SSL_CTX_use_certificate(c->ctx, c->certs[idx]) <= 0) {
+        ERR_error_string(ERR_get_error(), err);
+        tcn_Throw(e, "Error setting certificate (%s)", err);
+        rv = JNI_FALSE;
+        goto cleanup;
+    }
+    if (SSL_CTX_use_PrivateKey(c->ctx, c->keys[idx]) <= 0) {
+        ERR_error_string(ERR_get_error(), err);
+        tcn_Throw(e, "Error setting private key (%s)", err);
+        rv = JNI_FALSE;
+        goto cleanup;
+    }
+    if (SSL_CTX_check_private_key(c->ctx) <= 0) {
+        ERR_error_string(ERR_get_error(), err);
+        tcn_Throw(e, "Private key does not match the certificate public key (%s)",
+                  err);
+        rv = JNI_FALSE;
+        goto cleanup;
+    }
+cleanup:
+    TCN_FREE_CSTRING(cert);
+    TCN_FREE_CSTRING(key);
+    TCN_FREE_CSTRING(password);
+    return rv;
+}
+
+#else
+/* OpenSSL is not supported.
+ * Create empty stubs.
+ */
+
+TCN_IMPLEMENT_CALL(jlong, SSLContext, make)(TCN_STDARGS, jlong pool,
+                                            jint protocol, jint mode)
+{
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(pool);
+    UNREFERENCED(protocol);
+    UNREFERENCED(mode);
+    return 0;
+}
+
+TCN_IMPLEMENT_CALL(jint, SSLContext, free)(TCN_STDARGS, jlong ctx)
+{
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(ctx);
+    return APR_ENOTIMPL;
+}
+
+TCN_IMPLEMENT_CALL(void, SSLContext, setContextId)(TCN_STDARGS, jlong ctx,
+                                                   jstring id)
+{
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(ctx);
+    UNREFERENCED(id);
+}
+
+TCN_IMPLEMENT_CALL(void, SSLContext, setBIO)(TCN_STDARGS, jlong ctx,
+                                             jlong bio, jint dir)
+{
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(ctx);
+    UNREFERENCED(bio);
+    UNREFERENCED(dir);
+}
+
+TCN_IMPLEMENT_CALL(void, SSLContext, setOptions)(TCN_STDARGS, jlong ctx,
+                                                 jint opt)
+{
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(ctx);
+    UNREFERENCED(opt);
+}
+
+TCN_IMPLEMENT_CALL(void, SSLContext, setQuietShutdown)(TCN_STDARGS, jlong ctx,
+                                                       jboolean mode)
+{
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(ctx);
+    UNREFERENCED(mode);
+}
+
+TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCipherSuite)(TCN_STDARGS, jlong ctx,
+                                                         jstring ciphers)
+{
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(ctx);
+    UNREFERENCED(ciphers);
+    return JNI_FALSE;
+}
+
+TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCARevocation)(TCN_STDARGS, jlong ctx,
+                                                          jstring file,
+                                                          jstring path)
+{
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(ctx);
+    UNREFERENCED(file);
+    UNREFERENCED(path);
+    return JNI_FALSE;
+}
+
+TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCertificateChainFile)(TCN_STDARGS, jlong ctx,
+                                                                  jstring file,
+                                                                  jboolean skipfirst)
+{
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(ctx);
+    UNREFERENCED(file);
+    UNREFERENCED(skipfirst);
+    return JNI_FALSE;
+}
+
+TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCACertificate)(TCN_STDARGS,
+                                                           jlong ctx,
+                                                           jstring file,
+                                                           jstring path)
+{
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(ctx);
+    UNREFERENCED(file);
+    UNREFERENCED(path);
+    return JNI_FALSE;
+}
+
+TCN_IMPLEMENT_CALL(void, SSLContext, setShutdownType)(TCN_STDARGS, jlong ctx,
+                                                      jint type)
+{
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(ctx);
+    UNREFERENCED(type);
+}
+
+TCN_IMPLEMENT_CALL(void, SSLContext, setVerify)(TCN_STDARGS, jlong ctx,
+                                                jint level, jint depth)
+{
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(ctx);
+    UNREFERENCED(level);
+    UNREFERENCED(depth);
+}
+
+TCN_IMPLEMENT_CALL(void, SSLContext, setRandom)(TCN_STDARGS, jlong ctx,
+                                                jstring file)
+{
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(ctx);
+    UNREFERENCED(file);
+}
+
+TCN_IMPLEMENT_CALL(jboolean, SSLContext, setCertificate)(TCN_STDARGS, jlong ctx,
+                                                         jstring cert, jstring key,
+                                                         jstring password, jint idx)
+{
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(ctx);
+    UNREFERENCED(cert);
+    UNREFERENCED(key);
+    UNREFERENCED(password);
+    UNREFERENCED(idx);
+    return JNI_FALSE;
+}
+
+#endif
diff --git a/connectors/jni/native/src/sslinfo.c b/connectors/jni/native/src/sslinfo.c
new file mode 100644
index 0000000..4d68449
--- /dev/null
+++ b/connectors/jni/native/src/sslinfo.c
@@ -0,0 +1,589 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** SSL info wrapper
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+#include "tcn.h"
+#include "apr_file_io.h"
+#include "apr_thread_mutex.h"
+#include "apr_poll.h"
+
+#ifdef HAVE_OPENSSL
+#include "ssl_private.h"
+
+static const char *hex_basis = "0123456789ABCDEF";
+
+static char *convert_to_hex(const void *buf, size_t len)
+{
+    const unsigned char *p = ( const unsigned char *)buf;
+    char *str, *s;
+    size_t i;
+
+    if ((len < 1) || ((str = malloc(len * 2 + 1)) == NULL))
+        return NULL;
+    for (i = 0, s = str; i < len; i++) {
+        unsigned char c = *p++;
+        *s++ = hex_basis[c >> 4];
+        *s++ = hex_basis[c & 0x0F];
+    }
+    *s = '\0';
+    return str;
+}
+
+#define DIGIT2NUM(x) (((x)[0] - '0') * 10 + (x)[1] - '0')
+
+static int get_days_remaining(ASN1_UTCTIME *tm)
+{
+    apr_time_t then, now = apr_time_now();
+    apr_time_exp_t exp = {0};
+    int diff;
+
+    /* Fail if the time isn't a valid ASN.1 UTCTIME; RFC3280 mandates
+     * that the seconds digits are present even though ASN.1
+     * doesn't. */
+    if (tm->length < 11 || !ASN1_UTCTIME_check(tm))
+        return 0;
+
+    exp.tm_year = DIGIT2NUM(tm->data);
+    exp.tm_mon  = DIGIT2NUM(tm->data + 2) - 1;
+    exp.tm_mday = DIGIT2NUM(tm->data + 4) + 1;
+    exp.tm_hour = DIGIT2NUM(tm->data + 6);
+    exp.tm_min  = DIGIT2NUM(tm->data + 8);
+    exp.tm_sec  = DIGIT2NUM(tm->data + 10);
+
+    if (exp.tm_year <= 50)
+        exp.tm_year += 100;
+    if (apr_time_exp_gmt_get(&then, &exp) != APR_SUCCESS)
+        return 0;
+
+    diff = (int)((apr_time_sec(then) - apr_time_sec(now)) / (60*60*24));
+    return diff > 0 ? diff : 0;
+}
+
+static char *get_cert_valid(ASN1_UTCTIME *tm)
+{
+    char *result;
+    BIO* bio;
+    int n;
+
+    if ((bio = BIO_new(BIO_s_mem())) == NULL)
+        return NULL;
+    ASN1_UTCTIME_print(bio, tm);
+    n = BIO_pending(bio);
+    result = malloc(n+1);
+    n = BIO_read(bio, result, n);
+    result[n] = '\0';
+    BIO_free(bio);
+    return result;
+}
+
+static char *get_cert_PEM(X509 *xs)
+{
+    char *result = NULL;
+    BIO *bio;
+
+    if ((bio = BIO_new(BIO_s_mem())) == NULL)
+        return NULL;
+    if (PEM_write_bio_X509(bio, xs)) {
+        int n = BIO_pending(bio);
+        result = malloc(n+1);
+        n = BIO_read(bio, result, n);
+        result[n] = '\0';
+    }
+    BIO_free(bio);
+    return result;
+}
+
+static unsigned char *get_cert_ASN1(X509 *xs, int *len)
+{
+    unsigned char *result = NULL;
+    BIO *bio;
+
+    *len = 0;
+    if ((bio = BIO_new(BIO_s_mem())) == NULL)
+        return NULL;
+    if (i2d_X509_bio(bio, xs)) {
+        int n = BIO_pending(bio);
+        result = malloc(n);
+        n = BIO_read(bio, result, n);
+        *len = n;
+    }
+    BIO_free(bio);
+    return result;
+}
+
+
+static char *get_cert_serial(X509 *xs)
+{
+    char *result;
+    BIO *bio;
+    int n;
+
+    if ((bio = BIO_new(BIO_s_mem())) == NULL)
+        return NULL;
+    i2a_ASN1_INTEGER(bio, X509_get_serialNumber(xs));
+    n = BIO_pending(bio);
+    result = malloc(n+1);
+    n = BIO_read(bio, result, n);
+    result[n] = '\0';
+    BIO_free(bio);
+    return result;
+}
+
+static const struct {
+    int   fid;
+    int   nid;
+} info_cert_dn_rec[] = {
+    { SSL_INFO_DN_COUNTRYNAME,            NID_countryName            },
+    { SSL_INFO_DN_STATEORPROVINCENAME,    NID_stateOrProvinceName    },
+    { SSL_INFO_DN_LOCALITYNAME,           NID_localityName           },
+    { SSL_INFO_DN_ORGANIZATIONNAME,       NID_organizationName       },
+    { SSL_INFO_DN_ORGANIZATIONALUNITNAME, NID_organizationalUnitName },
+    { SSL_INFO_DN_COMMONNAME,             NID_commonName             },
+    { SSL_INFO_DN_TITLE,                  NID_title                  },
+    { SSL_INFO_DN_INITIALS,               NID_initials               },
+    { SSL_INFO_DN_GIVENNAME,              NID_givenName              },
+    { SSL_INFO_DN_SURNAME,                NID_surname                },
+    { SSL_INFO_DN_DESCRIPTION,            NID_description            },
+    { SSL_INFO_DN_UNIQUEIDENTIFIER,       NID_x500UniqueIdentifier   },
+    { SSL_INFO_DN_EMAILADDRESS,           NID_pkcs9_emailAddress     },
+    { 0,                                  0                          }
+};
+
+static char *lookup_ssl_cert_dn(X509_NAME *xsname, int dnidx)
+{
+    char *result;
+    X509_NAME_ENTRY *xsne;
+    int i, j, n, idx = 0;
+
+    result = NULL;
+
+    for (i = 0; info_cert_dn_rec[i].fid != 0; i++) {
+        if (info_cert_dn_rec[i].fid == dnidx) {
+            for (j = 0; j < sk_X509_NAME_ENTRY_num((STACK_OF(X509_NAME_ENTRY) *)
+                                                   (xsname->entries)); j++) {
+                xsne = sk_X509_NAME_ENTRY_value((STACK_OF(X509_NAME_ENTRY) *)
+                                                (xsname->entries), j);
+
+                n =OBJ_obj2nid((ASN1_OBJECT *)X509_NAME_ENTRY_get_object(xsne));
+                if (n == info_cert_dn_rec[i].nid && idx-- == 0) {
+                    result = malloc(xsne->value->length + 1);
+                    memcpy(result, xsne->value->data,
+                                   xsne->value->length);
+                    result[xsne->value->length] = '\0';
+
+#if APR_CHARSET_EBCDIC
+                    ap_xlate_proto_from_ascii(result, xsne->value->length);
+#endif /* APR_CHARSET_EBCDIC */
+                    break;
+                }
+            }
+            break;
+        }
+    }
+    return result;
+}
+
+TCN_IMPLEMENT_CALL(jobject, SSLSocket, getInfoB)(TCN_STDARGS, jlong sock,
+                                                 jint what)
+{
+    tcn_socket_t   *a = J2P(sock, tcn_socket_t *);
+    tcn_ssl_conn_t *s;
+    jbyteArray array = NULL;
+    apr_status_t rv = APR_SUCCESS;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(sock != 0);
+
+    s = (tcn_ssl_conn_t *)(a->opaque);
+    switch (what) {
+        case SSL_INFO_SESSION_ID:
+        {
+            SSL_SESSION *session  = SSL_get_session(s->ssl);
+            if (session) {
+                array = tcn_new_arrayb(e, &session->session_id[0],
+                                       session->session_id_length);
+            }
+        }
+        break;
+        default:
+            rv = APR_EINVAL;
+        break;
+    }
+    if (what & SSL_INFO_CLIENT_MASK) {
+        X509 *xs;
+        unsigned char *result;
+        int len;
+        if ((xs = SSL_get_peer_certificate(s->ssl)) != NULL) {
+            switch (what) {
+                case SSL_INFO_CLIENT_CERT:
+                    if ((result = get_cert_ASN1(xs, &len))) {
+                        array = tcn_new_arrayb(e, result, len);
+                        free(result);
+                    }
+                break;
+            }
+            X509_free(xs);
+        }
+        rv = APR_SUCCESS;
+    }
+    else if (what & SSL_INFO_SERVER_MASK) {
+        X509 *xs;
+        unsigned char *result;
+        int len;
+        if ((xs = SSL_get_certificate(s->ssl)) != NULL) {
+            switch (what) {
+                case SSL_INFO_SERVER_CERT:
+                    if ((result = get_cert_ASN1(xs, &len))) {
+                        array = tcn_new_arrayb(e, result, len);
+                        free(result);
+                    }
+                break;
+            }
+            /* XXX: No need to call the X509_free(xs); */
+        }
+        rv = APR_SUCCESS;
+    }
+    else if (what & SSL_INFO_CLIENT_CERT_CHAIN) {
+        X509 *xs;
+        unsigned char *result;
+        STACK_OF(X509) *sk =  SSL_get_peer_cert_chain(s->ssl);
+        int len, n = what & 0x0F;
+        if (n < sk_X509_num(sk)) {
+            xs = sk_X509_value(sk, n);
+            if ((result = get_cert_ASN1(xs, &len))) {
+                array = tcn_new_arrayb(e, result, len);
+                free(result);
+            }
+        }
+        rv = APR_SUCCESS;
+    }
+    if (rv != APR_SUCCESS)
+        tcn_ThrowAPRException(e, rv);
+
+    return array;
+}
+
+TCN_IMPLEMENT_CALL(jstring, SSLSocket, getInfoS)(TCN_STDARGS, jlong sock,
+                                                 jint what)
+{
+    tcn_socket_t   *a = J2P(sock, tcn_socket_t *);
+    tcn_ssl_conn_t *s;
+    jstring value = NULL;
+    apr_status_t rv = APR_SUCCESS;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(sock != 0);
+
+    s = (tcn_ssl_conn_t *)(a->opaque);
+    switch (what) {
+        case SSL_INFO_SESSION_ID:
+        {
+            SSL_SESSION *session  = SSL_get_session(s->ssl);
+            if (session) {
+                char *hs = convert_to_hex(&session->session_id[0],
+                                          session->session_id_length);
+                if (hs) {
+                    value = tcn_new_string(e, hs);
+                    free(hs);
+                }
+            }
+        }
+        break;
+        case SSL_INFO_PROTOCOL:
+            value = tcn_new_string(e, SSL_get_version(s->ssl));
+        break;
+        case SSL_INFO_CIPHER:
+            value = tcn_new_string(e, SSL_get_cipher_name(s->ssl));
+        break;
+        case SSL_INFO_CIPHER_VERSION:
+            value = tcn_new_string(e, SSL_get_cipher_version(s->ssl));
+        break;
+        case SSL_INFO_CIPHER_DESCRIPTION:
+            {
+                SSL_CIPHER *cipher = SSL_get_current_cipher(s->ssl);
+                if (cipher) {
+                    char buf[256];
+                    char *desc = SSL_CIPHER_description(cipher, buf, 256);
+                    value = tcn_new_string(e, desc);
+                }
+            }
+        break;
+        default:
+            rv = APR_EINVAL;
+        break;
+    }
+    if (what & (SSL_INFO_CLIENT_S_DN | SSL_INFO_CLIENT_I_DN)) {
+        X509 *xs;
+        X509_NAME *xsname;
+        if ((xs = SSL_get_peer_certificate(s->ssl)) != NULL) {
+            char *result;
+            int idx = what & 0x0F;
+            if (what & SSL_INFO_CLIENT_S_DN)
+                xsname = X509_get_subject_name(xs);
+            else
+                xsname = X509_get_issuer_name(xs);
+            if (idx) {
+                result = lookup_ssl_cert_dn(xsname, idx);
+                if (result) {
+                    value = tcn_new_string(e, result);
+                    free(result);
+                }
+            }
+            else
+                value = tcn_new_string(e, X509_NAME_oneline(xsname, NULL, 0));
+            X509_free(xs);
+        }
+        rv = APR_SUCCESS;
+    }
+    else if (what & (SSL_INFO_SERVER_S_DN | SSL_INFO_SERVER_I_DN)) {
+        X509 *xs;
+        X509_NAME *xsname;
+        if ((xs = SSL_get_certificate(s->ssl)) != NULL) {
+            char *result;
+            int idx = what & 0x0F;
+            if (what & SSL_INFO_SERVER_S_DN)
+                xsname = X509_get_subject_name(xs);
+            else
+                xsname = X509_get_issuer_name(xs);
+            if (idx) {
+                result = lookup_ssl_cert_dn(xsname, what & 0x0F);
+                if (result) {
+                    value = tcn_new_string(e, result);
+                    free(result);
+                }
+            }
+            else
+                value = tcn_new_string(e, X509_NAME_oneline(xsname, NULL, 0));
+            /* XXX: No need to call the X509_free(xs); */
+        }
+        rv = APR_SUCCESS;
+    }
+    else if (what & SSL_INFO_CLIENT_MASK) {
+        X509 *xs;
+        char *result;
+        int nid;
+        if ((xs = SSL_get_peer_certificate(s->ssl)) != NULL) {
+            switch (what) {
+                case SSL_INFO_CLIENT_V_START:
+                    if ((result = get_cert_valid(X509_get_notBefore(xs)))) {
+                        value = tcn_new_string(e, result);
+                        free(result);
+                    }
+                break;
+                case SSL_INFO_CLIENT_V_END:
+                    if ((result = get_cert_valid(X509_get_notAfter(xs)))) {
+                        value = tcn_new_string(e, result);
+                        free(result);
+                    }
+                break;
+                case SSL_INFO_CLIENT_A_SIG:
+                    nid = OBJ_obj2nid((ASN1_OBJECT *)xs->cert_info->signature->algorithm);
+                    if (nid == NID_undef)
+                        value = tcn_new_string(e, "UNKNOWN");
+                    else
+                        value = tcn_new_string(e, OBJ_nid2ln(nid));
+                break;
+                case SSL_INFO_CLIENT_A_KEY:
+                    nid = OBJ_obj2nid((ASN1_OBJECT *)xs->cert_info->key->algor->algorithm);
+                    if (nid == NID_undef)
+                        value = tcn_new_string(e, "UNKNOWN");
+                    else
+                        value = tcn_new_string(e, OBJ_nid2ln(nid));
+                break;
+                case SSL_INFO_CLIENT_CERT:
+                    if ((result = get_cert_PEM(xs))) {
+                        value = tcn_new_string(e, result);
+                        free(result);
+                    }
+                break;
+                case SSL_INFO_CLIENT_M_SERIAL:
+                    if ((result = get_cert_serial(xs))) {
+                        value = tcn_new_string(e, result);
+                        free(result);
+                    }
+                break;
+            }
+            X509_free(xs);
+        }
+        rv = APR_SUCCESS;
+    }
+    else if (what & SSL_INFO_SERVER_MASK) {
+        X509 *xs;
+        char *result;
+        int nid;
+        if ((xs = SSL_get_certificate(s->ssl)) != NULL) {
+            switch (what) {
+                case SSL_INFO_SERVER_V_START:
+                    if ((result = get_cert_valid(X509_get_notBefore(xs)))) {
+                        value = tcn_new_string(e, result);
+                        free(result);
+                    }
+                break;
+                case SSL_INFO_SERVER_V_END:
+                    if ((result = get_cert_valid(X509_get_notAfter(xs)))) {
+                        value = tcn_new_string(e, result);
+                        free(result);
+                    }
+                break;
+                case SSL_INFO_SERVER_A_SIG:
+                    nid = OBJ_obj2nid((ASN1_OBJECT *)xs->cert_info->signature->algorithm);
+                    if (nid == NID_undef)
+                        value = tcn_new_string(e, "UNKNOWN");
+                    else
+                        value = tcn_new_string(e, OBJ_nid2ln(nid));
+                break;
+                case SSL_INFO_SERVER_A_KEY:
+                    nid = OBJ_obj2nid((ASN1_OBJECT *)xs->cert_info->key->algor->algorithm);
+                    if (nid == NID_undef)
+                        value = tcn_new_string(e, "UNKNOWN");
+                    else
+                        value = tcn_new_string(e, OBJ_nid2ln(nid));
+                break;
+                case SSL_INFO_SERVER_CERT:
+                    if ((result = get_cert_PEM(xs))) {
+                        value = tcn_new_string(e, result);
+                        free(result);
+                    }
+                break;
+                case SSL_INFO_SERVER_M_SERIAL:
+                    if ((result = get_cert_serial(xs))) {
+                        value = tcn_new_string(e, result);
+                        free(result);
+                    }
+                break;
+            }
+            /* XXX: No need to call the X509_free(xs); */
+        }
+        rv = APR_SUCCESS;
+    }
+    else if (what & SSL_INFO_CLIENT_CERT_CHAIN) {
+        X509 *xs;
+        char *result;
+        STACK_OF(X509) *sk =  SSL_get_peer_cert_chain(s->ssl);
+        int n = what & 0x0F;
+        if (n < sk_X509_num(sk)) {
+            xs = sk_X509_value(sk, n);
+            if ((result = get_cert_PEM(xs))) {
+                value = tcn_new_string(e, result);
+                free(result);
+            }
+        }
+        rv = APR_SUCCESS;
+    }
+    if (rv != APR_SUCCESS)
+        tcn_ThrowAPRException(e, rv);
+
+    return value;
+}
+
+TCN_IMPLEMENT_CALL(jint, SSLSocket, getInfoI)(TCN_STDARGS, jlong sock,
+                                              jint what)
+{
+    tcn_socket_t   *a = J2P(sock, tcn_socket_t *);
+    tcn_ssl_conn_t *s;
+    apr_status_t rv = APR_SUCCESS;
+    jint value = -1;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(sock != 0);
+
+    s = (tcn_ssl_conn_t *)(a->opaque);
+
+    switch (what) {
+        case SSL_INFO_CIPHER_USEKEYSIZE:
+        case SSL_INFO_CIPHER_ALGKEYSIZE:
+        {
+            int usekeysize = 0;
+            int algkeysize = 0;
+            SSL_CIPHER *cipher = SSL_get_current_cipher(s->ssl);
+            if (cipher) {
+                usekeysize = SSL_CIPHER_get_bits(cipher, &algkeysize);
+                if (what == SSL_INFO_CIPHER_USEKEYSIZE)
+                    value = usekeysize;
+                else
+                    value = algkeysize;
+            }
+        }
+        break;
+        case SSL_INFO_CLIENT_CERT_CHAIN:
+        {
+            STACK_OF(X509) *sk =  SSL_get_peer_cert_chain(s->ssl);
+            value = sk_X509_num(sk);
+        }
+        break;
+        default:
+            rv = APR_EINVAL;
+        break;
+    }
+    if (what & SSL_INFO_CLIENT_MASK) {
+        X509 *xs;
+        if ((xs = SSL_get_peer_certificate(s->ssl)) != NULL) {
+            switch (what) {
+                case SSL_INFO_CLIENT_V_REMAIN:
+                    value = get_days_remaining(X509_get_notAfter(xs));
+                    rv = APR_SUCCESS;
+                break;
+                default:
+                    rv = APR_EINVAL;
+                break;                    
+           }
+           X509_free(xs);
+        }
+    }
+
+    if (rv != APR_SUCCESS)
+        tcn_ThrowAPRException(e, rv);
+    return value;
+}
+
+#else
+/* OpenSSL is not supported.
+ * Create empty stubs.
+ */
+
+TCN_IMPLEMENT_CALL(jobject, SSLSocket, getInfoB)(TCN_STDARGS, jlong sock,
+                                                 jint what)
+{
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(sock);
+    UNREFERENCED(what);
+    return NULL;
+}
+
+TCN_IMPLEMENT_CALL(jstring, SSLSocket, getInfoS)(TCN_STDARGS, jlong sock,
+                                                 jint what)
+{
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(sock);
+    UNREFERENCED(what);
+    return NULL;
+}
+
+TCN_IMPLEMENT_CALL(jint, SSLSocket, getInfoI)(TCN_STDARGS, jlong sock,
+                                              jint what)
+{
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(sock);
+    UNREFERENCED(what);
+    return 0;
+}
+
+#endif
diff --git a/connectors/jni/native/src/sslnetwork.c b/connectors/jni/native/src/sslnetwork.c
new file mode 100644
index 0000000..d5ba92d
--- /dev/null
+++ b/connectors/jni/native/src/sslnetwork.c
@@ -0,0 +1,594 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** SSL network wrapper
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+#include "tcn.h"
+#include "apr_thread_mutex.h"
+#include "apr_poll.h"
+
+
+#ifdef HAVE_OPENSSL
+#include "ssl_private.h"
+
+#ifdef TCN_DO_STATISTICS
+#include "apr_atomic.h"
+
+static volatile apr_uint32_t ssl_created  = 0;
+static volatile apr_uint32_t ssl_closed   = 0;
+static volatile apr_uint32_t ssl_cleared  = 0;
+static volatile apr_uint32_t ssl_accepted = 0;
+
+void ssl_network_dump_statistics()
+{
+    fprintf(stderr, "SSL Network Statistics ..\n");
+    fprintf(stderr, "Sockets created         : %d\n", ssl_created);
+    fprintf(stderr, "Sockets accepted        : %d\n", ssl_accepted);
+    fprintf(stderr, "Sockets closed          : %d\n", ssl_closed);
+    fprintf(stderr, "Sockets cleared         : %d\n", ssl_cleared);
+}
+
+#endif
+
+static int ssl_smart_shutdown(SSL *ssl, int shutdown_type)
+{
+    int i;
+    int rc = 0;
+
+    switch (shutdown_type) {
+        case SSL_SHUTDOWN_TYPE_UNCLEAN:
+            /* perform no close notify handshake at all
+             * (violates the SSL/TLS standard!)
+             */
+            shutdown_type = SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN;
+        break;
+        case SSL_SHUTDOWN_TYPE_ACCURATE:
+            /* send close notify and wait for clients close notify
+             * (standard compliant, but usually causes connection hangs)
+             */
+            shutdown_type = 0;
+        break;
+        default:
+            /*
+             * case SSL_SHUTDOWN_TYPE_UNSET:
+             * case SSL_SHUTDOWN_TYPE_STANDARD:
+             * send close notify, but don't wait for clients close notify
+             * (standard compliant and safe, so it's the DEFAULT!)
+             */
+            shutdown_type = SSL_RECEIVED_SHUTDOWN;
+        break;
+    }
+
+    SSL_set_shutdown(ssl, shutdown_type);
+    /*
+     * Repeat the calls, because SSL_shutdown internally dispatches through a
+     * little state machine. Usually only one or two interation should be
+     * needed, so we restrict the total number of restrictions in order to
+     * avoid process hangs in case the client played bad with the socket
+     * connection and OpenSSL cannot recognize it.
+     *  max 2x pending + 2x data = 4
+     */
+    for (i = 0; i < 4; i++) {
+        if ((rc = SSL_shutdown(ssl)))
+            break;
+    }
+    return rc;
+}
+
+static apr_status_t ssl_cleanup(void *data)
+{
+    tcn_ssl_conn_t *con = (tcn_ssl_conn_t *)data;
+
+    if (con) {
+        /* Pollset was already destroyed by
+         * the pool cleanup/destroy.
+         */
+        con->pollset = NULL;
+        if (con->ssl) {
+            SSL *ssl = con->ssl;
+            con->ssl = NULL;            
+            ssl_smart_shutdown(ssl, con->shutdown_type);
+            SSL_free(ssl);
+        }
+        if (con->peer) {
+            X509_free(con->peer);
+            con->peer = NULL;
+        }
+    }
+
+#ifdef TCN_DO_STATISTICS
+    apr_atomic_inc32(&ssl_cleared);
+#endif
+    return APR_SUCCESS;
+}
+
+static tcn_ssl_conn_t *ssl_create(JNIEnv *env, tcn_ssl_ctxt_t *ctx, apr_pool_t *pool)
+{
+    tcn_ssl_conn_t *con;
+    SSL *ssl;
+
+    if ((con = apr_pcalloc(pool, sizeof(tcn_ssl_conn_t))) == NULL) {
+        tcn_ThrowAPRException(env, apr_get_os_error());
+        return NULL;
+    }
+    if ((ssl = SSL_new(ctx->ctx)) == NULL) {
+        char err[256];
+        ERR_error_string(ERR_get_error(), err);
+        tcn_Throw(env, "SSL_new failed (%s)", err);
+        con = NULL;
+        return NULL;
+    }
+    SSL_clear(ssl);
+    con->pool = pool;
+    con->ctx  = ctx;
+    con->ssl  = ssl;
+    con->shutdown_type = ctx->shutdown_type;
+    apr_pollset_create(&(con->pollset), 1, pool, 0);
+
+    SSL_set_app_data(ssl, (void *)con);
+
+    if (ctx->mode) {
+        /*
+         *  Configure callbacks for SSL connection
+         */
+        SSL_set_tmp_rsa_callback(ssl, SSL_callback_tmp_RSA);
+        SSL_set_tmp_dh_callback(ssl,  SSL_callback_tmp_DH);
+        SSL_set_session_id_context(ssl, &(ctx->context_id[0]),
+                                   MD5_DIGEST_LENGTH);
+    }
+    SSL_set_verify_result(ssl, X509_V_OK);
+    SSL_rand_seed(ctx->rand_file);
+
+#ifdef TCN_DO_STATISTICS
+    ssl_created++;
+#endif
+    return con;
+}
+
+#ifdef WIN32
+#define APR_INVALID_SOCKET  INVALID_SOCKET
+#else
+#define APR_INVALID_SOCKET  -1
+#endif
+
+static apr_status_t wait_for_io_or_timeout(tcn_ssl_conn_t *con,
+                                           int for_what)
+{
+    apr_interval_time_t timeout;
+    apr_pollfd_t pfd;
+    int type;
+    apr_status_t status;
+    apr_os_sock_t sock;
+
+    if (!con->pollset)
+        return APR_ENOPOLL;    
+    if (!con->sock)
+        return APR_ENOTSOCK;        
+    
+    /* Check if the socket was already closed
+     */    
+    apr_os_sock_get(&sock, con->sock);    
+    if (sock == APR_INVALID_SOCKET)
+        return APR_ENOTSOCK;        
+
+    /* Figure out the the poll direction */
+    switch (for_what) {
+        case SSL_ERROR_WANT_WRITE:
+        case SSL_ERROR_WANT_CONNECT:
+        case SSL_ERROR_WANT_ACCEPT:
+            type = APR_POLLOUT;
+        break;
+        case SSL_ERROR_WANT_READ:
+            type = APR_POLLIN;
+        break;
+        default:
+            return APR_EINVAL;
+        break;
+    }
+
+    apr_socket_timeout_get(con->sock, &timeout);
+    pfd.desc_type = APR_POLL_SOCKET;
+    pfd.desc.s    = con->sock;
+    pfd.reqevents = type;
+
+    /* Remove the object if it was in the pollset, then add in the new
+     * object with the correct reqevents value. Ignore the status result
+     * on the remove, because it might not be in there (yet).
+     */
+    apr_pollset_remove(con->pollset, &pfd);
+
+    /* ### check status code */
+    apr_pollset_add(con->pollset, &pfd);
+
+    do {
+        int numdesc;
+        const apr_pollfd_t *pdesc;
+
+        status = apr_pollset_poll(con->pollset, timeout, &numdesc, &pdesc);
+        if (numdesc == 1 && (pdesc[0].rtnevents & type) != 0)
+            return APR_SUCCESS;
+    } while (APR_STATUS_IS_EINTR(status));
+
+    return status;
+}
+
+static apr_status_t APR_THREAD_FUNC
+ssl_socket_timeout_set(apr_socket_t *sock, apr_interval_time_t t)
+{
+    tcn_ssl_conn_t *con = (tcn_ssl_conn_t *)sock;
+    return apr_socket_timeout_set(con->sock, t);
+}
+
+static apr_status_t APR_THREAD_FUNC
+ssl_socket_timeout_get(apr_socket_t *sock, apr_interval_time_t *t)
+{
+    tcn_ssl_conn_t *con = (tcn_ssl_conn_t *)sock;
+    return apr_socket_timeout_get(con->sock, t);
+}
+
+static APR_INLINE apr_status_t APR_THREAD_FUNC
+ssl_socket_opt_set(apr_socket_t *sock, apr_int32_t opt, apr_int32_t on)
+{
+    tcn_ssl_conn_t *con = (tcn_ssl_conn_t *)sock;
+    return apr_socket_opt_set(con->sock, opt, on);
+}
+
+static APR_INLINE apr_status_t APR_THREAD_FUNC
+ssl_socket_opt_get(apr_socket_t *sock, apr_int32_t opt, apr_int32_t *on)
+{
+    tcn_ssl_conn_t *con = (tcn_ssl_conn_t *)sock;
+    return apr_socket_opt_get(con->sock, opt, on);
+}
+
+static apr_status_t APR_THREAD_FUNC
+ssl_socket_shutdown(apr_socket_t *sock, apr_shutdown_how_e how)
+{
+    apr_status_t rv = APR_SUCCESS;
+    tcn_ssl_conn_t *con = (tcn_ssl_conn_t *)sock;
+
+    if (con->ssl) {
+        SSL *ssl = con->ssl;
+        con->ssl = NULL;
+        if (how < 1)
+            how = con->shutdown_type;
+        rv = ssl_smart_shutdown(ssl, how);
+        /* TODO: Translate OpenSSL Error codes */
+        SSL_free(ssl);
+    }
+    return rv;
+}
+
+static apr_status_t APR_THREAD_FUNC
+ssl_socket_close(apr_socket_t *sock)
+{
+    tcn_ssl_conn_t *con = (tcn_ssl_conn_t *)sock;
+    apr_status_t rv = APR_SUCCESS;
+
+#ifdef TCN_DO_STATISTICS
+    apr_atomic_inc32(&ssl_closed);
+#endif
+    if (con->ssl) {
+        SSL *ssl = con->ssl;
+        con->ssl = NULL;
+        rv = ssl_smart_shutdown(ssl, con->shutdown_type);
+        SSL_free(ssl);
+    }
+    if (con->peer) {
+        X509_free(con->peer);
+        con->peer = NULL;
+    }
+    return rv;
+}
+
+TCN_IMPLEMENT_CALL(jint, SSLSocket, handshake)(TCN_STDARGS, jlong sock)
+{
+    tcn_socket_t *ss = J2P(sock, tcn_socket_t *);
+    tcn_ssl_conn_t *con;
+    int s, i;
+    apr_status_t rv;
+    X509 *peer;
+
+    UNREFERENCED_STDARGS;
+    TCN_ASSERT(sock != 0);
+    if (ss->net->type != TCN_SOCKET_SSL)
+        return APR_EINVAL;
+    con = (tcn_ssl_conn_t *)ss->opaque;
+    while (!SSL_is_init_finished(con->ssl)) {
+        if ((s = SSL_do_handshake(con->ssl)) <= 0) {
+            apr_status_t os = apr_get_netos_error();
+            if (!con->ssl)
+                return os == APR_SUCCESS ? APR_ENOTSOCK : os;
+            i = SSL_get_error(con->ssl, s);
+            switch (i) {
+                case SSL_ERROR_NONE:
+                    con->shutdown_type = SSL_SHUTDOWN_TYPE_STANDARD;
+                    return APR_SUCCESS;
+                break;
+                case SSL_ERROR_WANT_READ:
+                case SSL_ERROR_WANT_WRITE:
+                    if ((rv = wait_for_io_or_timeout(con, i)) != APR_SUCCESS) {
+                        con->shutdown_type = SSL_SHUTDOWN_TYPE_UNCLEAN;
+                        return rv;
+                    }
+                break;
+                case SSL_ERROR_SYSCALL:
+                case SSL_ERROR_SSL:
+                    if (!APR_STATUS_IS_EAGAIN(os) &&
+                        !APR_STATUS_IS_EINTR(os)) {
+                        con->shutdown_type = SSL_SHUTDOWN_TYPE_UNCLEAN;
+                        return os;
+                    }
+                break;
+                default:
+                    /*
+                    * Anything else is a fatal error
+                    */
+                    con->shutdown_type = SSL_SHUTDOWN_TYPE_UNCLEAN;
+                    return SSL_TO_APR_ERROR(i);
+                break;
+            }
+        }
+        if (!con->ssl)
+            return APR_ENOTSOCK;
+        
+        /*
+        * Check for failed client authentication
+        */
+        if (SSL_get_verify_result(con->ssl) != X509_V_OK) {
+            /* TODO: Log SSL client authentication failed */
+            con->shutdown_type = SSL_SHUTDOWN_TYPE_UNCLEAN;
+            /* TODO: Figure out the correct return value */
+            return APR_EGENERAL;
+        }
+
+        /*
+         * Remember the peer certificate
+         */
+        if ((peer = SSL_get_peer_certificate(con->ssl)) != NULL) {
+            if (con->peer)
+                X509_free(con->peer);
+            con->peer = peer;
+        }
+    }
+    return APR_SUCCESS;
+}
+
+static apr_status_t APR_THREAD_FUNC
+ssl_socket_recv(apr_socket_t *sock, char *buf, apr_size_t *len)
+{
+    tcn_ssl_conn_t *con = (tcn_ssl_conn_t *)sock;
+    int s, i, wr = (int)(*len);
+    apr_status_t rv = APR_SUCCESS;
+
+    for (;;) {
+        if ((s = SSL_read(con->ssl, buf, wr)) <= 0) {
+            apr_status_t os = apr_get_netos_error();
+            if (!con->ssl)
+                return os == APR_SUCCESS ? APR_ENOTSOCK : os;
+            
+            i = SSL_get_error(con->ssl, s);
+            /* Special case if the "close notify" alert send by peer */
+            if (s == 0 && (con->ssl->shutdown & SSL_RECEIVED_SHUTDOWN)) {
+                *len = 0;
+                return APR_EOF;
+            }
+            switch (i) {
+                case SSL_ERROR_ZERO_RETURN:
+                    *len = 0;
+                    con->shutdown_type = SSL_SHUTDOWN_TYPE_STANDARD;
+                    return APR_EOF;
+                break;
+                case SSL_ERROR_WANT_READ:
+                case SSL_ERROR_WANT_WRITE:
+                    if ((rv = wait_for_io_or_timeout(con, i)) != APR_SUCCESS) {
+                        con->shutdown_type = SSL_SHUTDOWN_TYPE_UNCLEAN;
+                        return rv;
+                    }
+                break;
+                case SSL_ERROR_SYSCALL:
+                case SSL_ERROR_SSL:
+                    if (!APR_STATUS_IS_EAGAIN(os) &&
+                        !APR_STATUS_IS_EINTR(os)) {
+                        con->shutdown_type = SSL_SHUTDOWN_TYPE_UNCLEAN;
+                        return os;
+                    }
+                break;
+                default:
+                    con->shutdown_type = SSL_SHUTDOWN_TYPE_UNCLEAN;
+                    return os;
+                break;
+            }
+        }
+        else {
+            *len = s;
+            con->shutdown_type = SSL_SHUTDOWN_TYPE_STANDARD;
+            break;
+        }
+    }
+    return rv;
+}
+
+static apr_status_t APR_THREAD_FUNC
+ssl_socket_send(apr_socket_t *sock, const char *buf,
+                apr_size_t *len)
+{
+    tcn_ssl_conn_t *con = (tcn_ssl_conn_t *)sock;
+    int s, i, wr = (int)(*len);
+    apr_status_t rv = APR_SUCCESS;
+
+    for (;;) {
+        if ((s = SSL_write(con->ssl, buf, wr)) <= 0) {
+            apr_status_t os = apr_get_netos_error();
+            if (!con->ssl)
+                return os == APR_SUCCESS ? APR_ENOTSOCK : os;
+            
+            i = SSL_get_error(con->ssl, s);
+            switch (i) {
+                case SSL_ERROR_ZERO_RETURN:
+                    *len = 0;
+                    con->shutdown_type = SSL_SHUTDOWN_TYPE_STANDARD;
+                    return APR_EOF;
+                break;
+                case SSL_ERROR_WANT_READ:
+                case SSL_ERROR_WANT_WRITE:
+                    if ((rv = wait_for_io_or_timeout(con, i)) != APR_SUCCESS) {
+                        con->shutdown_type = SSL_SHUTDOWN_TYPE_UNCLEAN;
+                        return rv;
+                    }
+                break;
+                case SSL_ERROR_SYSCALL:
+                case SSL_ERROR_SSL:
+                    if (!APR_STATUS_IS_EAGAIN(os) &&
+                        !APR_STATUS_IS_EINTR(os)) {
+                        con->shutdown_type = SSL_SHUTDOWN_TYPE_UNCLEAN;
+                        return os;
+                    }
+                break;
+                default:
+                    con->shutdown_type = SSL_SHUTDOWN_TYPE_UNCLEAN;
+                    return os;
+                break;
+            }
+        }
+        else {
+            *len = s;
+            break;
+        }
+    }
+    return rv;
+}
+
+static apr_status_t APR_THREAD_FUNC
+ssl_socket_sendv(apr_socket_t *sock,
+                 const struct iovec *vec,
+                 apr_int32_t nvec, apr_size_t *len)
+{
+    tcn_ssl_conn_t *con = (tcn_ssl_conn_t *)sock;
+    apr_status_t rv;
+    apr_size_t written = 0;
+    apr_int32_t i;
+
+    for (i = 0; i < nvec; i++) {
+        apr_size_t rd = vec[i].iov_len;
+        if ((rv = ssl_socket_send((apr_socket_t *)con,
+                                  vec[i].iov_base, &rd)) != APR_SUCCESS) {
+            *len = written;
+            return rv;
+        }
+        written += rd;
+    }
+    *len = written;
+    return APR_SUCCESS;
+}
+
+static tcn_nlayer_t ssl_socket_layer = {
+    TCN_SOCKET_SSL,
+    ssl_cleanup,
+    ssl_socket_close,
+    ssl_socket_shutdown,
+    ssl_socket_opt_get,
+    ssl_socket_opt_set,
+    ssl_socket_timeout_get,
+    ssl_socket_timeout_set,
+    ssl_socket_send,
+    ssl_socket_sendv,
+    ssl_socket_recv
+};
+
+
+TCN_IMPLEMENT_CALL(jint, SSLSocket, attach)(TCN_STDARGS, jlong ctx,
+                                            jlong sock)
+{
+    tcn_ssl_ctxt_t *c = J2P(ctx, tcn_ssl_ctxt_t *);
+    tcn_socket_t *s   = J2P(sock, tcn_socket_t *);
+    tcn_ssl_conn_t *con;
+    apr_os_sock_t  oss;
+    apr_status_t rv;
+
+    UNREFERENCED(o);
+    TCN_ASSERT(ctx != 0);
+    TCN_ASSERT(sock != 0);
+
+    if (!s->sock)
+        return APR_ENOTSOCK;
+
+    if ((rv = apr_os_sock_get(&oss, s->sock)) != APR_SUCCESS)
+        return rv;
+    if (oss == APR_INVALID_SOCKET)
+        return APR_ENOTSOCK;        
+        
+    if ((con = ssl_create(e, c, s->pool)) == NULL)
+        return APR_EGENERAL;
+    con->sock = s->sock;
+
+    SSL_set_fd(con->ssl, (int)oss);
+    if (c->mode)
+        SSL_set_accept_state(con->ssl);
+    else
+        SSL_set_connect_state(con->ssl);
+    /* Change socket type */
+    s->net    = &ssl_socket_layer;
+    s->opaque = con;
+
+    return APR_SUCCESS;
+}
+
+TCN_IMPLEMENT_CALL(jint, SSLSocket, renegotiate)(TCN_STDARGS,
+                                                 jlong sock)
+{
+    tcn_socket_t *s   = J2P(sock, tcn_socket_t *);
+    tcn_ssl_conn_t *con;
+
+    UNREFERENCED_STDARGS;
+    TCN_ASSERT(sock != 0);
+    con = (tcn_ssl_conn_t *)s->opaque;
+    return SSL_renegotiate(con->ssl);
+}
+
+#else
+/* OpenSSL is not supported.
+ * Create empty stubs.
+ */
+
+TCN_IMPLEMENT_CALL(jint, SSLSocket, handshake)(TCN_STDARGS, jlong sock)
+{
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(sock);
+    return (jint)APR_ENOTIMPL;
+}
+
+TCN_IMPLEMENT_CALL(jint, SSLSocket, attach)(TCN_STDARGS, jlong ctx,
+                                            jlong sock)
+{
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(ctx);
+    UNREFERENCED(sock);
+    return (jint)APR_ENOTIMPL;
+}
+
+TCN_IMPLEMENT_CALL(jint, SSLSocket, renegotiate)(TCN_STDARGS,
+                                                 jlong sock)
+{
+    UNREFERENCED_STDARGS;
+    UNREFERENCED(sock);
+    return (jint)APR_ENOTIMPL;
+}
+
+#endif
diff --git a/connectors/jni/native/src/sslutils.c b/connectors/jni/native/src/sslutils.c
new file mode 100644
index 0000000..1881a49
--- /dev/null
+++ b/connectors/jni/native/src/sslutils.c
@@ -0,0 +1,675 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** SSL Utilities
+ *
+ * @author Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+#include "tcn.h"
+#include "apr_thread_mutex.h"
+#include "apr_poll.h"
+
+#ifdef HAVE_OPENSSL
+#include "ssl_private.h"
+
+#ifdef WIN32
+extern int WIN32_SSL_password_prompt(tcn_pass_cb_t *data);
+#endif
+
+/*  _________________________________________________________________
+**
+**  Additional High-Level Functions for OpenSSL
+**  _________________________________________________________________
+*/
+
+/* we initialize this index at startup time
+ * and never write to it at request time,
+ * so this static is thread safe.
+ * also note that OpenSSL increments at static variable when
+ * SSL_get_ex_new_index() is called, so we _must_ do this at startup.
+ */
+static int SSL_app_data2_idx = -1;
+
+void SSL_init_app_data2_idx(void)
+{
+    int i;
+
+    if (SSL_app_data2_idx > -1) {
+        return;
+    }
+
+    /* we _do_ need to call this twice */
+    for (i = 0; i <= 1; i++) {
+        SSL_app_data2_idx =
+            SSL_get_ex_new_index(0,
+                                 "Second Application Data for SSL",
+                                 NULL, NULL, NULL);
+    }
+}
+
+void *SSL_get_app_data2(SSL *ssl)
+{
+    return (void *)SSL_get_ex_data(ssl, SSL_app_data2_idx);
+}
+
+void SSL_set_app_data2(SSL *ssl, void *arg)
+{
+    SSL_set_ex_data(ssl, SSL_app_data2_idx, (char *)arg);
+    return;
+}
+
+/* Simple echo password prompting */
+int SSL_password_prompt(tcn_pass_cb_t *data)
+{
+    int rv = 0;
+    data->password[0] = '\0';
+    if (data->cb.obj) {
+        JNIEnv *e;
+        jobject  o;
+        jstring  prompt;
+        tcn_get_java_env(&e);
+        prompt = AJP_TO_JSTRING(data->prompt);
+        if ((o = (*e)->CallObjectMethod(e, data->cb.obj,
+                            data->cb.mid[0], prompt))) {
+            TCN_ALLOC_CSTRING(o);
+            if (J2S(o)) {
+                strncpy(data->password, J2S(o), SSL_MAX_PASSWORD_LEN);
+                data->password[SSL_MAX_PASSWORD_LEN-1] = '\0';
+                rv = (int)strlen(data->password);
+            }
+            TCN_FREE_CSTRING(o);
+        }
+    }
+    else {
+#ifdef WIN32
+        rv = WIN32_SSL_password_prompt(data);
+#else
+        EVP_read_pw_string(data->password, SSL_MAX_PASSWORD_LEN,
+                           data->prompt, 0);
+#endif
+        rv = (int)strlen(data->password);
+    }
+    if (rv > 0) {
+        /* Remove LF char if present */
+        char *r = strchr(data->password, '\n');
+        if (r) {
+            *r = '\0';
+            rv--;
+        }
+#ifdef WIN32
+        if ((r = strchr(data->password, '\r'))) {
+            *r = '\0';
+            rv--;
+        }
+#endif
+    }
+    return rv;
+}
+
+int SSL_password_callback(char *buf, int bufsiz, int verify,
+                          void *cb)
+{
+    tcn_pass_cb_t *cb_data = (tcn_pass_cb_t *)cb;
+
+    if (buf == NULL)
+        return 0;
+    *buf = '\0';
+    if (cb_data == NULL)
+        cb_data = &tcn_password_callback;
+    if (!cb_data->prompt)
+        cb_data->prompt = SSL_DEFAULT_PASS_PROMPT;
+    if (cb_data->password[0]) {
+        /* Return already obtained password */
+        strncpy(buf, cb_data->password, bufsiz);
+        buf[bufsiz - 1] = '\0';
+        return (int)strlen(buf);
+    }
+    else {
+        if (SSL_password_prompt(cb_data) > 0)
+            strncpy(buf, cb_data->password, bufsiz);
+    }
+    buf[bufsiz - 1] = '\0';
+    return (int)strlen(buf);
+}
+
+static unsigned char dh0512_p[]={
+    0xD9,0xBA,0xBF,0xFD,0x69,0x38,0xC9,0x51,0x2D,0x19,0x37,0x39,
+    0xD7,0x7D,0x7E,0x3E,0x25,0x58,0x55,0x94,0x90,0x60,0x93,0x7A,
+    0xF2,0xD5,0x61,0x5F,0x06,0xE8,0x08,0xB4,0x57,0xF4,0xCF,0xB4,
+    0x41,0xCC,0xC4,0xAC,0xD4,0xF0,0x45,0x88,0xC9,0xD1,0x21,0x4C,
+    0xB6,0x72,0x48,0xBD,0x73,0x80,0xE0,0xDD,0x88,0x41,0xA0,0xF1,
+    0xEA,0x4B,0x71,0x13
+};
+static unsigned char dh1024_p[]={
+    0xA2,0x95,0x7E,0x7C,0xA9,0xD5,0x55,0x1D,0x7C,0x77,0x11,0xAC,
+    0xFD,0x48,0x8C,0x3B,0x94,0x1B,0xC5,0xC0,0x99,0x93,0xB5,0xDC,
+    0xDC,0x06,0x76,0x9E,0xED,0x1E,0x3D,0xBB,0x9A,0x29,0xD6,0x8B,
+    0x1F,0xF6,0xDA,0xC9,0xDF,0xD5,0x02,0x4F,0x09,0xDE,0xEC,0x2C,
+    0x59,0x1E,0x82,0x32,0x80,0x9B,0xED,0x51,0x68,0xD2,0xFB,0x1E,
+    0x25,0xDB,0xDF,0x9C,0x11,0x70,0xDF,0xCA,0x19,0x03,0x3D,0x3D,
+    0xC1,0xAC,0x28,0x88,0x4F,0x13,0xAF,0x16,0x60,0x6B,0x5B,0x2F,
+    0x56,0xC7,0x5B,0x5D,0xDE,0x8F,0x50,0x08,0xEC,0xB1,0xB9,0x29,
+    0xAA,0x54,0xF4,0x05,0xC9,0xDF,0x95,0x9D,0x79,0xC6,0xEA,0x3F,
+    0xC9,0x70,0x42,0xDA,0x90,0xC7,0xCC,0x12,0xB9,0x87,0x86,0x39,
+    0x1E,0x1A,0xCE,0xF7,0x3F,0x15,0xB5,0x2B
+};
+static unsigned char dh2048_p[]={
+    0xF2,0x4A,0xFC,0x7E,0x73,0x48,0x21,0x03,0xD1,0x1D,0xA8,0x16,
+    0x87,0xD0,0xD2,0xDC,0x42,0xA8,0xD2,0x73,0xE3,0xA9,0x21,0x31,
+    0x70,0x5D,0x69,0xC7,0x8F,0x95,0x0C,0x9F,0xB8,0x0E,0x37,0xAE,
+    0xD1,0x6F,0x36,0x1C,0x26,0x63,0x2A,0x36,0xBA,0x0D,0x2A,0xF5,
+    0x1A,0x0F,0xE8,0xC0,0xEA,0xD1,0xB5,0x52,0x47,0x1F,0x9A,0x0C,
+    0x0F,0xED,0x71,0x51,0xED,0xE6,0x62,0xD5,0xF8,0x81,0x93,0x55,
+    0xC1,0x0F,0xB4,0x72,0x64,0xB3,0x73,0xAA,0x90,0x9A,0x81,0xCE,
+    0x03,0xFD,0x6D,0xB1,0x27,0x7D,0xE9,0x90,0x5E,0xE2,0x10,0x74,
+    0x4F,0x94,0xC3,0x05,0x21,0x73,0xA9,0x12,0x06,0x9B,0x0E,0x20,
+    0xD1,0x5F,0xF7,0xC9,0x4C,0x9D,0x4F,0xFA,0xCA,0x4D,0xFD,0xFF,
+    0x6A,0x62,0x9F,0xF0,0x0F,0x3B,0xA9,0x1D,0xF2,0x69,0x29,0x00,
+    0xBD,0xE9,0xB0,0x9D,0x88,0xC7,0x4A,0xAE,0xB0,0x53,0xAC,0xA2,
+    0x27,0x40,0x88,0x58,0x8F,0x26,0xB2,0xC2,0x34,0x7D,0xA2,0xCF,
+    0x92,0x60,0x9B,0x35,0xF6,0xF3,0x3B,0xC3,0xAA,0xD8,0x58,0x9C,
+    0xCF,0x5D,0x9F,0xDB,0x14,0x93,0xFA,0xA3,0xFA,0x44,0xB1,0xB2,
+    0x4B,0x0F,0x08,0x70,0x44,0x71,0x3A,0x73,0x45,0x8E,0x6D,0x9C,
+    0x56,0xBC,0x9A,0xB5,0xB1,0x3D,0x8B,0x1F,0x1E,0x2B,0x0E,0x93,
+    0xC2,0x9B,0x84,0xE2,0xE8,0xFC,0x29,0x85,0x83,0x8D,0x2E,0x5C,
+    0xDD,0x9A,0xBB,0xFD,0xF0,0x87,0xBF,0xAF,0xC4,0xB6,0x1D,0xE7,
+    0xF9,0x46,0x50,0x7F,0xC3,0xAC,0xFD,0xC9,0x8C,0x9D,0x66,0x6B,
+    0x4C,0x6A,0xC9,0x3F,0x0C,0x0A,0x74,0x94,0x41,0x85,0x26,0x8F,
+    0x9F,0xF0,0x7C,0x0B
+};
+static unsigned char dh4096_p[] = {
+    0x8D,0xD3,0x8F,0x77,0x6F,0x6F,0xB0,0x74,0x3F,0x22,0xE9,0xD1,
+    0x17,0x15,0x69,0xD8,0x24,0x85,0xCD,0xC4,0xE4,0x0E,0xF6,0x52,
+    0x40,0xF7,0x1C,0x34,0xD0,0xA5,0x20,0x77,0xE2,0xFC,0x7D,0xA1,
+    0x82,0xF1,0xF3,0x78,0x95,0x05,0x5B,0xB8,0xDB,0xB3,0xE4,0x17,
+    0x93,0xD6,0x68,0xA7,0x0A,0x0C,0xC5,0xBB,0x9C,0x5E,0x1E,0x83,
+    0x72,0xB3,0x12,0x81,0xA2,0xF5,0xCD,0x44,0x67,0xAA,0xE8,0xAD,
+    0x1E,0x8F,0x26,0x25,0xF2,0x8A,0xA0,0xA5,0xF4,0xFB,0x95,0xAE,
+    0x06,0x50,0x4B,0xD0,0xE7,0x0C,0x55,0x88,0xAA,0xE6,0xB8,0xF6,
+    0xE9,0x2F,0x8D,0xA7,0xAD,0x84,0xBC,0x8D,0x4C,0xFE,0x76,0x60,
+    0xCD,0xC8,0xED,0x7C,0xBF,0xF3,0xC1,0xF8,0x6A,0xED,0xEC,0xE9,
+    0x13,0x7D,0x4E,0x72,0x20,0x77,0x06,0xA4,0x12,0xF8,0xD2,0x34,
+    0x6F,0xDC,0x97,0xAB,0xD3,0xA0,0x45,0x8E,0x7D,0x21,0xA9,0x35,
+    0x6E,0xE4,0xC9,0xC4,0x53,0xFF,0xE5,0xD9,0x72,0x61,0xC4,0x8A,
+    0x75,0x78,0x36,0x97,0x1A,0xAB,0x92,0x85,0x74,0x61,0x7B,0xE0,
+    0x92,0xB8,0xC6,0x12,0xA1,0x72,0xBB,0x5B,0x61,0xAA,0xE6,0x2C,
+    0x2D,0x9F,0x45,0x79,0x9E,0xF4,0x41,0x93,0x93,0xEF,0x8B,0xEF,
+    0xB7,0xBF,0x6D,0xF0,0x91,0x11,0x4F,0x7C,0x71,0x84,0xB5,0x88,
+    0xA3,0x8C,0x1A,0xD5,0xD0,0x81,0x9C,0x50,0xAC,0xA9,0x2B,0xE9,
+    0x92,0x2D,0x73,0x7C,0x0A,0xA3,0xFA,0xD3,0x6C,0x91,0x43,0xA6,
+    0x80,0x7F,0xD7,0xC4,0xD8,0x6F,0x85,0xF8,0x15,0xFD,0x08,0xA6,
+    0xF8,0x7B,0x3A,0xF4,0xD3,0x50,0xB4,0x2F,0x75,0xC8,0x48,0xB8,
+    0xA8,0xFD,0xCA,0x8F,0x62,0xF1,0x4C,0x89,0xB7,0x18,0x67,0xB2,
+    0x93,0x2C,0xC4,0xD4,0x71,0x29,0xA9,0x26,0x20,0xED,0x65,0x37,
+    0x06,0x87,0xFC,0xFB,0x65,0x02,0x1B,0x3C,0x52,0x03,0xA1,0xBB,
+    0xCF,0xE7,0x1B,0xA4,0x1A,0xE3,0x94,0x97,0x66,0x06,0xBF,0xA9,
+    0xCE,0x1B,0x07,0x10,0xBA,0xF8,0xD4,0xD4,0x05,0xCF,0x53,0x47,
+    0x16,0x2C,0xA1,0xFC,0x6B,0xEF,0xF8,0x6C,0x23,0x34,0xEF,0xB7,
+    0xD3,0x3F,0xC2,0x42,0x5C,0x53,0x9A,0x00,0x52,0xCF,0xAC,0x42,
+    0xD3,0x3B,0x2E,0xB6,0x04,0x32,0xE1,0x09,0xED,0x64,0xCD,0x6A,
+    0x63,0x58,0xB8,0x43,0x56,0x5A,0xBE,0xA4,0x9F,0x68,0xD4,0xF7,
+    0xC9,0x04,0xDF,0xCD,0xE5,0x93,0xB0,0x2F,0x06,0x19,0x3E,0xB8,
+    0xAB,0x7E,0xF8,0xE7,0xE7,0xC8,0x53,0xA2,0x06,0xC3,0xC7,0xF9,
+    0x18,0x3B,0x51,0xC3,0x9B,0xFF,0x8F,0x00,0x0E,0x87,0x19,0x68,
+    0x2F,0x40,0xC0,0x68,0xFA,0x12,0xAE,0x57,0xB5,0xF0,0x97,0xCA,
+    0x78,0x23,0x31,0xAB,0x67,0x7B,0x10,0x6B,0x59,0x32,0x9C,0x64,
+    0x20,0x38,0x1F,0xC5,0x07,0x84,0x9E,0xC4,0x49,0xB1,0xDF,0xED,
+    0x7A,0x8A,0xC3,0xE0,0xDD,0x30,0x55,0xFF,0x95,0x45,0xA6,0xEE,
+    0xCB,0xE4,0x26,0xB9,0x8E,0x89,0x37,0x63,0xD4,0x02,0x3D,0x5B,
+    0x4F,0xE5,0x90,0xF6,0x72,0xF8,0x10,0xEE,0x31,0x04,0x54,0x17,
+    0xE3,0xD5,0x63,0x84,0x80,0x62,0x54,0x46,0x85,0x6C,0xD2,0xC1,
+    0x3E,0x19,0xBD,0xE2,0x80,0x11,0x86,0xC7,0x4B,0x7F,0x67,0x86,
+    0x47,0xD2,0x38,0xCD,0x8F,0xFE,0x65,0x3C,0x11,0xCD,0x96,0x99,
+    0x4E,0x45,0xEB,0xEC,0x1D,0x94,0x8C,0x53,
+};
+static unsigned char dhxxx2_g[]={
+    0x02
+};
+
+static DH *get_dh(int idx)
+{
+    DH *dh;
+
+    if ((dh = DH_new()) == NULL)
+        return NULL;
+    switch (idx) {
+        case SSL_TMP_KEY_DH_512:
+            dh->p = BN_bin2bn(dh0512_p, sizeof(dh0512_p), NULL);
+        break;
+        case SSL_TMP_KEY_DH_1024:
+            dh->p = BN_bin2bn(dh1024_p, sizeof(dh1024_p), NULL);
+        break;
+        case SSL_TMP_KEY_DH_2048:
+            dh->p = BN_bin2bn(dh2048_p, sizeof(dh2048_p), NULL);
+        break;
+        case SSL_TMP_KEY_DH_4096:
+            dh->p = BN_bin2bn(dh4096_p, sizeof(dh2048_p), NULL);
+        break;
+    }
+    dh->g = BN_bin2bn(dhxxx2_g, sizeof(dhxxx2_g), NULL);
+    if ((dh->p == NULL) || (dh->g == NULL)) {
+        DH_free(dh);
+        return NULL;
+    }
+    else
+        return dh;
+}
+
+DH *SSL_dh_get_tmp_param(int key_len)
+{
+    DH *dh;
+
+    if (key_len == 512)
+        dh = get_dh(SSL_TMP_KEY_DH_512);
+    else if (key_len == 1024)
+        dh = get_dh(SSL_TMP_KEY_DH_1024);
+    else if (key_len == 2048)
+        dh = get_dh(SSL_TMP_KEY_DH_2048);
+    else if (key_len == 4096)
+        dh = get_dh(SSL_TMP_KEY_DH_4096);
+    else
+        dh = get_dh(SSL_TMP_KEY_DH_1024);
+    return dh;
+}
+
+DH *SSL_dh_get_param_from_file(const char *file)
+{
+    DH *dh = NULL;
+    BIO *bio;
+
+    if ((bio = BIO_new_file(file, "r")) == NULL)
+        return NULL;
+    dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
+    BIO_free(bio);
+    return dh;
+}
+
+/*
+ * Handle out temporary RSA private keys on demand
+ *
+ * The background of this as the TLSv1 standard explains it:
+ *
+ * | D.1. Temporary RSA keys
+ * |
+ * |    US Export restrictions limit RSA keys used for encryption to 512
+ * |    bits, but do not place any limit on lengths of RSA keys used for
+ * |    signing operations. Certificates often need to be larger than 512
+ * |    bits, since 512-bit RSA keys are not secure enough for high-value
+ * |    transactions or for applications requiring long-term security. Some
+ * |    certificates are also designated signing-only, in which case they
+ * |    cannot be used for key exchange.
+ * |
+ * |    When the public key in the certificate cannot be used for encryption,
+ * |    the server signs a temporary RSA key, which is then exchanged. In
+ * |    exportable applications, the temporary RSA key should be the maximum
+ * |    allowable length (i.e., 512 bits). Because 512-bit RSA keys are
+ * |    relatively insecure, they should be changed often. For typical
+ * |    electronic commerce applications, it is suggested that keys be
+ * |    changed daily or every 500 transactions, and more often if possible.
+ * |    Note that while it is acceptable to use the same temporary key for
+ * |    multiple transactions, it must be signed each time it is used.
+ * |
+ * |    RSA key generation is a time-consuming process. In many cases, a
+ * |    low-priority process can be assigned the task of key generation.
+ * |    Whenever a new key is completed, the existing temporary key can be
+ * |    replaced with the new one.
+ *
+ * XXX: base on comment above, if thread support is enabled,
+ * we should spawn a low-priority thread to generate new keys
+ * on the fly.
+ *
+ * So we generated 512 and 1024 bit temporary keys on startup
+ * which we now just hand out on demand....
+ */
+
+RSA *SSL_callback_tmp_RSA(SSL *ssl, int export, int keylen)
+{
+    int idx;
+
+    /* doesn't matter if export flag is on,
+     * we won't be asked for keylen > 512 in that case.
+     * if we are asked for a keylen > 1024, it is too expensive
+     * to generate on the fly.
+     */
+
+    switch (keylen) {
+        case 512:
+            idx = SSL_TMP_KEY_RSA_512;
+        break;
+        case 2048:
+            idx = SSL_TMP_KEY_RSA_2048;
+            if (SSL_temp_keys[idx] == NULL)
+                idx = SSL_TMP_KEY_RSA_1024;
+        break;
+        case 4096:
+            idx = SSL_TMP_KEY_RSA_4096;
+            if (SSL_temp_keys[idx] == NULL)
+                idx = SSL_TMP_KEY_RSA_2048;
+        break;
+        case 1024:
+        default:
+            idx = SSL_TMP_KEY_RSA_1024;
+        break;
+    }
+    return (RSA *)SSL_temp_keys[idx];
+}
+
+/*
+ * Hand out the already generated DH parameters...
+ */
+DH *SSL_callback_tmp_DH(SSL *ssl, int export, int keylen)
+{
+    int idx;
+    switch (keylen) {
+        case 512:
+            idx = SSL_TMP_KEY_DH_512;
+        break;
+        case 2048:
+            idx = SSL_TMP_KEY_DH_2048;
+        break;
+        case 4096:
+            idx = SSL_TMP_KEY_DH_4096;
+        break;
+        case 1024:
+        default:
+            idx = SSL_TMP_KEY_DH_1024;
+        break;
+    }
+    return (DH *)SSL_temp_keys[idx];
+}
+
+void SSL_vhost_algo_id(const unsigned char *vhost_id, unsigned char *md, int algo)
+{
+    MD5_CTX c;
+    MD5_Init(&c);
+    MD5_Update(&c, vhost_id, MD5_DIGEST_LENGTH);
+    switch (algo) {
+        case SSL_ALGO_UNKNOWN:
+            MD5_Update(&c, "UNKNOWN", 7);
+        break;
+        case SSL_ALGO_RSA:
+            MD5_Update(&c, "RSA", 3);
+        break;
+        case SSL_ALGO_DSA:
+            MD5_Update(&c, "DSA", 3);
+        break;
+    }
+    MD5_Final(md, &c);
+}
+
+/*
+ * Read a file that optionally contains the server certificate in PEM
+ * format, possibly followed by a sequence of CA certificates that
+ * should be sent to the peer in the SSL Certificate message.
+ */
+int SSL_CTX_use_certificate_chain(SSL_CTX *ctx, const char *file,
+                                  int skipfirst)
+{
+    BIO *bio;
+    X509 *x509;
+    unsigned long err;
+    int n;
+    STACK *extra_certs;
+
+    if ((bio = BIO_new(BIO_s_file_internal())) == NULL)
+        return -1;
+    if (BIO_read_filename(bio, file) <= 0) {
+        BIO_free(bio);
+        return -1;
+    }
+    /* optionally skip a leading server certificate */
+    if (skipfirst) {
+        if ((x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL)) == NULL) {
+            BIO_free(bio);
+            return -1;
+        }
+        X509_free(x509);
+    }
+    /* free a perhaps already configured extra chain */
+    extra_certs = SSL_CTX_get_extra_certs(ctx);
+    if (extra_certs != NULL) {
+        sk_X509_pop_free((STACK_OF(X509) *)extra_certs, X509_free);
+        SSL_CTX_set_extra_certs(ctx,NULL);
+    }
+    /* create new extra chain by loading the certs */
+    n = 0;
+    while ((x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL)) != NULL) {
+        if (!SSL_CTX_add_extra_chain_cert(ctx, x509)) {
+            X509_free(x509);
+            BIO_free(bio);
+            return -1;
+        }
+        n++;
+    }
+    /* Make sure that only the error is just an EOF */
+    if ((err = ERR_peek_error()) > 0) {
+        if (!(   ERR_GET_LIB(err) == ERR_LIB_PEM
+              && ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) {
+            BIO_free(bio);
+            return -1;
+        }
+        while (ERR_get_error() > 0) ;
+    }
+    BIO_free(bio);
+    return n;
+}
+
+static int ssl_X509_STORE_lookup(X509_STORE *store, int yype,
+                                 X509_NAME *name, X509_OBJECT *obj)
+{
+    X509_STORE_CTX ctx;
+    int rc;
+
+    X509_STORE_CTX_init(&ctx, store, NULL, NULL);
+    rc = X509_STORE_get_by_subject(&ctx, yype, name, obj);
+    X509_STORE_CTX_cleanup(&ctx);
+    return rc;
+}
+
+static int ssl_verify_CRL(int ok, X509_STORE_CTX *ctx, tcn_ssl_conn_t *con)
+{
+    X509_OBJECT obj;
+    X509_NAME *subject, *issuer;
+    X509 *cert;
+    X509_CRL *crl;
+    EVP_PKEY *pubkey;
+    int i, n, rc;
+
+    /*
+     * Determine certificate ingredients in advance
+     */
+    cert    = X509_STORE_CTX_get_current_cert(ctx);
+    subject = X509_get_subject_name(cert);
+    issuer  = X509_get_issuer_name(cert);
+
+    /*
+     * OpenSSL provides the general mechanism to deal with CRLs but does not
+     * use them automatically when verifying certificates, so we do it
+     * explicitly here. We will check the CRL for the currently checked
+     * certificate, if there is such a CRL in the store.
+     *
+     * We come through this procedure for each certificate in the certificate
+     * chain, starting with the root-CA's certificate. At each step we've to
+     * both verify the signature on the CRL (to make sure it's a valid CRL)
+     * and it's revocation list (to make sure the current certificate isn't
+     * revoked).  But because to check the signature on the CRL we need the
+     * public key of the issuing CA certificate (which was already processed
+     * one round before), we've a little problem. But we can both solve it and
+     * at the same time optimize the processing by using the following
+     * verification scheme (idea and code snippets borrowed from the GLOBUS
+     * project):
+     *
+     * 1. We'll check the signature of a CRL in each step when we find a CRL
+     *    through the _subject_ name of the current certificate. This CRL
+     *    itself will be needed the first time in the next round, of course.
+     *    But we do the signature processing one round before this where the
+     *    public key of the CA is available.
+     *
+     * 2. We'll check the revocation list of a CRL in each step when
+     *    we find a CRL through the _issuer_ name of the current certificate.
+     *    This CRLs signature was then already verified one round before.
+     *
+     * This verification scheme allows a CA to revoke its own certificate as
+     * well, of course.
+     */
+
+    /*
+     * Try to retrieve a CRL corresponding to the _subject_ of
+     * the current certificate in order to verify it's integrity.
+     */
+    memset((char *)&obj, 0, sizeof(obj));
+    rc = ssl_X509_STORE_lookup(con->ctx->crl,
+                               X509_LU_CRL, subject, &obj);
+    crl = obj.data.crl;
+
+    if ((rc > 0) && crl) {
+        /*
+         * Log information about CRL
+         * (A little bit complicated because of ASN.1 and BIOs...)
+         */
+        /*
+         * Verify the signature on this CRL
+         */
+        pubkey = X509_get_pubkey(cert);
+        rc = X509_CRL_verify(crl, pubkey);
+        /* Only refcounted in OpenSSL */
+        if (pubkey)
+            EVP_PKEY_free(pubkey);
+        if (rc <= 0) {
+            /* TODO: Log Invalid signature on CRL */
+            X509_STORE_CTX_set_error(ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
+            X509_OBJECT_free_contents(&obj);
+            return 0;
+        }
+
+        /*
+         * Check date of CRL to make sure it's not expired
+         */
+        i = X509_cmp_current_time(X509_CRL_get_nextUpdate(crl));
+
+        if (i == 0) {
+            /* TODO: Log Found CRL has invalid nextUpdate field */
+
+            X509_STORE_CTX_set_error(ctx,
+                                     X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
+            X509_OBJECT_free_contents(&obj);
+            return 0;
+        }
+
+        if (i < 0) {
+            /* TODO: Log Found CRL is expired */
+            X509_STORE_CTX_set_error(ctx, X509_V_ERR_CRL_HAS_EXPIRED);
+            X509_OBJECT_free_contents(&obj);
+
+            return 0;
+        }
+
+        X509_OBJECT_free_contents(&obj);
+    }
+
+    /*
+     * Try to retrieve a CRL corresponding to the _issuer_ of
+     * the current certificate in order to check for revocation.
+     */
+    memset((char *)&obj, 0, sizeof(obj));
+    rc = ssl_X509_STORE_lookup(con->ctx->crl,
+                               X509_LU_CRL, issuer, &obj);
+
+    crl = obj.data.crl;
+    if ((rc > 0) && crl) {
+        /*
+         * Check if the current certificate is revoked by this CRL
+         */
+        n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
+
+        for (i = 0; i < n; i++) {
+            X509_REVOKED *revoked =
+                sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
+
+            ASN1_INTEGER *sn = revoked->serialNumber;
+
+            if (!ASN1_INTEGER_cmp(sn, X509_get_serialNumber(cert))) {
+                X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REVOKED);
+                X509_OBJECT_free_contents(&obj);
+
+                return 0;
+            }
+        }
+
+        X509_OBJECT_free_contents(&obj);
+    }
+
+    return ok;
+}
+
+/*
+ * This OpenSSL callback function is called when OpenSSL
+ * does client authentication and verifies the certificate chain.
+ */
+int SSL_callback_SSL_verify(int ok, X509_STORE_CTX *ctx)
+{
+   /* Get Apache context back through OpenSSL context */
+    SSL *ssl = X509_STORE_CTX_get_ex_data(ctx,
+                                          SSL_get_ex_data_X509_STORE_CTX_idx());
+    tcn_ssl_conn_t *con = (tcn_ssl_conn_t *)SSL_get_app_data(ssl);
+    /* Get verify ingredients */
+    int errnum   = X509_STORE_CTX_get_error(ctx);
+    int errdepth = X509_STORE_CTX_get_error_depth(ctx);
+    int verify   = con->ctx->verify_mode;
+    int depth    = con->ctx->verify_depth;
+
+    if (verify == SSL_CVERIFY_UNSET ||
+        verify == SSL_CVERIFY_NONE)
+        return 1;
+
+    if (SSL_VERIFY_ERROR_IS_OPTIONAL(errnum) &&
+        (verify == SSL_CVERIFY_OPTIONAL_NO_CA)) {
+        ok = 1;
+        SSL_set_verify_result(ssl, X509_V_OK);
+    }
+    /*
+     * Additionally perform CRL-based revocation checks
+     */
+    if (ok && con->ctx->crl) {
+        if (!(ok = ssl_verify_CRL(ok, ctx, con))) {
+            errnum = X509_STORE_CTX_get_error(ctx);
+            /* TODO: Log something */
+        }
+    }
+    /*
+     * If we already know it's not ok, log the real reason
+     */
+    if (!ok) {
+        /* TODO: Some logging
+         * Certificate Verification: Error
+         */
+        if (con->peer) {
+            X509_free(con->peer);
+            con->peer = NULL;
+        }
+    }
+    if (errdepth > depth) {
+        /* TODO: Some logging
+         * Certificate Verification: Certificate Chain too long
+         */
+        ok = 0;
+    }
+    return ok;
+}
+
+#endif
diff --git a/connectors/jni/native/src/stdlib.c b/connectors/jni/native/src/stdlib.c
new file mode 100644
index 0000000..77fc803
--- /dev/null
+++ b/connectors/jni/native/src/stdlib.c
@@ -0,0 +1,120 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Mladen Turk
+ * @version $Revision$, $Date$
+ */
+ 
+#include "tcn.h"
+
+extern int tcn_parent_pid;
+
+TCN_IMPLEMENT_CALL(jlong, Stdlib, malloc)(TCN_STDARGS, jint size)
+{
+    UNREFERENCED_STDARGS;
+    if (size)
+        return P2J(malloc((size_t)size));
+    else
+        return 0;
+}
+
+TCN_IMPLEMENT_CALL(jlong, Stdlib, realloc)(TCN_STDARGS, jlong mem, jint size)
+{
+    void *ptr = J2P(mem, void *);
+    UNREFERENCED_STDARGS;
+    if (size)
+        return P2J(realloc(ptr, (size_t)size));
+    else
+        return 0;
+}
+
+TCN_IMPLEMENT_CALL(jlong, Stdlib, calloc)(TCN_STDARGS, jint num, jint size)
+{
+    UNREFERENCED_STDARGS;
+    if (num && size)
+        return P2J(calloc((size_t)num, (size_t)size));
+    else
+        return 0;
+}
+
+TCN_IMPLEMENT_CALL(void, Stdlib, free)(TCN_STDARGS, jlong mem)
+{
+    void *ptr = J2P(mem, void *);
+
+    UNREFERENCED_STDARGS;
+    if (ptr)
+        free(ptr);
+}
+
+TCN_IMPLEMENT_CALL(jboolean, Stdlib, memread)(TCN_STDARGS,
+                                              jbyteArray dst,
+                                              jlong src, jint sz)
+{
+    jbyte *s = J2P(src, jbyte *);
+    jbyte *dest = (*e)->GetPrimitiveArrayCritical(e, dst, NULL);
+
+    UNREFERENCED(o);
+    if (s && dest) {
+        memcpy(dest, s, (size_t)sz);
+        (*e)->ReleasePrimitiveArrayCritical(e, dst, dest, 0);
+        return JNI_TRUE;
+    }
+    else
+        return JNI_FALSE;
+}
+
+TCN_IMPLEMENT_CALL(jboolean, Stdlib, memwrite)(TCN_STDARGS, jlong dst,
+                                               jbyteArray src, jint sz)
+{
+    jbyte *dest = J2P(dst, jbyte *);
+    jbyte *s = (*e)->GetPrimitiveArrayCritical(e, src, NULL);
+
+    UNREFERENCED(o);
+    if (s && dest) {
+        memcpy(dest, s, (size_t)sz);
+        (*e)->ReleasePrimitiveArrayCritical(e, src, s, JNI_ABORT);
+        return JNI_TRUE;
+    }
+    else
+        return JNI_FALSE;
+}
+
+TCN_IMPLEMENT_CALL(jboolean, Stdlib, memset)(TCN_STDARGS, jlong dst,
+                                             jint  c, jint sz)
+{
+    jbyte *dest = J2P(dst, jbyte *);
+
+    UNREFERENCED_STDARGS;
+    if (memset(dest, (int)c, (size_t)sz))
+        return JNI_TRUE;
+    else
+        return JNI_FALSE;
+}
+
+TCN_IMPLEMENT_CALL(jint, Stdlib, getpid)(TCN_STDARGS)
+{
+    UNREFERENCED_STDARGS;
+    return (jint)getpid();
+}
+
+TCN_IMPLEMENT_CALL(jint, Stdlib, getppid)(TCN_STDARGS)
+{
+    UNREFERENCED_STDARGS;
+    return (jint)tcn_parent_pid;
+}
+
diff --git a/connectors/jni/native/src/thread.c b/connectors/jni/native/src/thread.c
new file mode 100644
index 0000000..e074ff4
--- /dev/null
+++ b/connectors/jni/native/src/thread.c
@@ -0,0 +1,29 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Mladen Turk
+ * @version $Revision$, $Date$
+ */
+
+#include "tcn.h"
+
+TCN_IMPLEMENT_CALL(jlong, Thread, current)(TCN_STDARGS)
+{
+    UNREFERENCED_STDARGS;
+    return (jlong)((unsigned long)apr_os_thread_current());
+}
diff --git a/connectors/jni/native/src/user.c b/connectors/jni/native/src/user.c
new file mode 100644
index 0000000..6297a20
--- /dev/null
+++ b/connectors/jni/native/src/user.c
@@ -0,0 +1,163 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 Mladen Turk
+ * @version $Revision$, $Date$
+ */
+ 
+#include "tcn.h"
+#include "apr_user.h"
+#include "apr_network_io.h"
+
+TCN_IMPLEMENT_CALL(jlong, User, uidCurrent)(TCN_STDARGS, jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    apr_uid_t uid;
+    apr_gid_t gid;
+
+    UNREFERENCED(o);
+    TCN_THROW_IF_ERR(apr_uid_current(&uid, &gid, p), uid);
+
+cleanup:
+    return (jlong)uid;
+}
+
+TCN_IMPLEMENT_CALL(jlong, User, gidCurrent)(TCN_STDARGS, jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    apr_uid_t uid;
+    apr_gid_t gid;
+
+    UNREFERENCED(o);
+    TCN_THROW_IF_ERR(apr_uid_current(&uid, &gid, p), gid);
+
+cleanup:
+    return (jlong)gid;
+}
+
+TCN_IMPLEMENT_CALL(jlong, User, uid)(TCN_STDARGS, jstring uname, jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    apr_uid_t uid;
+    apr_gid_t gid;
+    TCN_ALLOC_CSTRING(uname);
+
+    UNREFERENCED(o);
+    TCN_THROW_IF_ERR(apr_uid_get(&uid, &gid, J2S(uname), p), uid);
+
+cleanup:
+    TCN_FREE_CSTRING(uname);
+    return (jlong)uid;
+}
+
+TCN_IMPLEMENT_CALL(jlong, User, usergid)(TCN_STDARGS, jstring uname,
+                                         jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    apr_uid_t uid;
+    apr_gid_t gid;
+    TCN_ALLOC_CSTRING(uname);
+
+    UNREFERENCED(o);
+    TCN_THROW_IF_ERR(apr_uid_get(&uid, &gid, J2S(uname), p), gid);
+
+cleanup:
+    TCN_FREE_CSTRING(uname);
+    return (jlong)gid;
+}
+
+TCN_IMPLEMENT_CALL(jlong, User, gid)(TCN_STDARGS, jstring gname, jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    apr_gid_t gid;
+    TCN_ALLOC_CSTRING(gname);
+
+    UNREFERENCED(o);
+    TCN_THROW_IF_ERR( apr_gid_get(&gid, J2S(gname), p), gid);
+
+cleanup:
+    TCN_FREE_CSTRING(gname);
+    return (jlong)gid;
+}
+
+TCN_IMPLEMENT_CALL(jstring, User, username)(TCN_STDARGS, jlong userid, jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    apr_uid_t uid = (apr_uid_t)userid;
+    char *uname = NULL;
+
+    UNREFERENCED(o);
+    TCN_THROW_IF_ERR(apr_uid_name_get(&uname, uid, p), uname);
+
+cleanup:
+    if (uname)
+        return AJP_TO_JSTRING(uname);
+    else
+        return NULL;
+}
+
+TCN_IMPLEMENT_CALL(jstring, User, groupname)(TCN_STDARGS, jlong grpid, jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    apr_gid_t gid = (apr_uid_t)grpid;
+    char *gname = NULL;
+
+    UNREFERENCED(o);
+    TCN_THROW_IF_ERR(apr_gid_name_get(&gname, gid, p), gname);
+
+cleanup:
+    if (gname)
+        return AJP_TO_JSTRING(gname);
+    else
+        return NULL;
+}
+
+TCN_IMPLEMENT_CALL(jint, User,uidcompare)(TCN_STDARGS, jlong left, jlong right)
+{
+
+    UNREFERENCED_STDARGS;
+    return (int)apr_uid_compare((apr_uid_t)left,
+                                (apr_uid_t)right);
+}
+
+TCN_IMPLEMENT_CALL(jint, User,gidcompare)(TCN_STDARGS, jlong left, jlong right)
+{
+
+    UNREFERENCED_STDARGS;
+    return (int)apr_gid_compare((apr_gid_t)left,
+                                (apr_gid_t)right);
+}
+
+TCN_IMPLEMENT_CALL(jstring, User, homepath)(TCN_STDARGS, jstring uname, jlong pool)
+{
+    apr_pool_t *p = J2P(pool, apr_pool_t *);
+    char *dirname = NULL;
+    TCN_ALLOC_CSTRING(uname);
+
+    UNREFERENCED(o);
+    TCN_THROW_IF_ERR(apr_uid_homepath_get(&dirname, J2S(uname),
+                                          p), dirname);
+
+cleanup:
+    TCN_FREE_CSTRING(uname);
+    if (dirname)
+        return AJP_TO_JSTRING(dirname);
+    else
+        return NULL;
+}
+
diff --git a/connectors/jni/native/srclib/VERSIONS b/connectors/jni/native/srclib/VERSIONS
new file mode 100644
index 0000000..18b9f14
--- /dev/null
+++ b/connectors/jni/native/srclib/VERSIONS
@@ -0,0 +1,4 @@
+Use the following version of the libraries
+
+- APR 1.2.8, http://apr.apache.org
+- OpenSSL 0.9.8e, http://www.openssl.org
diff --git a/connectors/jni/native/tcnative.dsp b/connectors/jni/native/tcnative.dsp
new file mode 100644
index 0000000..5e5c46d
--- /dev/null
+++ b/connectors/jni/native/tcnative.dsp
@@ -0,0 +1,231 @@
+# Microsoft Developer Studio Project File - Name="tcnative" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=tcnative - 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 "tcnative.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 "tcnative.mak" CFG="tcnative - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "tcnative - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "tcnative - 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)" == "tcnative - 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 "LibR"
+# PROP Intermediate_Dir "LibR"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /Zi /O2 /Oy- /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /Zi /O2 /Oy- /I "./include" /I "../apr/include" /I "../apr/include/arch/win32" /I "$(JAVA_HOME)/include" /I "$(JAVA_HOME)/include/win32" /I "../openssl/inc32" /D "NDEBUG" /D "TCN_DECLARE_EXPORT" /D "WIN32" /D "_WINDOWS" /D "APR_DECLARE_STATIC" /D "NO_IDEA" /D "NO_RC5" /D "NO_MDC2" /D "OPENSSL_NO_IDEA" /D "OPENSSL_NO_RC5" /D "OPENSSL_NO_MDC2" /D "HAVE_OPENSSL" /D HAVE_SSL_SET_STATE=1 /Fd"LibR\tcnative_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL"
+# 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 advapi32.lib ws2_32.lib mswsock.lib wldap32.lib psapi.lib ole32.lib shlwapi.lib /nologo /base:"0x6EE00000" /subsystem:windows /dll /debug /machine:I386 /opt:ref
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib wldap32.lib psapi.lib ole32.lib shlwapi.lib libeay32.lib ssleay32.lib /nologo /base:"0x6EE00000" /subsystem:windows /dll /debug /machine:I386 /out:"LibR/tcnative-1.dll" /libpath:"../openssl/out32" /libpath:"../openssl/out32dll" /opt:ref
+
+!ELSEIF  "$(CFG)" == "tcnative - 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 "LibD"
+# PROP Intermediate_Dir "LibD"
+# 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 /W4 /GX /Zi /Od /I "./include" /I "../apr/include" /I "../apr/include/arch/win32" /I "$(JAVA_HOME)/include" /I "$(JAVA_HOME)/include/win32" /I "../openssl/inc32" /D "_DEBUG" /D "TCN_DECLARE_EXPORT" /D "WIN32" /D "_WINDOWS" /D "APR_DECLARE_STATIC" /D "NO_IDEA" /D "NO_RC5" /D "NO_MDC2" /D "OPENSSL_NO_IDEA" /D "OPENSSL_NO_RC5" /D "OPENSSL_NO_MDC2" /D "HAVE_OPENSSL" /D HAVE_SSL_SET_STATE=1 /Fd"LibD\tcnative_src" /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL"
+# 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 advapi32.lib ws2_32.lib mswsock.lib wldap32.lib psapi.lib ole32.lib shlwapi.lib /nologo /base:"0x6EE00000" /subsystem:windows /dll /incremental:no /debug /machine:I386
+# ADD LINK32 kernel32.lib advapi32.lib ws2_32.lib mswsock.lib wldap32.lib psapi.lib ole32.lib shlwapi.lib libeay32.lib ssleay32.lib /nologo /base:"0x6EE00000" /subsystem:windows /dll /incremental:no /debug /machine:I386 /out:"LibD/tcnative-1.dll" /libpath:"../openssl/out32" /libpath:"../openssl/out32dll" 
+
+!ENDIF 
+
+# Begin Target
+
+# Name "tcnative - Win32 Release"
+# Name "tcnative - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\src\address.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\dir.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\error.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\file.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\info.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\jnilib.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\lock.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\misc.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\mmap.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\multicast.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\network.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\poll.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\pool.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\proc.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\shm.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\ssl.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\sslcontext.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\sslinfo.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\sslnetwork.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\sslutils.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\stdlib.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\thread.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\src\user.c
+# End Source File
+# End Group
+# Begin Group "Generated Files"
+
+# PROP Default_Filter ""
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\include\ssl_private.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\include\tcn.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\include\tcn_api.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\include\tcn_version.h
+# End Source File
+# End Group
+# Begin Group "Platform Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\os\win32\ntpipe.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\os\win32\registry.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\os\win32\system.c
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=.\os\win32\libtcnative.rc
+# End Source File
+# End Target
+# End Project
diff --git a/connectors/jni/native/tcnative.pc.in b/connectors/jni/native/tcnative.pc.in
new file mode 100644
index 0000000..f6ae98a
--- /dev/null
+++ b/connectors/jni/native/tcnative.pc.in
@@ -0,0 +1,13 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+TCNATIVE_MAJOR_VERSION=@TCNATIVE_MAJOR_VERSION@
+includedir=@includedir@
+
+Name: Tomcat native Java
+Description: Companion Native Java library
+Version: @TCNATIVE_DOTTED_VERSION@
+# assume that tcnative requires libapr of same major version
+Requires: apr-@TCNATIVE_MAJOR_VERSION@
+Libs: -L${libdir} -l@TCNATIVE_LIBNAME@ @TCNATIVE_EXPORT_LIBS@
+Cflags: -I${includedir}
diff --git a/connectors/jni/test/org/apache/tomcat/jni/FileTestSuite.java b/connectors/jni/test/org/apache/tomcat/jni/FileTestSuite.java
new file mode 100644
index 0000000..fadac30
--- /dev/null
+++ b/connectors/jni/test/org/apache/tomcat/jni/FileTestSuite.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.jni;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import junit.textui.TestRunner;
+
+/**
+ * A basic test suite that tests File IO.
+ * 
+ * @author Mladen Turk
+ * @version $Revision$, $Date$ 
+ * @see org.apache.tomcat.jni
+ */
+public class FileTestSuite
+{
+    
+    public static void main(String[] args) {
+        TestRunner.run(suite());
+    }
+    
+    public static Test suite()
+    {
+        TestSuite suite = new TestSuite( "Tomcat Native File IO" );
+        return suite;
+    }
+}
diff --git a/connectors/juli/build.xml b/connectors/juli/build.xml
new file mode 100644
index 0000000..326934b
--- /dev/null
+++ b/connectors/juli/build.xml
@@ -0,0 +1,214 @@
+<project name="Juli" 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"/>
+
+
+<!-- ========== Derived Values ============================================ -->
+
+
+	  <property name="junit.jar"        value="${junit.home}/junit.jar"/>
+	  <property name="jmx.jar" location="../lib/mx4j.jar" />
+
+	
+<!-- ========== Component Declarations ==================================== -->
+
+
+  <!-- The name of this component -->
+  <property name="component.name"          value="juli"/>
+
+  <!-- The title of this component -->
+  <property name="component.title"         value="Java Util Logging Implementation"/>
+
+  <!-- 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="${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="${jmx.jar}"/>
+    <pathelement location="${junit.jar}"/>
+  </path>
+
+  <!-- Should all tests fail if one does? -->
+  <property name="test.failonerror"        value="true"/>
+
+
+<!-- ========== 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) 2005 - 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}"
+			source="1.4"
+          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-juli.jar" value="${build.home}/lib/tomcat-${component.name}.jar"/>
+    <jar    jarfile="${tomcat-juli.jar}"
+             index="true"
+            basedir="${build.home}/classes"
+             manifest="${build.home}/conf/MANIFEST.MF">
+      <include name="org/apache/juli/**"/>
+    </jar>
+  </target>
+
+  <target name="compile" depends="static,compile-only"
+          description="Compile shareable components">
+  </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/juli/src/conf/logging.properties b/connectors/juli/src/conf/logging.properties
new file mode 100644
index 0000000..3192ae6
--- /dev/null
+++ b/connectors/juli/src/conf/logging.properties
@@ -0,0 +1,24 @@
+handlers = org.apache.juli.FileHandler java.util.logging.ConsoleHandler
+
+############################################################
+# Handler specific properties.
+# Describes specific configuration info for Handlers.
+############################################################
+
+org.apache.juli.FileHandler.level = FINE
+org.apache.juli.FileHandler.directory = ${catalina.base}/logs
+
+java.util.logging.ConsoleHandler.level = FINE
+java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
+
+
+############################################################
+# Facility specific properties.
+# Provides extra control for each logger.
+############################################################
+
+# For example, set the com.xyz.foo logger to only log SEVERE
+# messages:
+#org.apache.catalina.startup.ContextConfig.level = FINE
+#org.apache.catalina.startup.HostConfig.level = FINE
+#org.apache.catalina.session.ManagerBase.level = FINE
diff --git a/connectors/juli/src/java/org/apache/juli/ClassLoaderLogManager.java b/connectors/juli/src/java/org/apache/juli/ClassLoaderLogManager.java
new file mode 100644
index 0000000..5a49b0e
--- /dev/null
+++ b/connectors/juli/src/java/org/apache/juli/ClassLoaderLogManager.java
@@ -0,0 +1,569 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.juli;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URLClassLoader;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.WeakHashMap;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogManager;
+import java.util.logging.Logger;
+
+
+/**
+ * Per classloader LogManager implementation.
+ */
+public class ClassLoaderLogManager extends LogManager {
+
+
+    // -------------------------------------------------------------- Variables
+
+
+    /**
+     * Map containing the classloader information, keyed per classloader. A
+     * weak hashmap is used to ensure no classloader reference is leaked from 
+     * application redeployment.
+     */
+    protected final Map classLoaderLoggers = new WeakHashMap();
+
+    
+    /**
+     * This prefix is used to allow using prefixes for the properties names
+     * of handlers and their subcomponents.
+     */
+    protected ThreadLocal prefix = new ThreadLocal();
+
+    
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add the specified logger to the classloader local configuration.
+     * 
+     * @param logger The logger to be added
+     */
+    public synchronized boolean addLogger(final Logger logger) {
+
+        final String loggerName = logger.getName();
+
+        ClassLoader classLoader = 
+            Thread.currentThread().getContextClassLoader();
+        ClassLoaderLogInfo info = getClassLoaderInfo(classLoader);
+        if (info.loggers.containsKey(loggerName)) {
+            return false;
+        }
+        info.loggers.put(loggerName, logger);
+
+        // Apply initial level for new logger
+        final String levelString = getProperty(loggerName + ".level");
+        if (levelString != null) {
+            try {
+                AccessController.doPrivileged(new PrivilegedAction() {
+                    public Object run() {
+                        logger.setLevel(Level.parse(levelString.trim()));
+                        return null;
+                    }
+                });
+            } catch (IllegalArgumentException e) {
+                // Leave level set to null
+            }
+        }
+
+        // If any parent loggers have levels definied, make sure they are
+        // instantiated
+        int dotIndex = loggerName.lastIndexOf('.');
+        while (dotIndex >= 0) {
+            final String parentName = loggerName.substring(0, dotIndex);
+            if (getProperty(parentName + ".level") != null) {
+                Logger.getLogger(parentName);
+                break;
+            }
+            dotIndex = loggerName.lastIndexOf('.', dotIndex - 1);
+        }
+
+        // Find associated node
+        LogNode node = info.rootNode.findNode(loggerName);
+        node.logger = logger;
+
+        // Set parent logger
+        Logger parentLogger = node.findParentLogger();
+        if (parentLogger != null) {
+            doSetParentLogger(logger, parentLogger);
+        }
+
+        // Tell children we are their new parent
+        node.setParentLogger(logger);
+
+        // Add associated handlers, if any are defined using the .handlers property.
+        // In this case, handlers of the parent logger(s) will not be used
+        String handlers = getProperty(loggerName + ".handlers");
+        if (handlers != null) {
+            logger.setUseParentHandlers(false);
+            StringTokenizer tok = new StringTokenizer(handlers, ",");
+            while (tok.hasMoreTokens()) {
+                String handlerName = (tok.nextToken().trim());
+                Handler handler = null;
+                ClassLoader current = classLoader;
+                while (current != null) {
+                    info = (ClassLoaderLogInfo) classLoaderLoggers.get(current);
+                    if (info != null) {
+                        handler = (Handler) info.handlers.get(handlerName);
+                        if (handler != null) {
+                            break;
+                        }
+                    }
+                    current = current.getParent();
+                }
+                if (handler != null) {
+                    logger.addHandler(handler);
+                }
+            }
+        }
+
+        // Parse useParentHandlers to set if the logger should delegate to its parent.
+        // Unlike java.util.logging, the default is to not delegate if a list of handlers
+        // has been specified for the logger.
+        String useParentHandlersString = getProperty(loggerName + ".useParentHandlers");
+        if (Boolean.valueOf(useParentHandlersString).booleanValue()) {
+            logger.setUseParentHandlers(true);
+        }
+        
+        return true;
+    }
+
+    
+    /**
+     * Get the logger associated with the specified name inside 
+     * the classloader local configuration. If this returns null,
+     * and the call originated for Logger.getLogger, a new
+     * logger with the specified name will be instantiated and
+     * added using addLogger.
+     * 
+     * @param name The name of the logger to retrieve
+     */
+    public synchronized Logger getLogger(final String name) {
+        ClassLoader classLoader = Thread.currentThread()
+                .getContextClassLoader();
+        return (Logger) getClassLoaderInfo(classLoader).loggers.get(name);
+    }
+    
+    
+    /**
+     * Get an enumeration of the logger names currently defined in the 
+     * classloader local configuration.
+     */
+    public synchronized Enumeration getLoggerNames() {
+        ClassLoader classLoader = Thread.currentThread()
+                .getContextClassLoader();
+        return Collections.enumeration(getClassLoaderInfo(classLoader).loggers.keySet());
+    }
+
+    
+    /**
+     * Get the value of the specified property in the classloader local
+     * configuration.
+     * 
+     * @param name The property name
+     */    
+    public String getProperty(String name) {
+        ClassLoader classLoader = Thread.currentThread()
+            .getContextClassLoader();
+        String prefix = (String) this.prefix.get();
+        if (prefix != null) {
+            name = prefix + name;
+        }
+        ClassLoaderLogInfo info = getClassLoaderInfo(classLoader);
+        String result = info.props.getProperty(name);
+        // If the property was not found, and the current classloader had no 
+        // configuration (property list is empty), look for the parent classloader
+        // properties.
+        if ((result == null) && (info.props.isEmpty())) {
+            ClassLoader current = classLoader.getParent();
+            while (current != null) {
+                info = (ClassLoaderLogInfo) classLoaderLoggers.get(current);
+                if (info != null) {
+                    result = info.props.getProperty(name);
+                    if ((result != null) || (!info.props.isEmpty())) {
+                        break;
+                    }
+                }
+                current = current.getParent();
+            }
+            if (result == null) {
+                result = super.getProperty(name);
+            }
+        }
+        // Simple property replacement (mostly for folder names)
+        if (result != null) {
+            result = replace(result);
+        }
+        return result;
+    }
+    
+    
+    public void readConfiguration()
+        throws IOException, SecurityException {
+        
+        checkAccess();
+        
+        readConfiguration(Thread.currentThread().getContextClassLoader());
+        
+    }
+        
+    public void readConfiguration(InputStream is)
+        throws IOException, SecurityException {
+        
+        checkAccess();
+        reset();
+
+        readConfiguration(is, Thread.currentThread().getContextClassLoader());
+    
+    }
+        
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Retrieve the configuration associated with the specified classloader. If
+     * it does not exist, it will be created.
+     * 
+     * @param classLoader The classloader for which we will retrieve or build the 
+     *                    configuration
+     */
+    protected ClassLoaderLogInfo getClassLoaderInfo(ClassLoader classLoader) {
+        
+        if (classLoader == null) {
+            classLoader = ClassLoader.getSystemClassLoader();
+        }
+        ClassLoaderLogInfo info = (ClassLoaderLogInfo) classLoaderLoggers
+                .get(classLoader);
+        if (info == null) {
+            final ClassLoader classLoaderParam = classLoader;
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    try {
+                        readConfiguration(classLoaderParam);
+                    } catch (IOException e) {
+                        // Ignore
+                    }
+                    return null;
+                }
+            });
+            info = (ClassLoaderLogInfo) classLoaderLoggers.get(classLoader);
+        }
+        return info;
+    }
+
+    
+    /**
+     * Read configuration for the specified classloader.
+     * 
+     * @param classLoader 
+     * @throws IOException Errot
+     */
+    protected void readConfiguration(ClassLoader classLoader)
+        throws IOException {
+        
+        InputStream is = null;
+        // Special case for URL classloaders which are used in containers: 
+        // only look in the local repositories to avoid redefining loggers 20 times
+        if ((classLoader instanceof URLClassLoader) 
+                && (((URLClassLoader) classLoader).findResource("logging.properties") != null)) {
+            is = classLoader.getResourceAsStream("logging.properties");
+        }
+        if ((is == null) && (classLoader == ClassLoader.getSystemClassLoader())) {
+            String configFileStr = System.getProperty("java.util.logging.config.file");
+            if (configFileStr != null) {
+                try {
+                    is = new FileInputStream(replace(configFileStr));
+                } catch (IOException e) {
+                    // Ignore
+                }
+            }
+            // Try the default JVM configuration
+            if (is == null) {
+                File defaultFile = new File(new File(System.getProperty("java.home"), "lib"), 
+                    "logging.properties");
+                try {
+                    is = new FileInputStream(defaultFile);
+                } catch (IOException e) {
+                    // Critical problem, do something ...
+                }
+            }
+        }
+        
+        Logger localRootLogger = new RootLogger();
+        if (is == null) {
+            // Retrieve the root logger of the parent classloader instead
+            ClassLoader current = classLoader.getParent();
+            ClassLoaderLogInfo info = null;
+            while (current != null && info == null) {
+                info = getClassLoaderInfo(current);
+                current = current.getParent();
+            }
+            if (info != null) {
+                localRootLogger.setParent(info.rootNode.logger);
+            }
+        }
+        ClassLoaderLogInfo info = 
+            new ClassLoaderLogInfo(new LogNode(null, localRootLogger));
+        classLoaderLoggers.put(classLoader, info);
+        
+        if (is != null) {
+            readConfiguration(is, classLoader);
+        }
+        addLogger(localRootLogger);
+    }
+    
+    
+    /**
+     * Load specified configuration.
+     * 
+     * @param is InputStream to the properties file
+     * @param classLoader for which the configuration will be loaded
+     * @throws IOException If something wrong happens during loading
+     */
+    protected void readConfiguration(InputStream is, ClassLoader classLoader)
+        throws IOException {
+        
+        ClassLoaderLogInfo info = 
+            (ClassLoaderLogInfo) classLoaderLoggers.get(classLoader);
+        
+        try {
+            info.props.load(is);
+        } catch (IOException e) {
+            // Report error
+            System.err.println("Configuration error");
+            e.printStackTrace();
+        } finally {
+            try {
+                is.close();
+            } catch (Throwable t) {}
+        }
+        
+        // Create handlers for the root logger of this classloader
+        String rootHandlers = info.props.getProperty(".handlers");
+        String handlers = info.props.getProperty("handlers");
+        Logger localRootLogger = info.rootNode.logger;
+        if (handlers != null) {
+            StringTokenizer tok = new StringTokenizer(handlers, ",");
+            while (tok.hasMoreTokens()) {
+                String handlerName = (tok.nextToken().trim());
+                String handlerClassName = handlerName;
+                String prefix = "";
+                if (handlerClassName.length() <= 0) {
+                    continue;
+                }
+                // Parse and remove a prefix (prefix start with a digit, such as 
+                // "10WebappFooHanlder.")
+                if (Character.isDigit(handlerClassName.charAt(0))) {
+                    int pos = handlerClassName.indexOf('.');
+                    if (pos >= 0) {
+                        prefix = handlerClassName.substring(0, pos + 1);
+                        handlerClassName = handlerClassName.substring(pos + 1);
+                    }
+                }
+                try {
+                    this.prefix.set(prefix);
+                    Handler handler = 
+                        (Handler) classLoader.loadClass(handlerClassName).newInstance();
+                    // The specification strongly implies all configuration should be done 
+                    // during the creation of the handler object.
+                    // This includes setting level, filter, formatter and encoding.
+                    this.prefix.set(null);
+                    info.handlers.put(handlerName, handler);
+                    if (rootHandlers == null) {
+                        localRootLogger.addHandler(handler);
+                    }
+                } catch (Exception e) {
+                    // Report error
+                    System.err.println("Handler error");
+                    e.printStackTrace();
+                }
+            }
+            
+            // Add handlers to the root logger, if any are defined using the .handlers property.
+            if (rootHandlers != null) {
+                StringTokenizer tok2 = new StringTokenizer(rootHandlers, ",");
+                while (tok2.hasMoreTokens()) {
+                    String handlerName = (tok2.nextToken().trim());
+                    Handler handler = (Handler) info.handlers.get(handlerName);
+                    if (handler != null) {
+                        localRootLogger.addHandler(handler);
+                    }
+                }
+            }
+            
+        }
+        
+    }
+    
+    
+    /**
+     * Set parent child relationship between the two specified loggers.
+     * 
+     * @param logger
+     * @param parent
+     */
+    protected static void doSetParentLogger(final Logger logger,
+            final Logger parent) {
+        AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                logger.setParent(parent);
+                return null;
+            }
+        });
+    }
+
+    
+    /**
+     * System property replacement in the given string.
+     * 
+     * @param str The original string
+     * @return the modified string
+     */
+    protected String replace(String str) {
+        String result = str;
+        if (result.startsWith("${")) {
+            int pos = result.indexOf('}');
+            if (pos != -1) {
+                String propName = result.substring(2, pos);
+                String replacement = System.getProperty(propName);
+                if (replacement != null) {
+                    result = replacement + result.substring(pos + 1);
+                }
+            }
+        }
+        return result;
+    }
+    
+
+    // ---------------------------------------------------- LogNode Inner Class
+
+
+    protected static final class LogNode {
+        Logger logger;
+
+        protected final Map children = new HashMap();
+
+        protected final LogNode parent;
+
+        LogNode(final LogNode parent, final Logger logger) {
+            this.parent = parent;
+            this.logger = logger;
+        }
+
+        LogNode(final LogNode parent) {
+            this(parent, null);
+        }
+
+        LogNode findNode(String name) {
+            LogNode currentNode = this;
+            if (logger.getName().equals(name)) {
+                return this;
+            }
+            while (name != null) {
+                final int dotIndex = name.indexOf('.');
+                final String nextName;
+                if (dotIndex < 0) {
+                    nextName = name;
+                    name = null;
+                } else {
+                    nextName = name.substring(0, dotIndex);
+                    name = name.substring(dotIndex + 1);
+                }
+                LogNode childNode = (LogNode) currentNode.children
+                        .get(nextName);
+                if (childNode == null) {
+                    childNode = new LogNode(currentNode);
+                    currentNode.children.put(nextName, childNode);
+                }
+                currentNode = childNode;
+            }
+            return currentNode;
+        }
+
+        Logger findParentLogger() {
+            Logger logger = null;
+            LogNode node = parent;
+            while (node != null && logger == null) {
+                logger = node.logger;
+                node = node.parent;
+            }
+            return logger;
+        }
+
+        void setParentLogger(final Logger parent) {
+            for (final Iterator iter = children.values().iterator(); iter
+                    .hasNext();) {
+                final LogNode childNode = (LogNode) iter.next();
+                if (childNode.logger == null) {
+                    childNode.setParentLogger(parent);
+                } else {
+                    doSetParentLogger(childNode.logger, parent);
+                }
+            }
+        }
+
+    }
+
+
+    // -------------------------------------------- ClassLoaderInfo Inner Class
+
+
+    protected static final class ClassLoaderLogInfo {
+        final LogNode rootNode;
+        final Map loggers = new HashMap();
+        final Map handlers = new HashMap();
+        final Properties props = new Properties();
+
+        ClassLoaderLogInfo(final LogNode rootNode) {
+            this.rootNode = rootNode;
+        }
+
+    }
+
+
+    // ------------------------------------------------- RootLogger Inner Class
+
+
+    /**
+     * This class is needed to instantiate the root of each per classloader 
+     * hierarchy.
+     */
+    protected class RootLogger extends Logger {
+        public RootLogger() {
+            super("", null);
+        }
+    }
+
+
+}
diff --git a/connectors/juli/src/java/org/apache/juli/FileHandler.java b/connectors/juli/src/java/org/apache/juli/FileHandler.java
new file mode 100644
index 0000000..587440f
--- /dev/null
+++ b/connectors/juli/src/java/org/apache/juli/FileHandler.java
@@ -0,0 +1,269 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.juli;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.sql.Timestamp;
+import java.util.logging.ErrorManager;
+import java.util.logging.Filter;
+import java.util.logging.Formatter;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogManager;
+import java.util.logging.LogRecord;
+import java.util.logging.SimpleFormatter;
+
+/**
+ * Implementation of <b>Handler</b> that appends log messages to a file
+ * named {prefix}.{date}.{suffix} in a configured directory, with an
+ * optional preceding timestamp.
+ *
+ * @version $Revision$ $Date$
+ */
+
+public class FileHandler
+    extends Handler {
+
+
+    // ------------------------------------------------------------ Constructor
+
+    
+    public FileHandler() {
+        this(null, null, null);
+    }
+    
+    
+    public FileHandler(String directory, String prefix, String suffix) {
+        this.directory = directory;
+        this.prefix = prefix;
+        this.suffix = suffix;
+        configure();
+        open();
+    }
+    
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The as-of date for the currently open log file, or a zero-length
+     * string if there is no open log file.
+     */
+    private String date = "";
+
+
+    /**
+     * The directory in which log files are created.
+     */
+    private String directory = null;
+
+
+    /**
+     * The prefix that is added to log file filenames.
+     */
+    private String prefix = null;
+
+
+    /**
+     * The suffix that is added to log file filenames.
+     */
+    private String suffix = null;
+
+
+    /**
+     * The PrintWriter to which we are currently logging, if any.
+     */
+    private PrintWriter writer = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Format and publish a <tt>LogRecord</tt>.
+     *
+     * @param  record  description of the log event
+     */
+    public void publish(LogRecord record) {
+
+        if (!isLoggable(record)) {
+            return;
+        }
+
+        // Construct the timestamp we will use, if requested
+        Timestamp ts = new Timestamp(System.currentTimeMillis());
+        String tsString = ts.toString().substring(0, 19);
+        String tsDate = tsString.substring(0, 10);
+
+        // If the date has changed, switch log files
+        if (!date.equals(tsDate)) {
+            synchronized (this) {
+                if (!date.equals(tsDate)) {
+                    close();
+                    date = tsDate;
+                    open();
+                }
+            }
+        }
+
+        String result = null;
+        try {
+            result = getFormatter().format(record);
+        } catch (Exception e) {
+            reportError(null, e, ErrorManager.FORMAT_FAILURE);
+            return;
+        }
+        
+        try {
+            writer.write(result);
+            writer.flush();
+        } catch (Exception e) {
+            reportError(null, e, ErrorManager.WRITE_FAILURE);
+            return;
+        }
+        
+    }
+    
+    
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Close the currently open log file (if any).
+     */
+    public void close() {
+        
+        try {
+            if (writer == null)
+                return;
+            writer.write(getFormatter().getTail(this));
+            writer.flush();
+            writer.close();
+            writer = null;
+            date = "";
+        } catch (Exception e) {
+            reportError(null, e, ErrorManager.CLOSE_FAILURE);
+        }
+        
+    }
+
+
+    /**
+     * Flush the writer.
+     */
+    public void flush() {
+
+        try {
+            writer.flush();
+        } catch (Exception e) {
+            reportError(null, e, ErrorManager.FLUSH_FAILURE);
+        }
+        
+    }
+    
+    
+    /**
+     * Configure from <code>LogManager</code> properties.
+     */
+    private void configure() {
+
+        Timestamp ts = new Timestamp(System.currentTimeMillis());
+        String tsString = ts.toString().substring(0, 19);
+        date = tsString.substring(0, 10);
+
+        String className = FileHandler.class.getName();
+        
+        ClassLoader cl = Thread.currentThread().getContextClassLoader();
+        
+        // Retrieve configuration of logging file name
+        if (directory == null)
+            directory = getProperty(className + ".directory", "logs");
+        if (prefix == null)
+            prefix = getProperty(className + ".prefix", "juli.");
+        if (suffix == null)
+            suffix = getProperty(className + ".suffix", ".log");
+
+        // Get logging level for the handler
+        setLevel(Level.parse(getProperty(className + ".level", "" + Level.ALL)));
+
+        // Get filter configuration
+        String filterName = getProperty(className + ".filter", null);
+        if (filterName != null) {
+            try {
+                setFilter((Filter) cl.loadClass(filterName).newInstance());
+            } catch (Exception e) {
+                // Ignore
+            }
+        }
+
+        // Set formatter
+        String formatterName = getProperty(className + ".formatter", null);
+        if (formatterName != null) {
+            try {
+                setFormatter((Formatter) cl.loadClass(formatterName).newInstance());
+            } catch (Exception e) {
+                // Ignore
+            }
+        } else {
+            setFormatter(new SimpleFormatter());
+        }
+        
+        // Set error manager
+        setErrorManager(new ErrorManager());
+        
+    }
+
+    
+    private String getProperty(String name, String defaultValue) {
+        String value = LogManager.getLogManager().getProperty(name);
+        if (value == null) {
+            value = defaultValue;
+        } else {
+            value = value.trim();
+        }
+        return value;
+    }
+    
+    
+    /**
+     * Open the new log file for the date specified by <code>date</code>.
+     */
+    private void open() {
+
+        // Create the directory if necessary
+        File dir = new File(directory);
+        dir.mkdirs();
+
+        // Open the current log file
+        try {
+            String pathname = dir.getAbsolutePath() + File.separator +
+                prefix + date + suffix;
+            writer = new PrintWriter(new FileWriter(pathname, true), true);
+            writer.write(getFormatter().getHead(this));
+        } catch (Exception e) {
+            reportError(null, e, ErrorManager.OPEN_FAILURE);
+            writer = null;
+        }
+
+    }
+
+
+}
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/amd64/tomcat5.exe b/connectors/procrun/bin/amd64/tomcat5.exe
new file mode 100644
index 0000000..3ac96d7
--- /dev/null
+++ b/connectors/procrun/bin/amd64/tomcat5.exe
Binary files differ
diff --git a/connectors/procrun/bin/amd64/tomcat5w.exe b/connectors/procrun/bin/amd64/tomcat5w.exe
new file mode 100644
index 0000000..29f00e9
--- /dev/null
+++ b/connectors/procrun/bin/amd64/tomcat5w.exe
Binary files differ
diff --git a/connectors/procrun/bin/ia64/tomcat5.exe b/connectors/procrun/bin/ia64/tomcat5.exe
new file mode 100644
index 0000000..d2107fc
--- /dev/null
+++ b/connectors/procrun/bin/ia64/tomcat5.exe
Binary files differ
diff --git a/connectors/procrun/bin/ia64/tomcat5w.exe b/connectors/procrun/bin/ia64/tomcat5w.exe
new file mode 100644
index 0000000..f66fbfd
--- /dev/null
+++ b/connectors/procrun/bin/ia64/tomcat5w.exe
Binary files differ
diff --git a/connectors/procrun/bin/tomcat5.exe b/connectors/procrun/bin/tomcat5.exe
new file mode 100644
index 0000000..c0544c1
--- /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..5b0864a
--- /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..0918a55
--- /dev/null
+++ b/connectors/util/build.xml
@@ -0,0 +1,152 @@
+<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="${base.path}/commons-logging-1.0.4/commons-logging.jar" />
+    <property name="jmx.jar" location="${base.path}/mx4j-2.0.1/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" />
+    <property name="tomcat-loader.jar" value="${tomcat-util.lib}/tomcat-loader.jar" />
+    <property name="tomcat-jni.jar" value="../jni/dist/tomcat-native-1.0.0.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}" />
+        <pathelement location="${tomcat-jni.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" />
+        <available property="jdk1.5.present" classname="javax.net.ssl.CertPathTrustManagerParameters" />
+    </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="tomcat-util.jar"/> <!--tomcat-loader.jar -->
+        
+    <target name="tomcat-util.jar" 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="-- skip.digester = ${skip.digester}" />
+        <echo message="-- JDK14 = ${jdk1.4.present}"/>
+        <echo message="-- JDK15 = ${jdk1.5.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/JSSE15*" unless="jdk1.5.present" />
+            <exclude name="**/util/net/jsse/JSSEKeyManager.java" unless="jdk1.4.present" />
+            <exclude name="**/util/digester/*" if="skip.digester" />
+            <exclude name="**/util/net/AprEndpoint.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>
+
+    <target name="tomcat-loader.jar" depends="build-prepare">
+        <mkdir dir="${tomcat-util.build}/loader"/>
+        <javac srcdir="loader"
+           destdir="${tomcat-util.build}/loader"
+           deprecation="${compile.deprecation}"
+           debug="${compile.debug}"
+           optimize="off"
+           verbose="off"
+           excludes="**/CVS/**">
+            <classpath refid="compile.classpath"/>
+        </javac>
+     	<jar jarfile="${tomcat-loader.jar}"
+             index="true"
+             basedir="${tomcat-util.build}/loader"
+             manifest="loader/tomcat-loader.manifest" >
+            <include name="org/apache/tomcat/util/loader/**"/>
+        </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..59caf55
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/IntrospectionUtils.java
@@ -0,0 +1,1003 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 {
+
+    
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( IntrospectionUtils.class );
+    
+    /**
+     * 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) {
+            if (log.isDebugEnabled())
+                log.debug("No setAttribute in " + proxy.getClass());
+            return;
+        }
+        if (false)
+            if (log.isDebugEnabled())
+                log.debug("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) {
+            if (log.isDebugEnabled())
+                log.debug("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) {
+        if (log.isDebugEnabled()) {
+            log.debug(msg);
+            for (int i = 0; i < cp.length; i++) {
+                log.debug(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) {
+            log.warn("IAE " + o + " " + name + " " + value, ex2);
+        } 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);
+        String isGetter = "is" + 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 (isGetter.equals(methods[i].getName()) && paramT.length == 0) {
+                    return methods[i].invoke(o, (Object[]) null);
+                }
+
+                if ("getProperty".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;
+                return getPropertyMethod.invoke(o, params);
+            }
+
+        } catch (IllegalArgumentException ex2) {
+            log.warn("IAE " + o + " " + name, ex2);
+        } 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('$');
+                prev = pos + 1; // 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()) {
+                    if (log.isDebugEnabled())
+                        log.debug("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 void clear() {
+        objectMethods.clear();
+    }
+    
+    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) {
+        if (log.isDebugEnabled())
+            log.debug("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..25bb515
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/Ascii.java
@@ -0,0 +1,247 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..39e6e7e
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/B2CConverter.java
@@ -0,0 +1,271 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( B2CConverter.class );
+    
+    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 ) {
+        if (log.isDebugEnabled())
+            log.debug("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 {
+    
+    // Has a private, internal byte[8192]
+    
+    /** Create a converter.
+     */
+    public ReadConvertor( IntermediateInputStream in, String enc )
+        throws UnsupportedEncodingException
+    {
+        super( in, enc );
+    }
+    
+    /** 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..5fe37d8
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/Base64.java
@@ -0,0 +1,268 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 {
+
+
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( Base64.class );
+    
+    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  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] != '=' )
+                    if (log.isDebugEnabled())
+                        log.debug("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..ca2f93b
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/ByteChunk.java
@@ -0,0 +1,824 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.
+ */
+
+// TODO: This class could either extend ByteBuffer, or better a ByteBuffer inside
+// this way it could provide the search/etc on ByteBuffer, as a helper.
+
+/**
+ * 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.
+ *
+ * There are 2 modes: The chunk can be associated with a sink - ByteInputChannel or ByteOutputChannel,
+ * which will be used when the buffer is empty ( on input ) or filled ( on output ).
+ * For output, it can also grow. This operating mode is selected by calling setLimit() or
+ * allocate(initial, limit) with limit != -1.
+ *
+ * Various search and append method are defined - similar with String and StringBuffer, but
+ * operating on bytes.
+ *
+ * This is important because it allows processing the http headers directly on the received bytes,
+ * without converting to chars and Strings until the strings are needed. In addition, the charset
+ * is determined later, from headers or user code.
+ *
+ *
+ * @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 emptiy
+     *
+     * Same as java.nio.channel.ReadableByteChannel
+     */
+    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;
+    }
+
+    /** Same as java.nio.channel.WrittableByteChannel.
+     */
+    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 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  ) {
+        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;
+    }
+    public String getEncoding() {
+        if (enc == null)
+            enc=DEFAULT_CHARACTER_ENCODING;
+        return 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 start;
+    }
+
+    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 --------------------
+    /** Append a char, by casting it to byte. This IS NOT intended for unicode.
+     *
+     * @param c
+     * @throws IOException
+     */
+    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 != null ) {
+            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;
+
+    }
+
+
+    /** Send the buffer to the sink. Called by append() when the limit is reached.
+     *  You can also call it explicitely to force the data to be written.
+     *
+     * @throws IOException
+     */
+    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;
+        } else if (end-start == 0) {
+            return "";
+        }
+        return StringCache.toString(this);
+    }
+    
+    public String toStringInternal() {
+        String strValue=null;
+        try {
+            if( enc==null ) enc=DEFAULT_CHARACTER_ENCODING;
+            strValue = 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.UnsupportedEncodingException e) {
+            // Use the platform encoding in that case; the usage of a bad
+            // encoding will have been logged elsewhere already
+            strValue = new String(buff, start, end-start);
+        }
+        return strValue;
+    }
+
+    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;
+    }
+
+
+    /**
+     * Convert specified String to a byte array. This ONLY WORKS for ascii, UTF
+     * chars will be truncated.
+     * 
+     * @param value to convert to byte array
+     * @return the byte array value
+     */
+    public static final byte[] convertToBytes(String value) {
+        byte[] result = new byte[value.length()];
+        for (int i = 0; i < value.length(); i++) {
+            result[i] = (byte) value.charAt(i);
+        }
+        return result;
+    }
+    
+    
+}
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..3c866a8
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/C2BConverter.java
@@ -0,0 +1,262 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog(C2BConverter.class );
+    
+    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 {
+            if (log.isDebugEnabled())
+                log.debug("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..34a8702
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/CharChunk.java
@@ -0,0 +1,717 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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, CharSequence {
+
+    // 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 
+
+    // -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  ) {
+        if( buff==null || buff.length < initial ) {
+            buff=new char[initial];
+        }
+        this.limit=limit;
+        start=0;
+        end=0;
+        isSet=true;
+    }
+
+
+    public void setOptimizedWrite(boolean optimizedWrite) {
+        this.optimizedWrite = optimizedWrite;
+    }
+
+    public void setChars( char[] c, int off, int len ) {
+        buff=c;
+        start=off;
+        end=start + len;
+        isSet=true;
+    }
+
+    /** 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 start;
+    }
+
+    /**
+     * 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 != null ) {
+            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 (null == buff) {
+            return null;
+        } else if (end-start == 0) {
+            return "";
+        }
+        return StringCache.toString(this);
+    }
+    
+    public String toStringInternal() {
+        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;
+    }
+
+    // Char sequence impl
+    
+    public char charAt(int index) {
+        return buff[index + start];
+    }
+    
+    public CharSequence subSequence(int start, int end) {
+        try {
+            CharChunk result = (CharChunk) this.clone();
+            result.setOffset(this.start + start);
+            result.setEnd(this.start + end);
+            return result;
+        } catch (CloneNotSupportedException e) {
+            // Cannot happen
+            return null;
+        }
+    }
+    
+    public int length() {
+        return end - start;
+    }
+    
+}
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..03db465
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/DateTool.java
@@ -0,0 +1,165 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..192988a
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/HexUtils.java
@@ -0,0 +1,194 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..bae356e
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/MessageBytes.java
@@ -0,0 +1,736 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 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) {
+        byteC.setBytes( b, off, len );
+        type=T_BYTES;
+        hasStrValue=false;
+        hasHashCode=false;
+        hasIntValue=false;
+        hasLongValue=false;
+        hasDateValue=false; 
+    }
+
+    /** 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[]
+     *
+     * @param c the bytes
+     * @param off the start offset of the bytes
+     * @param len the length of the bytes
+     */
+    public void setChars( char[] c, int off, int len ) {
+        charC.setChars( c, off, len );
+        type=T_CHARS;
+        hasStrValue=false;
+        hasHashCode=false;
+        hasIntValue=false;
+        hasLongValue=false;
+        hasDateValue=false; 
+    }
+
+    /** 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 ) {
+        strValue=s;
+        hasHashCode=false;
+        hasIntValue=false;
+        hasLongValue=false;
+        hasDateValue=false; 
+        if (s == null) {
+            hasStrValue=false;
+            type=T_NULL;
+        } else {
+            hasStrValue=true;
+            type=T_STR;
+        }
+    }
+
+    // -------------------- Conversion and getters --------------------
+
+    /** Compute the string value
+     */
+    public String toString() {
+        if( hasStrValue ) return strValue;
+        
+        switch (type) {
+        case T_CHARS:
+            strValue=charC.toString();
+            hasStrValue=true;
+            return strValue;
+        case T_BYTES:
+            strValue=byteC.toString();
+            hasStrValue=true;
+            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) {
+        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.setOffset(0);
+        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;
+        hasStrValue=false;
+        hasHashCode=false;
+        hasIntValue=true;
+        hasLongValue=false;
+        hasDateValue=false; 
+        type=T_BYTES;
+    }
+
+    /** Set the buffer to the representation of an long
+     */
+    public void setLong(long l) {
+        byteC.allocate(32, 64);
+        long current = l;
+        byte[] buf = byteC.getBuffer();
+        int start = 0;
+        int end = 0;
+        if (l == 0) {
+            buf[end++] = (byte) '0';
+        }
+        if (l < 0) {
+            current = -l;
+            buf[end++] = (byte) '-';
+        }
+        while (current > 0) {
+            int digit = (int) (current % 10);
+            current = current / 10;
+            buf[end++] = HexUtils.HEX[digit];
+        }
+        byteC.setOffset(0);
+        byteC.setEnd(end);
+        // Inverting buffer
+        end--;
+        if (l < 0) {
+            start++;
+        }
+        while (end > start) {
+            byte temp = buf[start];
+            buf[start] = buf[end];
+            buf[end] = temp;
+            start++;
+            end--;
+        }
+        longValue=l;
+        hasStrValue=false;
+        hasHashCode=false;
+        hasIntValue=false;
+        hasLongValue=true;
+        hasDateValue=false; 
+        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/StringCache.java b/connectors/util/java/org/apache/tomcat/util/buf/StringCache.java
new file mode 100644
index 0000000..8cc662e
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/StringCache.java
@@ -0,0 +1,668 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.TreeMap;
+
+/**
+ * This class implements a String cache for ByteChunk and CharChunk.
+ *
+ * @author Remy Maucherat
+ */
+public class StringCache {
+
+
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( StringCache.class );
+    
+    
+    // ------------------------------------------------------- Static Variables
+
+    
+    /**
+     * Enabled ?
+     */
+    protected static boolean byteEnabled = 
+        ("true".equals(System.getProperty("tomcat.util.buf.StringCache.byte.enabled", "false")));
+
+    
+    protected static boolean charEnabled = 
+        ("true".equals(System.getProperty("tomcat.util.buf.StringCache.char.enabled", "false")));
+
+    
+    protected static int trainThreshold = 
+        Integer.parseInt(System.getProperty("tomcat.util.buf.StringCache.trainThreshold", "20000"));
+    
+
+    protected static int cacheSize = 
+        Integer.parseInt(System.getProperty("tomcat.util.buf.StringCache.cacheSize", "200"));
+    
+    protected static int maxStringSize = 
+        Integer.parseInt(System.getProperty("tomcat.util.buf.StringCache.maxStringSize", "128"));
+
+    /**
+     * Statistics hash map for byte chunk.
+     */
+    protected static HashMap bcStats = new HashMap(cacheSize);
+
+    
+    /**
+     * toString count for byte chunk.
+     */
+    protected static int bcCount = 0;
+    
+    
+    /**
+     * Cache for byte chunk.
+     */
+    protected static ByteEntry[] bcCache = null;
+    
+
+    /**
+     * Statistics hash map for char chunk.
+     */
+    protected static HashMap ccStats = new HashMap(cacheSize);
+
+
+    /**
+     * toString count for char chunk.
+     */
+    protected static int ccCount = 0; 
+    
+
+    /**
+     * Cache for char chunk.
+     */
+    protected static CharEntry[] ccCache = null;
+
+    
+    /**
+     * Access count.
+     */
+    protected static int accessCount = 0;
+    
+
+    /**
+     * Hit count.
+     */
+    protected static int hitCount = 0;
+    
+
+    // ------------------------------------------------------------ Properties
+
+    
+    /**
+     * @return Returns the cacheSize.
+     */
+    public int getCacheSize() {
+        return cacheSize;
+    }
+    
+    
+    /**
+     * @param cacheSize The cacheSize to set.
+     */
+    public void setCacheSize(int cacheSize) {
+        StringCache.cacheSize = cacheSize;
+    }
+
+    
+    /**
+     * @return Returns the enabled.
+     */
+    public boolean getByteEnabled() {
+        return byteEnabled;
+    }
+    
+    
+    /**
+     * @param byteEnabled The enabled to set.
+     */
+    public void setByteEnabled(boolean byteEnabled) {
+        StringCache.byteEnabled = byteEnabled;
+    }
+    
+    
+    /**
+     * @return Returns the enabled.
+     */
+    public boolean getCharEnabled() {
+        return charEnabled;
+    }
+    
+    
+    /**
+     * @param charEnabled The enabled to set.
+     */
+    public void setCharEnabled(boolean charEnabled) {
+        StringCache.charEnabled = charEnabled;
+    }
+    
+    
+    /**
+     * @return Returns the trainThreshold.
+     */
+    public int getTrainThreshold() {
+        return trainThreshold;
+    }
+    
+    
+    /**
+     * @param trainThreshold The trainThreshold to set.
+     */
+    public void setTrainThreshold(int trainThreshold) {
+        StringCache.trainThreshold = trainThreshold;
+    }
+
+    
+    /**
+     * @return Returns the accessCount.
+     */
+    public int getAccessCount() {
+        return accessCount;
+    }
+    
+    
+    /**
+     * @return Returns the hitCount.
+     */
+    public int getHitCount() {
+        return hitCount;
+    }
+
+    
+    // -------------------------------------------------- Public Static Methods
+
+    
+    public void reset() {
+        hitCount = 0;
+        accessCount = 0;
+        synchronized (bcStats) {
+            bcCache = null;
+            bcCount = 0;
+        }
+        synchronized (ccStats) {
+            ccCache = null;
+            ccCount = 0;
+        }
+    }
+    
+    
+    public static String toString(ByteChunk bc) {
+
+        // If the cache is null, then either caching is disabled, or we're
+        // still training
+        if (bcCache == null) {
+            String value = bc.toStringInternal();
+            if (byteEnabled && (value.length() < maxStringSize)) {
+                // If training, everything is synced
+                synchronized (bcStats) {
+                    // If the cache has been generated on a previous invocation
+                    // while waiting fot the lock, just return the toString value
+                    // we just calculated
+                    if (bcCache != null) {
+                        return value;
+                    }
+                    // Two cases: either we just exceeded the train count, in which
+                    // case the cache must be created, or we just update the count for
+                    // the string
+                    if (bcCount > trainThreshold) {
+                        long t1 = System.currentTimeMillis();
+                        // Sort the entries according to occurrence
+                        TreeMap tempMap = new TreeMap();
+                        Iterator entries = bcStats.keySet().iterator();
+                        while (entries.hasNext()) {
+                            ByteEntry entry = (ByteEntry) entries.next();
+                            int[] countA = (int[]) bcStats.get(entry);
+                            Integer count = new Integer(countA[0]);
+                            // Add to the list for that count
+                            ArrayList list = (ArrayList) tempMap.get(count);
+                            if (list == null) {
+                                // Create list
+                                list = new ArrayList();
+                                tempMap.put(count, list);
+                            }
+                            list.add(entry);
+                        }
+                        // Allocate array of the right size
+                        int size = bcStats.size();
+                        if (size > cacheSize) {
+                            size = cacheSize;
+                        }
+                        ByteEntry[] tempbcCache = new ByteEntry[size];
+                        // Fill it up using an alphabetical order
+                        // and a dumb insert sort
+                        ByteChunk tempChunk = new ByteChunk();
+                        int n = 0;
+                        while (n < size) {
+                            Object key = tempMap.lastKey();
+                            ArrayList list = (ArrayList) tempMap.get(key);
+                            for (int i = 0; i < list.size() && n < size; i++) {
+                                ByteEntry entry = (ByteEntry) list.get(i);
+                                tempChunk.setBytes(entry.name, 0, entry.name.length);
+                                int insertPos = findClosest(tempChunk, tempbcCache, n);
+                                if (insertPos == n) {
+                                    tempbcCache[n + 1] = entry;
+                                } else {
+                                    System.arraycopy(tempbcCache, insertPos + 1, tempbcCache, 
+                                            insertPos + 2, n - insertPos - 1);
+                                    tempbcCache[insertPos + 1] = entry;
+                                }
+                                n++;
+                            }
+                            tempMap.remove(key);
+                        }
+                        bcCount = 0;
+                        bcStats.clear();
+                        bcCache = tempbcCache;
+                        if (log.isDebugEnabled()) {
+                            long t2 = System.currentTimeMillis();
+                            log.debug("ByteCache generation time: " + (t2 - t1) + "ms");
+                        }
+                    } else {
+                        bcCount++;
+                        // Allocate new ByteEntry for the lookup
+                        ByteEntry entry = new ByteEntry();
+                        entry.value = value;
+                        int[] count = (int[]) bcStats.get(entry);
+                        if (count == null) {
+                            int end = bc.getEnd();
+                            int start = bc.getStart();
+                            // Create byte array and copy bytes
+                            entry.name = new byte[bc.getLength()];
+                            System.arraycopy(bc.getBuffer(), start, entry.name, 0, end - start);
+                            // Set encoding
+                            entry.enc = bc.getEncoding();
+                            // Initialize occurrence count to one 
+                            count = new int[1];
+                            count[0] = 1;
+                            // Set in the stats hash map
+                            bcStats.put(entry, count);
+                        } else {
+                            count[0] = count[0] + 1;
+                        }
+                    }
+                }
+            }
+            return value;
+        } else {
+            accessCount++;
+            // Find the corresponding String
+            String result = find(bc);
+            if (result == null) {
+                return bc.toStringInternal();
+            }
+            // Note: We don't care about safety for the stats
+            hitCount++;
+            return result;
+        }
+        
+    }
+
+
+    public static String toString(CharChunk cc) {
+        
+        // If the cache is null, then either caching is disabled, or we're
+        // still training
+        if (ccCache == null) {
+            String value = cc.toStringInternal();
+            if (charEnabled && (value.length() < maxStringSize)) {
+                // If training, everything is synced
+                synchronized (ccStats) {
+                    // If the cache has been generated on a previous invocation
+                    // while waiting fot the lock, just return the toString value
+                    // we just calculated
+                    if (ccCache != null) {
+                        return value;
+                    }
+                    // Two cases: either we just exceeded the train count, in which
+                    // case the cache must be created, or we just update the count for
+                    // the string
+                    if (ccCount > trainThreshold) {
+                        long t1 = System.currentTimeMillis();
+                        // Sort the entries according to occurrence
+                        TreeMap tempMap = new TreeMap();
+                        Iterator entries = ccStats.keySet().iterator();
+                        while (entries.hasNext()) {
+                            CharEntry entry = (CharEntry) entries.next();
+                            int[] countA = (int[]) ccStats.get(entry);
+                            Integer count = new Integer(countA[0]);
+                            // Add to the list for that count
+                            ArrayList list = (ArrayList) tempMap.get(count);
+                            if (list == null) {
+                                // Create list
+                                list = new ArrayList();
+                                tempMap.put(count, list);
+                            }
+                            list.add(entry);
+                        }
+                        // Allocate array of the right size
+                        int size = ccStats.size();
+                        if (size > cacheSize) {
+                            size = cacheSize;
+                        }
+                        CharEntry[] tempccCache = new CharEntry[size];
+                        // Fill it up using an alphabetical order
+                        // and a dumb insert sort
+                        CharChunk tempChunk = new CharChunk();
+                        int n = 0;
+                        while (n < size) {
+                            Object key = tempMap.lastKey();
+                            ArrayList list = (ArrayList) tempMap.get(key);
+                            for (int i = 0; i < list.size() && n < size; i++) {
+                                CharEntry entry = (CharEntry) list.get(i);
+                                tempChunk.setChars(entry.name, 0, entry.name.length);
+                                int insertPos = findClosest(tempChunk, tempccCache, n);
+                                if (insertPos == n) {
+                                    tempccCache[n + 1] = entry;
+                                } else {
+                                    System.arraycopy(tempccCache, insertPos + 1, tempccCache, 
+                                            insertPos + 2, n - insertPos - 1);
+                                    tempccCache[insertPos + 1] = entry;
+                                }
+                                n++;
+                            }
+                            tempMap.remove(key);
+                        }
+                        ccCount = 0;
+                        ccStats.clear();
+                        ccCache = tempccCache;
+                        if (log.isDebugEnabled()) {
+                            long t2 = System.currentTimeMillis();
+                            log.debug("CharCache generation time: " + (t2 - t1) + "ms");
+                        }
+                    } else {
+                        ccCount++;
+                        // Allocate new CharEntry for the lookup
+                        CharEntry entry = new CharEntry();
+                        entry.value = value;
+                        int[] count = (int[]) ccStats.get(entry);
+                        if (count == null) {
+                            int end = cc.getEnd();
+                            int start = cc.getStart();
+                            // Create char array and copy chars
+                            entry.name = new char[cc.getLength()];
+                            System.arraycopy(cc.getBuffer(), start, entry.name, 0, end - start);
+                            // Initialize occurrence count to one 
+                            count = new int[1];
+                            count[0] = 1;
+                            // Set in the stats hash map
+                            ccStats.put(entry, count);
+                        } else {
+                            count[0] = count[0] + 1;
+                        }
+                    }
+                }
+            }
+            return value;
+        } else {
+            accessCount++;
+            // Find the corresponding String
+            String result = find(cc);
+            if (result == null) {
+                return cc.toStringInternal();
+            }
+            // Note: We don't care about safety for the stats
+            hitCount++;
+            return result;
+        }
+        
+    }
+    
+    
+    // ----------------------------------------------------- Protected Methods
+
+
+    /**
+     * Compare given byte chunk with byte array.
+     * Return -1, 0 or +1 if inferior, equal, or superior to the String.
+     */
+    protected static final int compare(ByteChunk name, byte[] compareTo) {
+        int result = 0;
+
+        byte[] b = name.getBuffer();
+        int start = name.getStart();
+        int end = name.getEnd();
+        int len = compareTo.length;
+
+        if ((end - start) < len) {
+            len = end - start;
+        }
+        for (int i = 0; (i < len) && (result == 0); i++) {
+            if (b[i + start] > compareTo[i]) {
+                result = 1;
+            } else if (b[i + start] < compareTo[i]) {
+                result = -1;
+            }
+        }
+        if (result == 0) {
+            if (compareTo.length > (end - start)) {
+                result = -1;
+            } else if (compareTo.length < (end - start)) {
+                result = 1;
+            }
+        }
+        return result;
+    }
+
+    
+    /**
+     * Find an entry given its name in the cache and return the associated String.
+     */
+    protected static final String find(ByteChunk name) {
+        int pos = findClosest(name, bcCache, bcCache.length);
+        if ((pos < 0) || (compare(name, bcCache[pos].name) != 0)
+                || !(name.getEncoding().equals(bcCache[pos].enc))) {
+            return null;
+        } else {
+            return bcCache[pos].value;
+        }
+    }
+
+    
+    /**
+     * Find an entry 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.
+     */
+    protected static final int findClosest(ByteChunk name, ByteEntry[] array, int len) {
+
+        int a = 0;
+        int b = len - 1;
+
+        // Special cases: -1 and 0
+        if (b == -1) {
+            return -1;
+        }
+        
+        if (compare(name, array[0].name) < 0) {
+            return -1;
+        }         
+        if (b == 0) {
+            return 0;
+        }
+
+        int i = 0;
+        while (true) {
+            i = (b + a) / 2;
+            int result = compare(name, array[i].name);
+            if (result == 1) {
+                a = i;
+            } else if (result == 0) {
+                return i;
+            } else {
+                b = i;
+            }
+            if ((b - a) == 1) {
+                int result2 = compare(name, array[b].name);
+                if (result2 < 0) {
+                    return a;
+                } else {
+                    return b;
+                }
+            }
+        }
+
+    }
+
+
+    /**
+     * Compare given char chunk with char array.
+     * Return -1, 0 or +1 if inferior, equal, or superior to the String.
+     */
+    protected static final int compare(CharChunk name, char[] compareTo) {
+        int result = 0;
+
+        char[] c = name.getBuffer();
+        int start = name.getStart();
+        int end = name.getEnd();
+        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[i]) {
+                result = 1;
+            } else if (c[i + start] < compareTo[i]) {
+                result = -1;
+            }
+        }
+        if (result == 0) {
+            if (compareTo.length > (end - start)) {
+                result = -1;
+            } else if (compareTo.length < (end - start)) {
+                result = 1;
+            }
+        }
+        return result;
+    }
+
+    
+    /**
+     * Find an entry given its name in the cache and return the associated String.
+     */
+    protected static final String find(CharChunk name) {
+        int pos = findClosest(name, ccCache, ccCache.length);
+        if ((pos < 0) || (compare(name, ccCache[pos].name) != 0)) {
+            return null;
+        } else {
+            return ccCache[pos].value;
+        }
+    }
+
+    
+    /**
+     * Find an entry 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.
+     */
+    protected static final int findClosest(CharChunk name, CharEntry[] array, int len) {
+
+        int a = 0;
+        int b = len - 1;
+
+        // Special cases: -1 and 0
+        if (b == -1) {
+            return -1;
+        }
+        
+        if (compare(name, array[0].name) < 0 ) {
+            return -1;
+        }         
+        if (b == 0) {
+            return 0;
+        }
+
+        int i = 0;
+        while (true) {
+            i = (b + a) / 2;
+            int result = compare(name, array[i].name);
+            if (result == 1) {
+                a = i;
+            } else if (result == 0) {
+                return i;
+            } else {
+                b = i;
+            }
+            if ((b - a) == 1) {
+                int result2 = compare(name, array[b].name);
+                if (result2 < 0) {
+                    return a;
+                } else {
+                    return b;
+                }
+            }
+        }
+
+    }
+
+
+    // -------------------------------------------------- ByteEntry Inner Class
+
+
+    public static class ByteEntry {
+
+        public byte[] name = null;
+        public String enc = null;
+        public String value = null;
+
+        public String toString() {
+            return value;
+        }
+        public int hashCode() {
+            return value.hashCode();
+        }
+        public boolean equals(Object obj) {
+            if (obj instanceof ByteEntry) {
+                return value.equals(((ByteEntry) obj).value);
+            }
+            return false;
+        }
+        
+    }
+
+
+    // -------------------------------------------------- CharEntry Inner Class
+
+
+    public static class CharEntry {
+
+        public char[] name = null;
+        public String value = null;
+
+        public String toString() {
+            return value;
+        }
+        public int hashCode() {
+            return value.hashCode();
+        }
+        public boolean equals(Object obj) {
+            if (obj instanceof CharEntry) {
+                return value.equals(((CharEntry) obj).value);
+            }
+            return false;
+        }
+        
+    }
+
+
+}
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..a7315d7
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/TimeStamp.java
@@ -0,0 +1,156 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..162b36b
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/UDecoder.java
@@ -0,0 +1,282 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 {
+    
+    protected static final boolean ALLOW_ENCODED_SLASH = Boolean.valueOf(
+            System.getProperty(
+                    "org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH",
+                    "false")).booleanValue();
+    
+    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;
+    
+        boolean noSlash = !(ALLOW_ENCODED_SLASH || query);
+
+        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 );
+                if (noSlash && (res == '/')) {
+                    throw new CharConversionException( "noSlash");
+                }
+                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;
+    
+        boolean noSlash = !(ALLOW_ENCODED_SLASH || query);
+        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 );
+                if (noSlash && (res == '/')) {
+                    throw new CharConversionException( "noSlash");
+                    }
+                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;
+    }
+
+}
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..885db17
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/UEncoder.java
@@ -0,0 +1,178 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 {
+
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog(UEncoder.class );
+    
+    // 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 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 ) {
+        if (log.isDebugEnabled())
+            log.debug("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..858a0fc
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/UTF8Decoder.java
@@ -0,0 +1,150 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 {
+    
+    
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog(UTF8Decoder.class );
+    
+    // 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 ) {
+        if (log.isDebugEnabled())
+            log.debug("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..22eef49
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/collections/EmptyEnumeration.java
@@ -0,0 +1,42 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..052840f
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/collections/LRUCache.java
@@ -0,0 +1,151 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..30449c4
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/collections/MultiMap.java
@@ -0,0 +1,236 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..7d9ef7d
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/collections/MultiMapNamesEnumeration.java
@@ -0,0 +1,81 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..8b035d7
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/collections/MultiMapValuesEnumeration.java
@@ -0,0 +1,64 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..39a87a5
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/collections/Queue.java
@@ -0,0 +1,103 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..d7bd653
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/collections/SimpleHashtable.java
@@ -0,0 +1,324 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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
+{
+    
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( SimpleHashtable.class );
+    
+    // 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 ) {
+	if (log.isDebugEnabled())
+            log.debug( "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..741c542
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/collections/SimplePool.java
@@ -0,0 +1,128 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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  {
+    
+    
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog(SimplePool.class );
+    
+    /*
+     * 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 ) {
+        if (log.isDebugEnabled())
+            log.debug("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..1c78c42
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/compat/Jdk14Compat.java
@@ -0,0 +1,121 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..7a33248
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/compat/JdkCompat.java
@@ -0,0 +1,222 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Vector;
+
+
+/**
+ *  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";
+
+    /** 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..43eaf48
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/AbstractObjectCreationFactory.java
@@ -0,0 +1,79 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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..34b552c
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/AbstractRulesImpl.java
@@ -0,0 +1,167 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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..6830bd0
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/ArrayStack.java
@@ -0,0 +1,169 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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/CallMethodRule.java b/connectors/util/java/org/apache/tomcat/util/digester/CallMethodRule.java
new file mode 100644
index 0000000..3374ae0
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/CallMethodRule.java
@@ -0,0 +1,630 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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..56d1783
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/CallParamRule.java
@@ -0,0 +1,263 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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..b1d4fc0
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/Digester.java
@@ -0,0 +1,2825 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 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();
+        log = null;
+        saxLog = null;
+        configured = false;
+        
+    }
+
+    
+    public void reset() {
+        root = null;
+        setErrorHandler(null);
+        clear();
+    }
+
+
+    /**
+     * 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;
+        }
+
+        log = LogFactory.getLog("org.apache.commons.digester.Digester");
+        saxLog = LogFactory.getLog("org.apache.commons.digester.Digester.sax");
+
+        // 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/FactoryCreateRule.java b/connectors/util/java/org/apache/tomcat/util/digester/FactoryCreateRule.java
new file mode 100644
index 0000000..98fa702
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/FactoryCreateRule.java
@@ -0,0 +1,496 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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..6a1ee3d
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/GenericParser.java
@@ -0,0 +1,87 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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..7f7f682
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/NodeCreateRule.java
@@ -0,0 +1,429 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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..742d616
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/ObjectCreateRule.java
@@ -0,0 +1,242 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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..ced916f
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/ObjectCreationFactory.java
@@ -0,0 +1,60 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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..927200b
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/ObjectParamRule.java
@@ -0,0 +1,125 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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..7760499
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/ParserFeatureSetterFactory.java
@@ -0,0 +1,75 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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..d3f8549
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/PathCallParamRule.java
@@ -0,0 +1,94 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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..c20239c
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/Rule.java
@@ -0,0 +1,245 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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..090c66a
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/RuleSet.java
@@ -0,0 +1,67 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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..1a84b83
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/RuleSetBase.java
@@ -0,0 +1,71 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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..34b2e7e
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/Rules.java
@@ -0,0 +1,128 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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..1ba91e7
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/RulesBase.java
@@ -0,0 +1,294 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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..5406300
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/SetNextRule.java
@@ -0,0 +1,215 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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..f49ddbd
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/SetPropertiesRule.java
@@ -0,0 +1,262 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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..60d8e90
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/SetPropertyRule.java
@@ -0,0 +1,150 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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..b801c50
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/SetRootRule.java
@@ -0,0 +1,216 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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..08500cd
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/SetTopRule.java
@@ -0,0 +1,216 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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..eadfc5c
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/WithDefaultsRulesWrapper.java
@@ -0,0 +1,164 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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..04536d5
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/XercesParser.java
@@ -0,0 +1,190 @@
+/* $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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..061f249
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/http/AcceptLanguage.java
@@ -0,0 +1,148 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..b00f5d3
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/http/BaseRequest.java
@@ -0,0 +1,346 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT 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..504b624
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/http/ContentType.java
@@ -0,0 +1,95 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 {
+
+    /**
+     * Parse the character encoding from the specified content type header.
+     * If the content type is null, or there is no explicit character encoding,
+     * <code>null</code> is returned.
+     *
+     * @param contentType a content type header
+     */
+    public static String getCharsetFromContentType(String contentType) {
+
+        if (contentType == null)
+            return (null);
+        int start = contentType.indexOf("charset=");
+        if (start < 0)
+            return (null);
+        String encoding = contentType.substring(start + 8);
+        int end = encoding.indexOf(';');
+        if (end >= 0)
+            encoding = encoding.substring(0, end);
+        encoding = encoding.trim();
+        if ((encoding.length() > 2) && (encoding.startsWith("\""))
+            && (encoding.endsWith("\"")))
+            encoding = encoding.substring(1, encoding.length() - 1);
+        return (encoding.trim());
+
+    }
+    
+    /**
+     * 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..a6d093b
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/http/Cookies.java
@@ -0,0 +1,482 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 {
+
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog(Cookies.class );
+    
+    // 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;
+            
+            cc=bytes[pos];
+            if(  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 ) {
+        if (log.isDebugEnabled())
+            log.debug("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..c7d399c
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/http/FastHttpDateFormat.java
@@ -0,0 +1,224 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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) {
+                synchronized (format) {
+                    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..fc519a9
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/http/HttpMessages.java
@@ -0,0 +1,107 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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/MimeHeaders.java b/connectors/util/java/org/apache/tomcat/util/http/MimeHeaders.java
new file mode 100644
index 0000000..0640cd5
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/http/MimeHeaders.java
@@ -0,0 +1,476 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 ) {
+        for ( int i = 0; i < count; i++ ) {
+            if(headers[i].getName().equalsIgnoreCase(name)) {
+                for ( int j=i+1; j < count; j++ ) {
+                    if(headers[j].getName().equalsIgnoreCase(name)) {
+                        removeHeader(j--);
+                    }
+                }
+                return headers[i].getValue();
+            }
+        }
+        MimeHeaderField mh = createHeader();
+        mh.getName().setString(name);
+        return mh.getValue();
+    }
+
+    //-------------------- 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;
+    }
+
+    /**
+     * Finds and returns a unique header field with the given name. If no such
+     * field exists, null is returned. If the specified header field is not
+     * unique then an {@link IllegalArgumentException} is thrown. 
+     */
+    public MessageBytes getUniqueValue(String name) {
+        MessageBytes result = null;
+        for (int i = 0; i < count; i++) {
+            if (headers[i].getName().equalsIgnoreCase(name)) {
+                if (result == null) {
+                    result = headers[i].getValue();
+                } else {
+                    throw new IllegalArgumentException();
+                }
+            }
+        }
+        return result;
+    }
+
+    // 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)) {
+                removeHeader(i--);
+            }
+        }
+    }
+
+    /**
+     * reset and swap with last header
+     * @param idx the index of the header to remove.
+     */
+    private void removeHeader(int idx) {
+        MimeHeaderField mh = headers[idx];
+        
+        mh.recycle();
+        headers[idx] = headers[count - 1];
+        headers[count - 1] = mh;
+        count--;
+    }
+
+}
+
+/** 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..e377f38
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/http/MimeMap.java
@@ -0,0 +1,188 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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("png", "image/png");
+		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..2ca5db0
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/http/Parameters.java
@@ -0,0 +1,617 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 {
+
+    
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog(Parameters.class );
+    
+    // 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 ) {
+                log.warn("Parameters: Invalid chunk ignored.");
+                continue;
+                // invalid chunk - it's better to ignore
+            }
+            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
+                log.warn("Parameters: Character decoding failed. " + 
+                        "Parameter skipped.", e);
+            }
+
+            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 ) {
+        if (log.isDebugEnabled())
+            log.debug("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..b9c9240
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/http/ServerCookie.java
@@ -0,0 +1,321 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog(ServerCookie.class );
+    
+    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 {
+            buf.append('"');
+            buf.append(escapeDoubleQuotes(value));
+            buf.append('"');
+        }
+    }
+
+    // log
+    static final int dbg=1;
+    public static void log(String s ) {
+        if (log.isDebugEnabled())
+            log.debug("ServerCookie: " + s);
+    }
+
+
+    /**
+     * Escapes any double quotes in the given string.
+     *
+     * @param s the input string
+     *
+     * @return The (possibly) escaped string
+     */
+    private static String escapeDoubleQuotes(String s) {
+
+        if (s == null || s.length() == 0 || s.indexOf('"') == -1) {
+            return s;
+        }
+
+        StringBuffer b = new StringBuffer();
+        for (int i = 0; i < s.length(); i++) {
+            char c = s.charAt(i);
+            if (c == '"')
+                b.append('\\').append('"');
+            else
+                b.append(c);
+        }
+
+        return b.toString();
+    }
+
+}
+
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..dfed673
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/http/mapper/Mapper.java
@@ -0,0 +1,1397 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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)) {
+            int slashCount = slashCount(path);
+            synchronized (host) {
+                Context[] contexts = host.contextList.contexts;
+                // Update nesting
+                if (slashCount > host.contextList.nesting) {
+                    host.contextList.nesting = slashCount;
+                }
+                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)) {
+            synchronized (host) {
+                Context[] contexts = host.contextList.contexts;
+                if( contexts.length == 0 ){
+                    return;
+                }
+                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 {
+
+        if (host.isNull()) {
+            host.getCharChunk().append(defaultHostName);
+        }
+        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..2d76485
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/http/mapper/MappingData.java
@@ -0,0 +1,53 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..5e5be43
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/log/CaptureLog.java
@@ -0,0 +1,50 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..4464487
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/log/SystemLogHandler.java
@@ -0,0 +1,245 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.EmptyStackException;
+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 ThreadLocal logs = new ThreadLocal();
+
+
+    /**
+     * 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;
+        if (!reuse.isEmpty()) {
+            try {
+                log = (CaptureLog)reuse.pop();
+            } catch (EmptyStackException e) {
+                log = new CaptureLog();
+            }
+        } else {
+            log = new CaptureLog();
+        }
+        Stack stack = (Stack)logs.get();
+        if (stack == null) {
+            stack = new Stack();
+            logs.set(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();
+        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();
+        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/AprEndpoint.java b/connectors/util/java/org/apache/tomcat/util/net/AprEndpoint.java
new file mode 100644
index 0000000..e4f998d
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/AprEndpoint.java
@@ -0,0 +1,1655 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.InetAddress;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.tomcat.jni.OS;
+import org.apache.tomcat.jni.Address;
+import org.apache.tomcat.jni.Error;
+import org.apache.tomcat.jni.File;
+import org.apache.tomcat.jni.Library;
+import org.apache.tomcat.jni.Poll;
+import org.apache.tomcat.jni.Pool;
+import org.apache.tomcat.jni.Socket;
+import org.apache.tomcat.jni.Status;
+import org.apache.tomcat.jni.SSL;
+import org.apache.tomcat.jni.SSLContext;
+import org.apache.tomcat.jni.SSLSocket;
+import org.apache.tomcat.util.res.StringManager;
+import org.apache.tomcat.util.threads.ThreadWithAttributes;
+
+/**
+ * APR tailored thread pool, providing the following services:
+ * <ul>
+ * <li>Socket acceptor thread</li>
+ * <li>Socket poller thread</li>
+ * <li>Sendfile thread</li>
+ * <li>Worker threads pool</li>
+ * </ul>
+ *
+ * When switching to Java 5, there's an opportunity to use the virtual
+ * machine's thread pool.
+ *
+ * @author Mladen Turk
+ * @author Remy Maucherat
+ */
+public class AprEndpoint {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    protected static Log log = LogFactory.getLog(AprEndpoint.class);
+
+    protected static StringManager sm =
+        StringManager.getManager("org.apache.tomcat.util.net.res");
+
+
+    /**
+     * 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";
+
+
+    // ----------------------------------------------------------------- Fields
+
+
+    /**
+     * Available workers.
+     */
+    protected WorkerStack workers = null;
+
+
+    /**
+     * Running state of the endpoint.
+     */
+    protected volatile boolean running = false;
+
+
+    /**
+     * Will be set to true whenever the endpoint is paused.
+     */
+    protected volatile boolean paused = false;
+
+
+    /**
+     * Track the initialization state of the endpoint.
+     */
+    protected boolean initialized = false;
+
+
+    /**
+     * Current worker threads busy count.
+     */
+    protected int curThreadsBusy = 0;
+
+
+    /**
+     * Current worker threads count.
+     */
+    protected int curThreads = 0;
+
+
+    /**
+     * Sequence number used to generate thread names.
+     */
+    protected int sequence = 0;
+
+
+    /**
+     * Root APR memory pool.
+     */
+    protected long rootPool = 0;
+
+
+    /**
+     * Server socket "pointer".
+     */
+    protected long serverSock = 0;
+
+
+    /**
+     * APR memory pool for the server socket.
+     */
+    protected long serverSockPool = 0;
+
+
+    /**
+     * SSL context.
+     */
+    protected long sslContext = 0;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Maximum amount of worker threads.
+     */
+    protected int maxThreads = 40;
+    public void setMaxThreads(int maxThreads) { this.maxThreads = maxThreads; }
+    public int getMaxThreads() { return maxThreads; }
+
+
+    /**
+     * Priority of the acceptor and poller threads.
+     */
+    protected int threadPriority = Thread.NORM_PRIORITY;
+    public void setThreadPriority(int threadPriority) { this.threadPriority = threadPriority; }
+    public int getThreadPriority() { return threadPriority; }
+
+
+    /**
+     * Size of the socket poller.
+     */
+    protected int pollerSize = 8 * 1024;
+    public void setPollerSize(int pollerSize) { this.pollerSize = pollerSize; }
+    public int getPollerSize() { return pollerSize; }
+
+
+    /**
+     * Size of the sendfile (= concurrent files which can be served).
+     */
+    protected int sendfileSize = 1 * 1024;
+    public void setSendfileSize(int sendfileSize) { this.sendfileSize = sendfileSize; }
+    public int getSendfileSize() { return sendfileSize; }
+
+
+    /**
+     * Server socket port.
+     */
+    protected int port;
+    public int getPort() { return port; }
+    public void setPort(int port ) { this.port=port; }
+
+
+    /**
+     * Address for the server socket.
+     */
+    protected InetAddress address;
+    public InetAddress getAddress() { return address; }
+    public void setAddress(InetAddress address) { this.address = address; }
+
+
+    /**
+     * Handling of accepted sockets.
+     */
+    protected Handler handler = null;
+    public void setHandler(Handler handler ) { this.handler = handler; }
+    public Handler getHandler() { return handler; }
+
+
+    /**
+     * Allows the server developer to specify the backlog that
+     * should be used for server sockets. By default, this value
+     * is 100.
+     */
+    protected int backlog = 100;
+    public void setBacklog(int backlog) { if (backlog > 0) this.backlog = backlog; }
+    public int getBacklog() { return backlog; }
+
+
+    /**
+     * Socket TCP no delay.
+     */
+    protected boolean tcpNoDelay = false;
+    public boolean getTcpNoDelay() { return tcpNoDelay; }
+    public void setTcpNoDelay(boolean tcpNoDelay) { this.tcpNoDelay = tcpNoDelay; }
+
+
+    /**
+     * Socket linger.
+     */
+    protected int soLinger = 100;
+    public int getSoLinger() { return soLinger; }
+    public void setSoLinger(int soLinger) { this.soLinger = soLinger; }
+
+
+    /**
+     * Socket timeout.
+     */
+    protected int soTimeout = -1;
+    public int getSoTimeout() { return soTimeout; }
+    public void setSoTimeout(int soTimeout) { this.soTimeout = soTimeout; }
+
+
+    /**
+     * Timeout on first request read before going to the poller, in ms.
+     */
+    protected int firstReadTimeout = -1;
+    public int getFirstReadTimeout() { return firstReadTimeout; }
+    public void setFirstReadTimeout(int firstReadTimeout) { this.firstReadTimeout = firstReadTimeout; }
+
+
+    /**
+     * Poll interval, in microseconds. The smaller the value, the more CPU the poller
+     * will use, but the more responsive to activity it will be.
+     */
+    protected int pollTime = 2000;
+    public int getPollTime() { return pollTime; }
+    public void setPollTime(int pollTime) { if (pollTime > 0) { this.pollTime = pollTime; } }
+
+
+    /**
+     * 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.
+     */
+    protected boolean daemon = true;
+    public void setDaemon(boolean b) { daemon = b; }
+    public boolean getDaemon() { return daemon; }
+
+
+    /**
+     * Name of the thread pool, which will be used for naming child threads.
+     */
+    protected String name = "TP";
+    public void setName(String name) { this.name = name; }
+    public String getName() { return name; }
+
+
+    /**
+     * Use endfile for sending static files.
+     */
+    protected boolean useSendfile = Library.APR_HAS_SENDFILE;
+    public void setUseSendfile(boolean useSendfile) { this.useSendfile = useSendfile; }
+    public boolean getUseSendfile() { return useSendfile; }
+
+
+    /**
+     * Acceptor thread count.
+     */
+    protected int acceptorThreadCount = 0;
+    public void setAcceptorThreadCount(int acceptorThreadCount) { this.acceptorThreadCount = acceptorThreadCount; }
+    public int getAcceptorThreadCount() { return acceptorThreadCount; }
+
+
+    /**
+     * Sendfile thread count.
+     */
+    protected int sendfileThreadCount = 0;
+    public void setSendfileThreadCount(int sendfileThreadCount) { this.sendfileThreadCount = sendfileThreadCount; }
+    public int getSendfileThreadCount() { return sendfileThreadCount; }
+
+
+    /**
+     * Poller thread count.
+     */
+    protected int pollerThreadCount = 0;
+    public void setPollerThreadCount(int pollerThreadCount) { this.pollerThreadCount = pollerThreadCount; }
+    public int getPollerThreadCount() { return pollerThreadCount; }
+
+
+    /**
+     * The socket poller.
+     */
+    protected Poller[] pollers = null;
+    protected int pollerRoundRobin = 0;
+    public Poller getPoller() {
+        pollerRoundRobin = (pollerRoundRobin + 1) % pollers.length;
+        return pollers[pollerRoundRobin];
+    }
+
+
+    /**
+     * The static file sender.
+     */
+    protected Sendfile[] sendfiles = null;
+    protected int sendfileRoundRobin = 0;
+    public Sendfile getSendfile() {
+        sendfileRoundRobin = (sendfileRoundRobin + 1) % sendfiles.length;
+        return sendfiles[sendfileRoundRobin];
+    }
+
+
+    /**
+     * Dummy maxSpareThreads property.
+     */
+    public int getMaxSpareThreads() { return 0; }
+
+
+    /**
+     * Dummy minSpareThreads property.
+     */
+    public int getMinSpareThreads() { return 0; }
+
+
+    /**
+     * SSL engine.
+     */
+    protected String SSLEngine = "off";
+    public String getSSLEngine() { return SSLEngine; }
+    public void setSSLEngine(String SSLEngine) { this.SSLEngine = SSLEngine; }
+
+
+    /**
+     * SSL protocols.
+     */
+    protected String SSLProtocol = "all";
+    public String getSSLProtocol() { return SSLProtocol; }
+    public void setSSLProtocol(String SSLProtocol) { this.SSLProtocol = SSLProtocol; }
+
+
+    /**
+     * SSL password (if a cert is encrypted, and no password has been provided, a callback
+     * will ask for a password).
+     */
+    protected String SSLPassword = null;
+    public String getSSLPassword() { return SSLPassword; }
+    public void setSSLPassword(String SSLPassword) { this.SSLPassword = SSLPassword; }
+
+
+    /**
+     * SSL cipher suite.
+     */
+    protected String SSLCipherSuite = "ALL";
+    public String getSSLCipherSuite() { return SSLCipherSuite; }
+    public void setSSLCipherSuite(String SSLCipherSuite) { this.SSLCipherSuite = SSLCipherSuite; }
+
+
+    /**
+     * SSL certificate file.
+     */
+    protected String SSLCertificateFile = null;
+    public String getSSLCertificateFile() { return SSLCertificateFile; }
+    public void setSSLCertificateFile(String SSLCertificateFile) { this.SSLCertificateFile = SSLCertificateFile; }
+
+
+    /**
+     * SSL certificate key file.
+     */
+    protected String SSLCertificateKeyFile = null;
+    public String getSSLCertificateKeyFile() { return SSLCertificateKeyFile; }
+    public void setSSLCertificateKeyFile(String SSLCertificateKeyFile) { this.SSLCertificateKeyFile = SSLCertificateKeyFile; }
+
+
+    /**
+     * SSL certificate chain file.
+     */
+    protected String SSLCertificateChainFile = null;
+    public String getSSLCertificateChainFile() { return SSLCertificateChainFile; }
+    public void setSSLCertificateChainFile(String SSLCertificateChainFile) { this.SSLCertificateChainFile = SSLCertificateChainFile; }
+
+
+    /**
+     * SSL CA certificate path.
+     */
+    protected String SSLCACertificatePath = null;
+    public String getSSLCACertificatePath() { return SSLCACertificatePath; }
+    public void setSSLCACertificatePath(String SSLCACertificatePath) { this.SSLCACertificatePath = SSLCACertificatePath; }
+
+
+    /**
+     * SSL CA certificate file.
+     */
+    protected String SSLCACertificateFile = null;
+    public String getSSLCACertificateFile() { return SSLCACertificateFile; }
+    public void setSSLCACertificateFile(String SSLCACertificateFile) { this.SSLCACertificateFile = SSLCACertificateFile; }
+
+
+    /**
+     * SSL CA revocation path.
+     */
+    protected String SSLCARevocationPath = null;
+    public String getSSLCARevocationPath() { return SSLCARevocationPath; }
+    public void setSSLCARevocationPath(String SSLCARevocationPath) { this.SSLCARevocationPath = SSLCARevocationPath; }
+
+
+    /**
+     * SSL CA revocation file.
+     */
+    protected String SSLCARevocationFile = null;
+    public String getSSLCARevocationFile() { return SSLCARevocationFile; }
+    public void setSSLCARevocationFile(String SSLCARevocationFile) { this.SSLCARevocationFile = SSLCARevocationFile; }
+
+
+    /**
+     * SSL verify client.
+     */
+    protected String SSLVerifyClient = "none";
+    public String getSSLVerifyClient() { return SSLVerifyClient; }
+    public void setSSLVerifyClient(String SSLVerifyClient) { this.SSLVerifyClient = SSLVerifyClient; }
+
+
+    /**
+     * SSL verify depth.
+     */
+    protected int SSLVerifyDepth = 10;
+    public int getSSLVerifyDepth() { return SSLVerifyDepth; }
+    public void setSSLVerifyDepth(int SSLVerifyDepth) { this.SSLVerifyDepth = SSLVerifyDepth; }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Number of keepalive sockets.
+     */
+    public int getKeepAliveCount() {
+        if (pollers == null) {
+            return 0;
+        } else {
+            int keepAliveCount = 0;
+            for (int i = 0; i < pollers.length; i++) {
+                keepAliveCount += pollers[i].getKeepAliveCount();
+            }
+            return keepAliveCount;
+        }
+    }
+
+
+    /**
+     * Number of sendfile sockets.
+     */
+    public int getSendfileCount() {
+        if (sendfiles == null) {
+            return 0;
+        } else {
+            int sendfileCount = 0;
+            for (int i = 0; i < sendfiles.length; i++) {
+                sendfileCount += sendfiles[i].getSendfileCount();
+            }
+            return sendfileCount;
+        }
+    }
+
+
+    /**
+     * Return the amount of threads that are managed by the pool.
+     *
+     * @return the amount of threads that are managed by the pool
+     */
+    public int getCurrentThreadCount() {
+        return curThreads;
+    }
+
+
+    /**
+     * Return the amount of threads currently busy.
+     *
+     * @return the amount of threads currently busy
+     */
+    public int getCurrentThreadsBusy() {
+        return curThreadsBusy;
+    }
+
+
+    /**
+     * Return the state of the endpoint.
+     *
+     * @return true if the endpoint is running, false otherwise
+     */
+    public boolean isRunning() {
+        return running;
+    }
+
+
+    /**
+     * Return the state of the endpoint.
+     *
+     * @return true if the endpoint is paused, false otherwise
+     */
+    public boolean isPaused() {
+        return paused;
+    }
+
+
+    // ----------------------------------------------- Public Lifecycle Methods
+
+
+    /**
+     * Initialize the endpoint.
+     */
+    public void init()
+        throws Exception {
+
+        if (initialized)
+            return;
+        
+        // Create the root APR memory pool
+        rootPool = Pool.create(0);
+        // Create the pool for the server socket
+        serverSockPool = Pool.create(rootPool);
+        // Create the APR address that will be bound
+        String addressStr = null;
+        if (address == null) {
+            addressStr = null;
+        } else {
+            addressStr = address.getHostAddress();
+        }
+        int family = Socket.APR_INET;
+        if (Library.APR_HAVE_IPV6) {
+            if (addressStr == null)
+                family = Socket.APR_UNSPEC;
+            else if (addressStr.indexOf(':') >= 0)
+                family = Socket.APR_UNSPEC;
+        }
+        long inetAddress = Address.info(addressStr, family,
+                port, 0, rootPool);
+        // Create the APR server socket
+        serverSock = Socket.create(family, Socket.SOCK_STREAM,
+                Socket.APR_PROTO_TCP, rootPool);
+        if (OS.IS_UNIX) {
+            Socket.optSet(serverSock, Socket.APR_SO_REUSEADDR, 1);
+        }
+        // Deal with the firewalls that tend to drop the inactive sockets
+        Socket.optSet(serverSock, Socket.APR_SO_KEEPALIVE, 1);
+        // Bind the server socket
+        int ret = Socket.bind(serverSock, inetAddress);
+        if (ret != 0) {
+            throw new Exception(sm.getString("endpoint.init.bind", "" + ret, Error.strerror(ret)));
+        }
+        // Start listening on the server socket
+        ret = Socket.listen(serverSock, backlog);
+        if (ret != 0) {
+            throw new Exception(sm.getString("endpoint.init.listen", "" + ret, Error.strerror(ret)));
+        }
+        if (OS.IS_WIN32 || OS.IS_WIN64) {
+            // On Windows set the reuseaddr flag after the bind/listen
+            Socket.optSet(serverSock, Socket.APR_SO_REUSEADDR, 1);
+        }
+
+        // Sendfile usage on systems which don't support it cause major problems
+        if (useSendfile && !Library.APR_HAS_SENDFILE) {
+            log.warn(sm.getString("endpoint.sendfile.nosupport"));
+            useSendfile = false;
+        }
+
+        // Initialize thread count defaults for acceptor, poller and sendfile
+        if (acceptorThreadCount == 0) {
+            // FIXME: Doesn't seem to work that well with multiple accept threads
+            acceptorThreadCount = 1;
+        }
+        if (pollerThreadCount == 0) {
+            if ((OS.IS_WIN32 || OS.IS_WIN64) && (pollerSize > 1024)) {
+                // The maximum per poller to get reasonable performance is 1024
+                pollerThreadCount = pollerSize / 1024;
+                // Adjust poller size so that it won't reach the limit
+                pollerSize = pollerSize - (pollerSize % 1024);
+            } else {
+                // No explicit poller size limitation
+                pollerThreadCount = 1;
+            }
+        }
+        if (sendfileThreadCount == 0) {
+            if ((OS.IS_WIN32 || OS.IS_WIN64) && (sendfileSize > 1024)) {
+                // The maximum per poller to get reasonable performance is 1024
+                sendfileThreadCount = sendfileSize / 1024;
+                // Adjust poller size so that it won't reach the limit
+                sendfileSize = sendfileSize - (sendfileSize % 1024);
+            } else {
+                // No explicit poller size limitation
+                // FIXME: Default to one per CPU ?
+                sendfileThreadCount = 1;
+            }
+        }
+        
+        // Delay accepting of new connections until data is available
+        // Only Linux kernels 2.4 + have that implemented
+        // on other platforms this call is noop and will return APR_ENOTIMPL.
+        Socket.optSet(serverSock, Socket.APR_TCP_DEFER_ACCEPT, 1);
+
+        // Initialize SSL if needed
+        if (!"off".equalsIgnoreCase(SSLEngine)) {
+            // Initialize SSL
+            // FIXME: one per VM call ?
+            if ("on".equalsIgnoreCase(SSLEngine)) {
+                SSL.initialize(null);
+            } else {
+                SSL.initialize(SSLEngine);
+            }
+            // SSL protocol
+            int value = SSL.SSL_PROTOCOL_ALL;
+            if ("SSLv2".equalsIgnoreCase(SSLProtocol)) {
+                value = SSL.SSL_PROTOCOL_SSLV2;
+            } else if ("SSLv3".equalsIgnoreCase(SSLProtocol)) {
+                value = SSL.SSL_PROTOCOL_SSLV3;
+            } else if ("TLSv1".equalsIgnoreCase(SSLProtocol)) {
+                value = SSL.SSL_PROTOCOL_TLSV1;
+            } else if ("SSLv2+SSLv3".equalsIgnoreCase(SSLProtocol)) {
+                value = SSL.SSL_PROTOCOL_SSLV2 | SSL.SSL_PROTOCOL_SSLV3;
+            }
+            // Create SSL Context
+            sslContext = SSLContext.make(rootPool, value, SSL.SSL_MODE_SERVER);
+            // List the ciphers that the client is permitted to negotiate
+            SSLContext.setCipherSuite(sslContext, SSLCipherSuite);
+            // Load Server key and certificate
+            SSLContext.setCertificate(sslContext, SSLCertificateFile, SSLCertificateKeyFile, SSLPassword, SSL.SSL_AIDX_RSA);
+            // Set certificate chain file
+            SSLContext.setCertificateChainFile(sslContext, SSLCertificateChainFile, false);
+            // Support Client Certificates
+            SSLContext.setCACertificate(sslContext, SSLCACertificateFile, SSLCACertificatePath);
+            // Set revocation
+            SSLContext.setCARevocation(sslContext, SSLCARevocationFile, SSLCARevocationPath);
+            // Client certificate verification
+            value = SSL.SSL_CVERIFY_NONE;
+            if ("optional".equalsIgnoreCase(SSLVerifyClient)) {
+                value = SSL.SSL_CVERIFY_OPTIONAL;
+            } else if ("require".equalsIgnoreCase(SSLVerifyClient)) {
+                value = SSL.SSL_CVERIFY_REQUIRE;
+            } else if ("optionalNoCA".equalsIgnoreCase(SSLVerifyClient)) {
+                value = SSL.SSL_CVERIFY_OPTIONAL_NO_CA;
+            }
+            SSLContext.setVerify(sslContext, value, SSLVerifyDepth);
+            // For now, sendfile is not supported with SSL
+            useSendfile = false;
+        }
+
+        initialized = true;
+
+    }
+
+
+    /**
+     * Start the APR endpoint, creating acceptor, poller and sendfile threads.
+     */
+    public void start()
+        throws Exception {
+        // Initialize socket if not done before
+        if (!initialized) {
+            init();
+        }
+        if (!running) {
+            running = true;
+            paused = false;
+
+            // Create worker collection
+            workers = new WorkerStack(maxThreads);
+
+            // Start acceptor thread
+            for (int i = 0; i < acceptorThreadCount; i++) {
+                Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);
+                acceptorThread.setPriority(threadPriority);
+                acceptorThread.setDaemon(daemon);
+                acceptorThread.start();
+            }
+
+            // Start poller thread
+            pollers = new Poller[pollerThreadCount];
+            for (int i = 0; i < pollerThreadCount; i++) {
+                pollers[i] = new Poller();
+                pollers[i].init();
+                Thread pollerThread = new Thread(pollers[i], getName() + "-Poller-" + i);
+                pollerThread.setPriority(threadPriority);
+                pollerThread.setDaemon(true);
+                pollerThread.start();
+            }
+
+            // Start sendfile thread
+            if (useSendfile) {
+                sendfiles = new Sendfile[sendfileThreadCount];
+                for (int i = 0; i < sendfileThreadCount; i++) {
+                    sendfiles[i] = new Sendfile();
+                    sendfiles[i].init();
+                    Thread sendfileThread = new Thread(sendfiles[i], getName() + "-Sendfile-" + i);
+                    sendfileThread.setPriority(threadPriority);
+                    sendfileThread.setDaemon(true);
+                    sendfileThread.start();
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Pause the endpoint, which will make it stop accepting new sockets.
+     */
+    public void pause() {
+        if (running && !paused) {
+            paused = true;
+            unlockAccept();
+        }
+    }
+
+
+    /**
+     * Resume the endpoint, which will make it start accepting new sockets
+     * again.
+     */
+    public void resume() {
+        if (running) {
+            paused = false;
+        }
+    }
+
+
+    /**
+     * Stop the endpoint. This will cause all processing threads to stop.
+     */
+    public void stop() {
+        if (running) {
+            running = false;
+            unlockAccept();
+            for (int i = 0; i < pollers.length; i++) {
+                pollers[i].destroy();
+            }
+            pollers = null;
+            if (useSendfile) {
+                for (int i = 0; i < sendfiles.length; i++) {
+                    sendfiles[i].destroy();
+                }
+                sendfiles = null;
+            }
+        }
+    }
+
+
+    /**
+     * Deallocate APR memory pools, and close server socket.
+     */
+    public void destroy() throws Exception {
+        if (running) {
+            stop();
+        }
+        Pool.destroy(serverSockPool);
+        serverSockPool = 0;
+        // Close server socket
+        Socket.close(serverSock);
+        serverSock = 0;
+        sslContext = 0;
+        // Close all APR memory pools and resources
+        Pool.destroy(rootPool);
+        rootPool = 0;
+        initialized = false ;
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Get a sequence number used for thread naming.
+     */
+    protected int getSequence() {
+        return sequence++;
+    }
+
+
+    /**
+     * Unlock the server socket accept using a bugus connection.
+     */
+    protected void unlockAccept() {
+        java.net.Socket s = null;
+        try {
+            // Need to create a connection to unlock the accept();
+            if (address == null) {
+                s = new java.net.Socket("127.0.0.1", port);
+            } else {
+                s = new java.net.Socket(address, port);
+                // setting soLinger to a small value will help shutdown the
+                // connection quicker
+                s.setSoLinger(true, 0);
+            }
+        } catch(Exception e) {
+            if (log.isDebugEnabled()) {
+                log.debug(sm.getString("endpoint.debug.unlock", "" + port), e);
+            }
+        } finally {
+            if (s != null) {
+                try {
+                    s.close();
+                } catch (Exception e) {
+                    // Ignore
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Process the specified connection.
+     */
+    protected boolean setSocketOptions(long socket) {
+        // Process the connection
+        int step = 1;
+        try {
+
+            // 1: Set socket options: timeout, linger, etc
+            if (soLinger >= 0)
+                Socket.optSet(socket, Socket.APR_SO_LINGER, soLinger);
+            if (tcpNoDelay)
+                Socket.optSet(socket, Socket.APR_TCP_NODELAY, (tcpNoDelay ? 1 : 0));
+            if (soTimeout > 0)
+                Socket.timeoutSet(socket, soTimeout * 1000);
+
+            // 2: SSL handshake
+            step = 2;
+            if (sslContext != 0) {
+                SSLSocket.attach(sslContext, socket);
+                if (SSLSocket.handshake(socket) != 0) {
+                    if (log.isDebugEnabled()) {
+                        log.debug(sm.getString("endpoint.err.handshake") + ": " + SSL.getLastError());
+                    }
+                    return false;
+                }
+            }
+
+        } catch (Throwable t) {
+            if (log.isDebugEnabled()) {
+                if (step == 2) {
+                    log.debug(sm.getString("endpoint.err.handshake"), t);
+                } else {
+                    log.debug(sm.getString("endpoint.err.unexpected"), t);
+                }
+            }
+            // Tell to close the socket
+            return false;
+        }
+        return true;
+    }
+
+
+    /**
+     * Create (or allocate) and return an available processor for use in
+     * processing a specific HTTP request, if possible.  If the maximum
+     * allowed processors have already been created and are in use, return
+     * <code>null</code> instead.
+     */
+    protected Worker createWorkerThread() {
+
+        synchronized (workers) {
+            if (workers.size() > 0) {
+                curThreadsBusy++;
+                return (workers.pop());
+            }
+            if ((maxThreads > 0) && (curThreads < maxThreads)) {
+                curThreadsBusy++;
+                return (newWorkerThread());
+            } else {
+                if (maxThreads < 0) {
+                    curThreadsBusy++;
+                    return (newWorkerThread());
+                } else {
+                    return (null);
+                }
+            }
+        }
+
+    }
+
+
+    /**
+     * Create and return a new processor suitable for processing HTTP
+     * requests and returning the corresponding responses.
+     */
+    protected Worker newWorkerThread() {
+
+        Worker workerThread = new Worker();
+        workerThread.start();
+        return (workerThread);
+
+    }
+
+
+    /**
+     * Return a new worker thread, and block while to worker is available.
+     */
+    protected Worker getWorkerThread() {
+        // Allocate a new worker thread
+        Worker workerThread = createWorkerThread();
+        while (workerThread == null) {
+            try {
+                synchronized (workers) {
+                    workers.wait();
+                }
+            } catch (InterruptedException e) {
+                // Ignore
+            }
+            workerThread = createWorkerThread();
+        }
+        return workerThread;
+    }
+
+
+    /**
+     * Recycle the specified Processor so that it can be used again.
+     *
+     * @param workerThread The processor to be recycled
+     */
+    protected void recycleWorkerThread(Worker workerThread) {
+        synchronized (workers) {
+            workers.push(workerThread);
+            curThreadsBusy--;
+            workers.notify();
+        }
+    }
+
+    
+    /**
+     * Allocate a new poller of the specified size.
+     */
+    protected long allocatePoller(int size, long pool, int timeout) {
+        try {
+            return Poll.create(size, pool, 0, timeout * 1000);
+        } catch (Error e) {
+            if (Status.APR_STATUS_IS_EINVAL(e.getError())) {
+                log.info(sm.getString("endpoint.poll.limitedpollsize", "" + size));
+                return 0;
+            } else {
+                log.error(sm.getString("endpoint.poll.initfail"), e);
+                return -1;
+            }
+        }
+    }
+    
+    
+
+    // --------------------------------------------------- Acceptor Inner Class
+
+
+    /**
+     * Server socket acceptor thread.
+     */
+    protected class Acceptor implements Runnable {
+
+
+        /**
+         * 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 (running) {
+
+                // Loop if endpoint is paused
+                while (paused) {
+                    try {
+                        Thread.sleep(1000);
+                    } catch (InterruptedException e) {
+                        // Ignore
+                    }
+                }
+
+                try {
+                    // Accept the next incoming connection from the server socket
+                    long socket = Socket.accept(serverSock);
+                    // Allocate a new worker thread
+                    // Hand this socket off to an appropriate processor
+                    getWorkerThread().assign(socket, true);
+                } catch (Throwable t) {
+                    log.error(sm.getString("endpoint.accept.fail"), t);
+                }
+
+                // The processor will recycle itself when it finishes
+
+            }
+
+        }
+
+    }
+
+
+    // ----------------------------------------------------- Poller Inner Class
+
+
+    /**
+     * Poller class.
+     */
+    public class Poller implements Runnable {
+
+        protected long serverPollset = 0;
+        protected long pool = 0;
+        protected long[] desc;
+
+        protected long[] addS;
+        protected int addCount = 0;
+        
+        protected int keepAliveCount = 0;
+        public int getKeepAliveCount() { return keepAliveCount; }
+
+        /**
+         * Create the poller. With some versions of APR, the maximum poller size will
+         * be 62 (reocmpiling APR is necessary to remove this limitation).
+         */
+        protected void init() {
+            pool = Pool.create(serverSockPool);
+            int size = pollerSize / pollerThreadCount;
+            serverPollset = allocatePoller(size, pool, soTimeout);
+            if (serverPollset == 0 && size > 1024) {
+                size = 1024;
+                serverPollset = allocatePoller(size, pool, soTimeout);
+            }
+            if (serverPollset == 0) {
+                size = 62;
+                serverPollset = allocatePoller(size, pool, soTimeout);
+            }
+            desc = new long[size * 2];
+            keepAliveCount = 0;
+            addS = new long[size];
+            addCount = 0;
+        }
+
+        /**
+         * Destroy the poller.
+         */
+        protected void destroy() {
+            // Close all sockets in the add queue
+            for (int i = 0; i < addCount; i++) {
+                Socket.destroy(addS[i]);
+            }
+            // Close all sockets still in the poller
+            int rv = Poll.pollset(serverPollset, desc);
+            if (rv > 0) {
+                for (int n = 0; n < rv; n++) {
+                    Socket.destroy(desc[n*2+1]);
+                }
+            }
+            Pool.destroy(pool);
+            keepAliveCount = 0;
+            addCount = 0;
+        }
+
+        /**
+         * Add specified socket and associated pool to the poller. The socket will
+         * be added to a temporary array, and polled first after a maximum amount
+         * of time equal to pollTime (in most cases, latency will be much lower,
+         * however).
+         *
+         * @param socket to add to the poller
+         */
+        public void add(long socket) {
+            synchronized (this) {
+                // Add socket to the list. Newly added sockets will wait
+                // at most for pollTime before being polled
+                if (addCount >= addS.length) {
+                    // Can't do anything: close the socket right away
+                    Socket.destroy(socket);
+                    return;
+                }
+                addS[addCount] = socket;
+                addCount++;
+                this.notify();
+            }
+        }
+
+        /**
+         * The background thread that listens for incoming TCP/IP connections and
+         * hands them off to an appropriate processor.
+         */
+        public void run() {
+
+            long maintainTime = 0;
+            // Loop until we receive a shutdown command
+            while (running) {
+                // Loop if endpoint is paused
+                while (paused) {
+                    try {
+                        // TODO: We can easly do the maintenance here
+                        Thread.sleep(1000);
+                    } catch (InterruptedException e) {
+                        // Ignore
+                    }
+                }
+
+                while (keepAliveCount < 1 && addCount < 1) {
+                    // Reset maintain time.
+                    maintainTime = 0;
+                    try {
+                        synchronized (this) {
+                            this.wait();
+                        }
+                    } catch (InterruptedException e) {
+                        // Ignore
+                    }
+                }
+
+                try {
+                    // Add sockets which are waiting to the poller
+                    if (addCount > 0) {
+                        synchronized (this) {
+                            for (int i = (addCount - 1); i >= 0; i--) {
+                                int rv = Poll.add
+                                    (serverPollset, addS[i], Poll.APR_POLLIN);
+                                if (rv == Status.APR_SUCCESS) {
+                                    keepAliveCount++;
+                                } else {
+                                    // Can't do anything: close the socket right away
+                                    Socket.destroy(addS[i]);
+                                }
+                            }
+                            addCount = 0;
+                        }
+                    }
+                    maintainTime += pollTime;
+                    // Pool for the specified interval
+                    int rv = Poll.poll(serverPollset, pollTime, desc, true);
+                    if (rv > 0) {
+                        keepAliveCount -= rv;
+                        for (int n = 0; n < rv; n++) {
+                            // Check for failed sockets
+                            if (((desc[n*2] & Poll.APR_POLLHUP) == Poll.APR_POLLHUP)
+                                    || ((desc[n*2] & Poll.APR_POLLERR) == Poll.APR_POLLERR)) {
+                                // Close socket and clear pool
+                                Socket.destroy(desc[n*2+1]);
+                                continue;
+                            }
+                            // Hand this socket off to a worker
+                            getWorkerThread().assign(desc[n*2+1], false);
+                        }
+                    } else if (rv < 0) {
+                        int errn = -rv;
+                        /* Any non timeup or interrupted error is critical */
+                        if ((errn != Status.TIMEUP) && (errn != Status.EINTR)) {
+                            if (errn >  Status.APR_OS_START_USERERR) {
+                                errn -=  Status.APR_OS_START_USERERR;
+                            }
+                            log.error(sm.getString("endpoint.poll.fail", "" + errn, Error.strerror(errn)));
+                            // Handle poll critical failure
+                            synchronized (this) {
+                                destroy();
+                                init();
+                            }
+                            continue;
+                        }
+                    }
+                    if (soTimeout > 0 && maintainTime > 1000000L) {
+                        rv = Poll.maintain(serverPollset, desc, true);
+                        maintainTime = 0;
+                        if (rv > 0) {
+                            keepAliveCount -= rv;
+                            for (int n = 0; n < rv; n++) {
+                                // Close socket and clear pool
+                                Socket.destroy(desc[n]);
+                            }
+                        }
+                    }
+                } catch (Throwable t) {
+                    log.error(sm.getString("endpoint.poll.error"), t);
+                }
+
+            }
+
+        }
+
+    }
+
+
+    // ----------------------------------------------------- Worker Inner Class
+
+
+    /**
+     * Server processor class.
+     */
+    protected class Worker implements Runnable {
+
+
+        protected Thread thread = null;
+        protected boolean available = false;
+        protected long socket = 0;
+        protected boolean options = false;
+
+
+        /**
+         * 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
+         */
+        protected synchronized void assign(long socket, boolean options) {
+
+            // 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;
+            this.options = options;
+            available = true;
+            notifyAll();
+
+        }
+
+
+        /**
+         * Await a newly assigned Socket from our Connector, or <code>null</code>
+         * if we are supposed to shut down.
+         */
+        protected synchronized long 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
+            long socket = this.socket;
+            available = false;
+            notifyAll();
+
+            return (socket);
+
+        }
+
+
+        /**
+         * 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 (running) {
+
+                // Wait for the next socket to be assigned
+                long socket = await();
+                if (socket == 0)
+                    continue;
+
+                // Process the request from this socket
+                if ((options && !setSocketOptions(socket)) || !handler.process(socket)) {
+                    // Close socket and pool
+                    Socket.destroy(socket);
+                    socket = 0;
+                }
+
+                // Finish up this request
+                recycleWorkerThread(this);
+
+            }
+
+        }
+
+
+        /**
+         * Start the background processing thread.
+         */
+        public void start() {
+            thread = new ThreadWithAttributes(AprEndpoint.this, this);
+            thread.setName(getName() + "-" + (++curThreads));
+            thread.setDaemon(true);
+            thread.start();
+        }
+
+
+    }
+
+
+    // ----------------------------------------------- SendfileData Inner Class
+
+
+    /**
+     * SendfileData class.
+     */
+    public static class SendfileData {
+        // File
+        public String fileName;
+        public long fd;
+        public long fdpool;
+        // Range information
+        public long start;
+        public long end;
+        // Socket and socket pool
+        public long socket;
+        // Position
+        public long pos;
+        // KeepAlive flag
+        public boolean keepAlive;
+    }
+
+
+    // --------------------------------------------------- Sendfile Inner Class
+
+
+    /**
+     * Sendfile class.
+     */
+    public class Sendfile implements Runnable {
+
+        protected long sendfilePollset = 0;
+        protected long pool = 0;
+        protected long[] desc;
+        protected HashMap sendfileData;
+        
+        protected int sendfileCount;
+        public int getSendfileCount() { return sendfileCount; }
+
+        protected ArrayList addS;
+
+        /**
+         * Create the sendfile poller. With some versions of APR, the maximum poller size will
+         * be 62 (reocmpiling APR is necessary to remove this limitation).
+         */
+        protected void init() {
+            pool = Pool.create(serverSockPool);
+            int size = sendfileSize / sendfileThreadCount;
+            sendfilePollset = allocatePoller(size, pool, soTimeout);
+            if (sendfilePollset == 0 && size > 1024) {
+                size = 1024;
+                sendfilePollset = allocatePoller(size, pool, soTimeout);
+            }
+            if (sendfilePollset == 0) {
+                size = 62;
+                sendfilePollset = allocatePoller(size, pool, soTimeout);
+            }
+            desc = new long[size * 2];
+            sendfileData = new HashMap(size);
+            addS = new ArrayList();
+        }
+
+        /**
+         * Destroy the poller.
+         */
+        protected void destroy() {
+            // Close any socket remaining in the add queue
+            for (int i = (addS.size() - 1); i >= 0; i--) {
+                SendfileData data = (SendfileData) addS.get(i);
+                Socket.destroy(data.socket);
+            }
+            // Close all sockets still in the poller
+            int rv = Poll.pollset(sendfilePollset, desc);
+            if (rv > 0) {
+                for (int n = 0; n < rv; n++) {
+                    Socket.destroy(desc[n*2+1]);
+                }
+            }
+            Pool.destroy(pool);
+            sendfileData.clear();
+        }
+
+        /**
+         * Add the sendfile data to the sendfile poller. Note that in most cases,
+         * the initial non blocking calls to sendfile will return right away, and
+         * will be handled asynchronously inside the kernel. As a result,
+         * the poller will never be used.
+         *
+         * @param data containing the reference to the data which should be snet
+         * @return true if all the data has been sent right away, and false
+         *              otherwise
+         */
+        public boolean add(SendfileData data) {
+            // Initialize fd from data given
+            try {
+                data.fdpool = Socket.pool(data.socket);
+                data.fd = File.open
+                    (data.fileName, File.APR_FOPEN_READ
+                     | File.APR_FOPEN_SENDFILE_ENABLED | File.APR_FOPEN_BINARY,
+                     0, data.fdpool);
+                data.pos = data.start;
+                // Set the socket to nonblocking mode
+                Socket.timeoutSet(data.socket, 0);
+                while (true) {
+                    long nw = Socket.sendfilen(data.socket, data.fd,
+                                               data.pos, data.end - data.pos, 0);
+                    if (nw < 0) {
+                        if (!(-nw == Status.EAGAIN)) {
+                            Socket.destroy(data.socket);
+                            data.socket = 0;
+                            return false;
+                        } else {
+                            // Break the loop and add the socket to poller.
+                            break;
+                        }
+                    } else {
+                        data.pos = data.pos + nw;
+                        if (data.pos >= data.end) {
+                            // Entire file has been sent
+                            Pool.destroy(data.fdpool);
+                            // Set back socket to blocking mode
+                            Socket.timeoutSet(data.socket, soTimeout * 1000);
+                            return true;
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                log.error(sm.getString("endpoint.sendfile.error"), e);
+                return false;
+            }
+            // Add socket to the list. Newly added sockets will wait
+            // at most for pollTime before being polled
+            synchronized (this) {
+                addS.add(data);
+                this.notify();
+            }
+            return false;
+        }
+
+        /**
+         * Remove socket from the poller.
+         *
+         * @param data the sendfile data which should be removed
+         */
+        protected void remove(SendfileData data) {
+            int rv = Poll.remove(sendfilePollset, data.socket);
+            if (rv == Status.APR_SUCCESS) {
+                sendfileCount--;
+            }
+            sendfileData.remove(data);
+        }
+
+        /**
+         * 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 (running) {
+
+                // Loop if endpoint is paused
+                while (paused) {
+                    try {
+                        Thread.sleep(1000);
+                    } catch (InterruptedException e) {
+                        // Ignore
+                    }
+                }
+
+                while (sendfileCount < 1 && addS.size() < 1) {
+                    try {
+                        synchronized (this) {
+                            this.wait();
+                        }
+                    } catch (InterruptedException e) {
+                        // Ignore
+                    }
+                }
+
+                try {
+                    // Add socket to the poller
+                    if (addS.size() > 0) {
+                        synchronized (this) {
+                            for (int i = (addS.size() - 1); i >= 0; i--) {
+                                SendfileData data = (SendfileData) addS.get(i);
+                                int rv = Poll.add(sendfilePollset, data.socket, Poll.APR_POLLOUT);
+                                if (rv == Status.APR_SUCCESS) {
+                                    sendfileData.put(new Long(data.socket), data);
+                                    sendfileCount++;
+                                } else {
+                                    log.warn(sm.getString("endpoint.sendfile.addfail", "" + rv, Error.strerror(rv)));
+                                    // Can't do anything: close the socket right away
+                                    Socket.destroy(data.socket);
+                                }
+                            }
+                            addS.clear();
+                        }
+                    }
+                    // Pool for the specified interval
+                    int rv = Poll.poll(sendfilePollset, pollTime, desc, false);
+                    if (rv > 0) {
+                        for (int n = 0; n < rv; n++) {
+                            // Get the sendfile state
+                            SendfileData state =
+                                (SendfileData) sendfileData.get(new Long(desc[n*2+1]));
+                            // Problem events
+                            if (((desc[n*2] & Poll.APR_POLLHUP) == Poll.APR_POLLHUP)
+                                    || ((desc[n*2] & Poll.APR_POLLERR) == Poll.APR_POLLERR)) {
+                                // Close socket and clear pool
+                                remove(state);
+                                // Destroy file descriptor pool, which should close the file
+                                // Close the socket, as the reponse would be incomplete
+                                Socket.destroy(state.socket);
+                                continue;
+                            }
+                            // Write some data using sendfile
+                            long nw = Socket.sendfilen(state.socket, state.fd,
+                                                       state.pos,
+                                                       state.end - state.pos, 0);
+                            if (nw < 0) {
+                                // Close socket and clear pool
+                                remove(state);
+                                // Close the socket, as the reponse would be incomplete
+                                // This will close the file too.
+                                Socket.destroy(state.socket);
+                                continue;
+                            }
+
+                            state.pos = state.pos + nw;
+                            if (state.pos >= state.end) {
+                                remove(state);
+                                if (state.keepAlive) {
+                                    // Destroy file descriptor pool, which should close the file
+                                    Pool.destroy(state.fdpool);
+                                    Socket.timeoutSet(state.socket, soTimeout * 1000);
+                                    // If all done hand this socket off to a worker for
+                                    // processing of further requests
+                                    getWorkerThread().assign(state.socket, false);
+                                } else {
+                                    // Close the socket since this is
+                                    // the end of not keep-alive request.
+                                    Socket.destroy(state.socket);
+                                }
+                            }
+                        }
+                    } else if (rv < 0) {
+                        int errn = -rv;
+                        /* Any non timeup or interrupted error is critical */
+                        if ((errn != Status.TIMEUP) && (errn != Status.EINTR)) {
+                            if (errn >  Status.APR_OS_START_USERERR) {
+                                errn -=  Status.APR_OS_START_USERERR;
+                            }
+                            log.error(sm.getString("endpoint.poll.fail", "" + errn, Error.strerror(errn)));
+                            // Handle poll critical failure
+                            synchronized (this) {
+                                destroy();
+                                init();
+                            }
+                            continue;
+                        }
+                    }
+                    /* TODO: See if we need to call the maintain for sendfile poller */
+                } catch (Throwable t) {
+                    log.error(sm.getString("endpoint.poll.error"), t);
+                }
+            }
+
+        }
+
+    }
+
+
+    // ------------------------------------------------ Handler Inner Interface
+
+
+    /**
+     * Bare bones interface used for socket processing. Per thread data is to be
+     * stored in the ThreadWithAttributes extra folders, or alternately in
+     * thread local fields.
+     */
+    public interface Handler {
+        public boolean process(long socket);
+    }
+
+
+    // ------------------------------------------------- WorkerStack Inner Class
+
+
+    public class WorkerStack {
+        
+        protected Worker[] workers = null;
+        protected int end = 0;
+        
+        public WorkerStack(int size) {
+            workers = new Worker[size];
+        }
+        
+        /** 
+         * Put the object into the queue.
+         * 
+         * @param   object      the object to be appended to the queue (first element). 
+         */
+        public void push(Worker worker) {
+            workers[end++] = worker;
+        }
+        
+        /**
+         * Get the first object out of the queue. Return null if the queue
+         * is empty. 
+         */
+        public Worker pop() {
+            if (end > 0) {
+                return workers[--end];
+            }
+            return null;
+        }
+        
+        /**
+         * Get the first object out of the queue, Return null if the queue
+         * is empty.
+         */
+        public Worker peek() {
+            return workers[end];
+        }
+        
+        /**
+         * Is the queue empty?
+         */
+        public boolean isEmpty() {
+            return (end == 0);
+        }
+        
+        /**
+         * How many elements are there in this queue?
+         */
+        public int size() {
+            return (end);
+        }
+    }
+
+}
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..38b606d
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/DefaultServerSocketFactory.java
@@ -0,0 +1,70 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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/LeaderFollowerWorkerThread.java b/connectors/util/java/org/apache/tomcat/util/net/LeaderFollowerWorkerThread.java
new file mode 100644
index 0000000..8d2f65b
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/LeaderFollowerWorkerThread.java
@@ -0,0 +1,87 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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;
+import org.apache.tomcat.util.threads.ThreadPoolRunnable;
+
+/*
+ * 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 LeaderFollowerWorkerThread 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 LeaderFollowerWorkerThread(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) {
+                endpoint.processSocket(s, (TcpConnection) perThrData[0], (Object[]) perThrData[1]);
+            }
+
+        }
+    }
+    
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/net/MasterSlaveWorkerThread.java b/connectors/util/java/org/apache/tomcat/util/net/MasterSlaveWorkerThread.java
new file mode 100644
index 0000000..c1cfa10
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/MasterSlaveWorkerThread.java
@@ -0,0 +1,151 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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;
+
+import org.apache.tomcat.util.threads.ThreadWithAttributes;
+
+/**
+ * Regular master slave thread pool. Slave threads will wait for work.
+ */
+class MasterSlaveWorkerThread implements Runnable {
+
+    protected PoolTcpEndpoint endpoint;
+    protected String threadName;
+    protected boolean stopped = false;
+    private Object threadSync = new Object();
+    private Thread thread = null;
+    private boolean available = false;
+    private Socket socket = null;
+    private TcpConnection con = new TcpConnection();
+    private Object[] threadData = null;
+
+    
+    public MasterSlaveWorkerThread(PoolTcpEndpoint endpoint, String threadName) {
+        this.endpoint = endpoint;
+        this.threadName = threadName;
+    }
+
+
+    /**
+     * 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();
+
+    }
+
+    
+    /**
+     * 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();
+
+        return (socket);
+
+    }
+
+
+
+    /**
+     * 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) {
+
+            // Wait for the next socket to be assigned
+            Socket socket = await();
+            if (socket == null)
+                continue;
+
+            // Process the request from this socket
+            endpoint.processSocket(socket, con, threadData);
+
+            // Finish up this request
+            endpoint.recycleWorkerThread(this);
+
+        }
+
+        // Tell threadStop() we have shut ourselves down successfully
+        synchronized (threadSync) {
+            threadSync.notifyAll();
+        }
+
+    }
+
+
+    /**
+     * Start the background processing thread.
+     */
+    public void start() {
+        threadData = endpoint.getConnectionHandler().init();
+        thread = new ThreadWithAttributes(null, this);
+        thread.setName(threadName);
+        thread.setDaemon(true);
+        thread.start();
+    }
+
+
+    /**
+     * Stop the background processing thread.
+     */
+    public void stop() {
+        stopped = true;
+        assign(null);
+        thread = null;
+        threadData = null;
+    }
+
+
+}
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..ee62a28
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/PoolTcpEndpoint.java
@@ -0,0 +1,684 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 java.util.Stack;
+import java.util.Vector;
+
+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 Runnable { // implements Endpoint {
+
+    static Log log=LogFactory.getLog(PoolTcpEndpoint.class );
+
+    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 int backlog = BACKLOG;
+    private int serverTimeout = TIMEOUT;
+
+    private InetAddress inet;
+    private int port;
+
+    private ServerSocketFactory factory;
+    private ServerSocket serverSocket;
+
+    private volatile boolean running = false;
+    private volatile boolean paused = false;
+    private boolean initialized = false;
+    private boolean reinitializing = false;
+    static final int debug=0;
+
+    protected boolean tcpNoDelay=false;
+    protected int linger=100;
+    protected int socketTimeout=-1;
+    private boolean lf = true;
+
+    
+    // ------ Leader follower fields
+
+    
+    TcpConnectionHandler handler;
+    ThreadPoolRunnable listener;
+    ThreadPool tp;
+
+    
+    // ------ Master slave fields
+
+    /* The background thread. */
+    private Thread thread = null;
+    /* Available processors. */
+    private Stack workerThreads = new Stack();
+    private int curThreads = 0;
+    private int maxThreads = 20;
+    /* All processors which have been created. */
+    private Vector created = new Vector();
+
+    
+    public PoolTcpEndpoint() {
+	tp = new ThreadPool();
+    }
+
+    public PoolTcpEndpoint( ThreadPool tp ) {
+        this.tp=tp;
+    }
+
+    // -------------------- Configuration --------------------
+
+    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 String getStrategy() {
+        if (lf) {
+            return "lf";
+        } else {
+            return "ms";
+        }
+    }
+    
+    public void setStrategy(String strategy) {
+        if ("ms".equals(strategy)) {
+            lf = false;
+        } else {
+            lf = true;
+        }
+    }
+
+    public int getCurrentThreadCount() {
+        return curThreads;
+    }
+    
+    public int getCurrentThreadsBusy() {
+        return curThreads - workerThreads.size();
+    }
+    
+    // -------------------- 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 ) {
+            throw ex;
+        } catch( InstantiationException ex1 ) {
+            throw ex1;
+        }
+        initialized = true;
+    }
+    
+    public void startEndpoint() throws IOException, InstantiationException {
+        if (!initialized) {
+            initEndpoint();
+        }
+        if (lf) {
+            tp.start();
+        }
+        running = true;
+        paused = false;
+        if (lf) {
+            listener = new LeaderFollowerWorkerThread(this);
+            tp.runIt(listener);
+        } else {
+            maxThreads = getMaxThreads();
+            threadStart();
+        }
+    }
+
+    public void pauseEndpoint() {
+        if (running && !paused) {
+            paused = true;
+            unlockAccept();
+        }
+    }
+
+    public void resumeEndpoint() {
+        if (running) {
+            paused = false;
+        }
+    }
+
+    public void stopEndpoint() {
+        if (running) {
+            if (lf) {
+                tp.shutdown();
+            }
+            running = false;
+            if (serverSocket != null) {
+                closeServerSocket();
+            }
+            if (!lf) {
+                threadStop();
+            }
+            initialized=false ;
+        }
+    }
+
+    protected void closeServerSocket() {
+        if (!paused)
+            unlockAccept();
+        try {
+            if( serverSocket!=null)
+                serverSocket.close();
+        } catch(Exception e) {
+            log.error(sm.getString("endpoint.err.close"), 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) {
+            if (log.isDebugEnabled()) {
+                log.debug(sm.getString("endpoint.debug.unlock", "" + port), e);
+            }
+        } 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(sm.getString("endpoint.warn.nullSocket"));
+            } 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;
+    }
+
+    void setSocketOptions(Socket socket)
+        throws SocketException {
+        if(linger >= 0 ) 
+            socket.setSoLinger( true, linger);
+        if( tcpNoDelay )
+            socket.setTcpNoDelay(tcpNoDelay);
+        if( socketTimeout > 0 )
+            socket.setSoTimeout( socketTimeout );
+    }
+
+    
+    void processSocket(Socket s, TcpConnection con, Object[] threadData) {
+        // Process the connection
+        int step = 1;
+        try {
+            
+            // 1: Set socket options: timeout, linger, etc
+            setSocketOptions(s);
+            
+            // 2: SSL handshake
+            step = 2;
+            if (getServerSocketFactory() != null) {
+                getServerSocketFactory().handshake(s);
+            }
+            
+            // 3: Process the connection
+            step = 3;
+            con.setEndpoint(this);
+            con.setSocket(s);
+            getConnectionHandler().processConnection(con, threadData);
+            
+        } catch (SocketException se) {
+            log.debug(sm.getString("endpoint.err.socket", s.getInetAddress()),
+                    se);
+            // Try to close the socket
+            try {
+                s.close();
+            } catch (IOException e) {
+            }
+        } catch (Throwable t) {
+            if (step == 2) {
+                if (log.isDebugEnabled()) {
+                    log.debug(sm.getString("endpoint.err.handshake"), t);
+                }
+            } else {
+                log.error(sm.getString("endpoint.err.unexpected"), t);
+            }
+            // Try to close the socket
+            try {
+                s.close();
+            } catch (IOException e) {
+            }
+        } finally {
+            if (con != null) {
+                con.recycle();
+            }
+        }
+    }
+    
+
+    // -------------------------------------------------- Master Slave Methods
+
+
+    /**
+     * Create (or allocate) and return an available processor for use in
+     * processing a specific HTTP request, if possible.  If the maximum
+     * allowed processors have already been created and are in use, return
+     * <code>null</code> instead.
+     */
+    private MasterSlaveWorkerThread createWorkerThread() {
+
+        synchronized (workerThreads) {
+            if (workerThreads.size() > 0) {
+                return ((MasterSlaveWorkerThread) workerThreads.pop());
+            }
+            if ((maxThreads > 0) && (curThreads < maxThreads)) {
+                return (newWorkerThread());
+            } else {
+                if (maxThreads < 0) {
+                    return (newWorkerThread());
+                } else {
+                    return (null);
+                }
+            }
+        }
+
+    }
+
+    
+    /**
+     * Create and return a new processor suitable for processing HTTP
+     * requests and returning the corresponding responses.
+     */
+    private MasterSlaveWorkerThread newWorkerThread() {
+
+        MasterSlaveWorkerThread workerThread = 
+            new MasterSlaveWorkerThread(this, tp.getName() + "-" + (++curThreads));
+        workerThread.start();
+        created.addElement(workerThread);
+        return (workerThread);
+
+    }
+
+
+    /**
+     * Recycle the specified Processor so that it can be used again.
+     *
+     * @param processor The processor to be recycled
+     */
+    void recycleWorkerThread(MasterSlaveWorkerThread workerThread) {
+        workerThreads.push(workerThread);
+    }
+
+    
+    /**
+     * 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 (running) {
+
+            // Loop if endpoint is paused
+            while (paused) {
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException e) {
+                    // Ignore
+                }
+            }
+
+            // Allocate a new worker thread
+            MasterSlaveWorkerThread workerThread = createWorkerThread();
+            if (workerThread == null) {
+                try {
+                    // Wait a little for load to go down: as a result, 
+                    // no accept will be made until the concurrency is
+                    // lower than the specified maxThreads, and current
+                    // connections will wait for a little bit instead of
+                    // failing right away.
+                    Thread.sleep(100);
+                } catch (InterruptedException e) {
+                    // Ignore
+                }
+                continue;
+            }
+            
+            // Accept the next incoming connection from the server socket
+            Socket socket = acceptSocket();
+
+            // Hand this socket off to an appropriate processor
+            workerThread.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() {
+        thread = new Thread(this, tp.getName());
+        thread.setPriority(getThreadPriority());
+        thread.setDaemon(true);
+        thread.start();
+    }
+
+
+    /**
+     * Stop the background processing thread.
+     */
+    private void threadStop() {
+        thread = null;
+    }
+
+
+}
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..067cb49
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/SSLImplementation.java
@@ -0,0 +1,87 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..acc210c
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/SSLSupport.java
@@ -0,0 +1,128 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..00392a5
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/ServerSocketFactory.java
@@ -0,0 +1,173 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..c4b3963
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/TcpConnection.java
@@ -0,0 +1,110 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..b9096d4
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/TcpConnectionHandler.java
@@ -0,0 +1,66 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..3521f6e
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/URL.java
@@ -0,0 +1,732 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..d8d4889
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSE13Factory.java
@@ -0,0 +1,44 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..4c4244c
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSE13SocketFactory.java
@@ -0,0 +1,157 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 java.security.Provider;
+
+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 {
+            try {
+                Class ssps = Class.forName("sun.security.provider.Sun");
+                Security.addProvider ((Provider)ssps.newInstance());
+            }catch(Exception cnfe) {
+                //Ignore, since this is a non-Sun JVM
+            }
+            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..21509e4
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSE14Factory.java
@@ -0,0 +1,44 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..8eb8b59
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSE14SocketFactory.java
@@ -0,0 +1,268 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..09f81e1
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSE14Support.java
@@ -0,0 +1,159 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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/JSSE15Factory.java b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSE15Factory.java
new file mode 100644
index 0000000..1a837ff
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSE15Factory.java
@@ -0,0 +1,42 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.5 JVM).
+ *
+ * @author Bill Barker
+ */
+
+class JSSE15Factory extends JSSE14Factory {
+
+    JSSE15Factory() {
+        super();
+    }
+
+    public ServerSocketFactory getSocketFactory() {
+        return new JSSE15SocketFactory();
+    }
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSE15SocketFactory.java b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSE15SocketFactory.java
new file mode 100644
index 0000000..8ccf646
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSE15SocketFactory.java
@@ -0,0 +1,164 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.Collection;
+import java.security.KeyStore;
+import java.security.cert.PKIXBuilderParameters;
+import java.security.cert.X509CertSelector;
+import java.security.cert.CRL;
+import java.security.cert.CollectionCertStoreParameters;
+import java.security.cert.CertStoreParameters;
+import java.security.cert.CertPathParameters;
+import java.security.cert.CertStore;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CRLException;
+import java.security.cert.CertificateException;
+
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509KeyManager;
+import javax.net.ssl.ManagerFactoryParameters;
+import javax.net.ssl.CertPathTrustManagerParameters;
+
+/**
+ * SSL Socket Factory for JDK 1.5
+ *
+ * @author Bill Barker
+ */
+public class JSSE15SocketFactory  extends JSSE14SocketFactory {
+
+    private static org.apache.commons.logging.Log log =
+        org.apache.commons.logging.LogFactory.getLog(JSSE15SocketFactory.class);
+
+    public JSSE15SocketFactory() {
+        super();
+    }
+
+
+    /**
+     * Gets the intialized trust managers.
+     */
+    protected TrustManager[] getTrustManagers(String keystoreType, String algorithm)
+        throws Exception {
+        if(attributes.get("truststoreAlgorithm") == null) {
+            // in 1.5, the Trust default isn't the same as the Key default.
+            algorithm = TrustManagerFactory.getDefaultAlgorithm();
+        }
+        String crlf = (String)attributes.get("crlFile");
+        if(crlf == null) {
+            return super.getTrustManagers(keystoreType, algorithm);
+        }
+
+        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);
+            CertPathParameters params = getParameters(algorithm, crlf, trustStore);
+            ManagerFactoryParameters mfp = new CertPathTrustManagerParameters(params);
+            tmf.init(mfp);
+            tms = tmf.getTrustManagers();
+        }
+
+        return tms;
+    }
+
+
+    /**
+     * Return the initialization parameters for the TrustManager.
+     * Currently, only the default <code>PKIX</code> is supported.
+     * 
+     * @param algorithm The algorithm to get parameters for.
+     * @param crlf The path to the CRL file.
+     * @param trustStore The configured TrustStore.
+     * @return The parameters including the CRLs and TrustStore.
+     */
+    protected CertPathParameters getParameters(String algorithm, 
+                                                String crlf, 
+                                                KeyStore trustStore)
+        throws Exception {
+        CertPathParameters params = null;
+        if("PKIX".equalsIgnoreCase(algorithm)) {
+            PKIXBuilderParameters xparams = new PKIXBuilderParameters(trustStore, 
+                                                                     new X509CertSelector());
+            Collection crls = getCRLs(crlf);
+            CertStoreParameters csp = new CollectionCertStoreParameters(crls);
+            CertStore store = CertStore.getInstance("Collection", csp);
+            xparams.addCertStore(store);
+            xparams.setRevocationEnabled(true);
+            String trustLength = (String)attributes.get("trustMaxCertLength");
+            if(trustLength != null) {
+                try {
+                    xparams.setMaxPathLength(Integer.parseInt(trustLength));
+                } catch(Exception ex) {
+                    log.warn("Bad maxCertLength: "+trustLength);
+                }
+            }
+
+            params = xparams;
+        } else {
+            throw new CRLException("CRLs not supported for type: "+algorithm);
+        }
+        return params;
+    }
+
+
+    /**
+     * Load the collection of CRLs.
+     * 
+     */
+    protected Collection<? extends CRL> getCRLs(String crlf) 
+        throws IOException, CRLException, CertificateException {
+
+        File crlFile = new File(crlf);
+        if( !crlFile.isAbsolute() ) {
+            crlFile = new File(System.getProperty("catalina.base"), crlf);
+        }
+        Collection<? extends CRL> crls = null;
+        InputStream is = null;
+        try {
+            CertificateFactory cf = CertificateFactory.getInstance("X.509");
+            is = new FileInputStream(crlFile);
+            crls = cf.generateCRLs(is);
+        } catch(IOException iex) {
+            throw iex;
+        } catch(CRLException crle) {
+            throw crle;
+        } catch(CertificateException ce) {
+            throw ce;
+        } finally { 
+            if(is != null) {
+                try{
+                    is.close();
+                } catch(Exception ex) {
+                }
+            }
+        }
+        return crls;
+    }
+
+}
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..b5d2483
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSEFactory.java
@@ -0,0 +1,43 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..722355a
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSEImplementation.java
@@ -0,0 +1,89 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 JSSE15Factory =
+	"org.apache.tomcat.util.net.jsse.JSSE15Factory";
+    static final String JSSE14Factory = 
+        "org.apache.tomcat.util.net.jsse.JSSE14Factory";
+    static final String JSSE13Factory = 
+        "org.apache.tomcat.util.net.jsse.JSSE13Factory";
+    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 = null;
+
+    public JSSEImplementation() throws ClassNotFoundException {
+        // Check to see if JSSE is floating around somewhere
+        Class.forName(SSLSocketClass);
+        if( JdkCompat.isJava15() ) {
+            try {
+                Class factcl = Class.forName(JSSE15Factory);
+                factory = (JSSEFactory)factcl.newInstance();
+            } catch(Exception ex) {
+                if(logger.isDebugEnabled())
+                    logger.debug("Error getting factory: " + JSSE15Factory, ex);
+            }
+        }
+        if(factory == null && JdkCompat.isJava14() ) {
+            try {
+                Class factcl = Class.forName(JSSE14Factory);
+                factory = (JSSEFactory)factcl.newInstance();
+            } catch(Exception ex) {
+                if(logger.isDebugEnabled()) {
+                    logger.debug("Error getting factory: " + JSSE14Factory, ex);
+                }
+            }
+        } if(factory == null) {
+            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..7b82696
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSEKeyManager.java
@@ -0,0 +1,144 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..cf26390
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSESocketFactory.java
@@ -0,0 +1,374 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 = sslProxy.getDefaultCipherSuites();
+        }
+
+        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) {
+            // Bugzilla 38774: http://issues.apache.org/bugzilla/show_bug.cgi?id=38774
+            keystorePass = System.getProperty("javax.net.ssl.keyStorePassword");
+            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);
+            if(! "PKCS11".equalsIgnoreCase(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());
+        } catch (FileNotFoundException fnfe) {
+            throw fnfe;
+        } catch (IOException ioe) {
+            throw ioe;      
+        } catch(Exception ex) {
+            log.error("Exception trying to load keystore " +path,ex);
+            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..393df1f
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSESupport.java
@@ -0,0 +1,179 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..de2ea16
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/puretls/PureTLSImplementation.java
@@ -0,0 +1,58 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..924f2c8
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/puretls/PureTLSSocket.java
@@ -0,0 +1,43 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..16305d2
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/puretls/PureTLSSocketFactory.java
@@ -0,0 +1,230 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..52a5129
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/puretls/PureTLSSupport.java
@@ -0,0 +1,144 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..cbe481f
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/res/LocalStrings.properties
@@ -0,0 +1,24 @@
+# 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}
+endpoint.err.socket=Socket error caused by remote host {0}
+endpoint.err.handshake=Handshake failed
+endpoint.err.unexpected=Unexpected error processing socket
+endpoint.warn.nullSocket=Null socket returned by accept
+endpoint.debug.unlock=Caught exception trying to unlock accept on port {0}
+endpoint.err.close=Caught exception trying to close socket
+endpoint.noProcessor=No Processors - worker thread dead!
+
+endpoint.init.bind=Socket bind failed: [{0}] {1}
+endpoint.init.listen=Socket listen failed: [{0}] {1}
+endpoint.accept.fail=Socket accept failed
+endpoint.poll.limitedpollsize=Failed to create poller with specified size of {0}
+endpoint.poll.initfail=Poller creation failed
+endpoint.poll.fail=Critical poller failure (restarting poller): [{0}] {1}
+endpoint.poll.error=Unexpected poller error
+endpoint.sendfile.error=Unexpected sendfile error
+endpoint.sendfile.addfail=Sednfile failure: [{0}] {1}
+endpoint.sendfile.nosupport=Disabling sendfile, since either the APR version or the system doesn't support it
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..593980e
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/res/StringManager.java
@@ -0,0 +1,286 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..cafb9c8
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/threads/Expirer.java
@@ -0,0 +1,156 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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
+{
+    
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog(Expirer.class );
+    
+    // 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 ) {
+		    managedCount--;
+		    managedObjs[ i ] = managedObjs[managedCount];
+                    managedObjs[managedCount] = null;
+		}
+		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( log.isTraceEnabled() ) log.trace( "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( log.isTraceEnabled() ) log.trace( "TS: " + maxInactiveInterval + " " +
+				ts.getLastAccessedTime());
+	    if (maxInactiveInterval < 0)
+		continue;
+	    
+	    long timeIdle = timeNow - ts.getThisAccessedTime();
+	    
+	    if (timeIdle >= maxInactiveInterval) {
+		if( expireCallback != null ) {
+		    if( log.isDebugEnabled() )
+			log.debug( ts + " " + timeIdle + " " +
+			       maxInactiveInterval );
+		    expireCallback.expired( ts );
+		}
+	    }
+	}
+    }
+
+}
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..f9901c4
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/threads/Reaper.java
@@ -0,0 +1,126 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog(Reaper.class );
+    
+    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;
+        if (log.isDebugEnabled())
+            log.debug("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..f5bc7df
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/threads/ThreadPool.java
@@ -0,0 +1,839 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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 > MAX_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(interval);
+                    }
+
+                    // 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..c415988
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/threads/ThreadPoolRunnable.java
@@ -0,0 +1,39 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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..0940b37
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/threads/ThreadWithAttributes.java
@@ -0,0 +1,101 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS 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/util/loader/loader.properties b/connectors/util/loader/loader.properties
new file mode 100644
index 0000000..b5ac050
--- /dev/null
+++ b/connectors/util/loader/loader.properties
@@ -0,0 +1,110 @@
+catalina.home=/opt/50
+
+loader.auto-startup=org.apache.tomcat.util.jmx.JmxRemoteLoader,\
+             org.apache.catalina.startup.CatalinaModuleListener
+
+#
+# List of comma-separated packages that start with or equal this string
+# will cause a security exception to be thrown when
+# passed to checkPackageAccess unless the
+# corresponding RuntimePermission ("accessClassInPackage."+package) has
+# been granted.
+loader.package.access=sun.,org.apache.catalina.,org.apache.coyote.,org.apache.tomcat.,org.apache.jasper.,sun.beans.
+#
+# List of comma-separated packages that start with or equal this string
+# will cause a security exception to be thrown when
+# passed to checkPackageDefinition unless the
+# corresponding RuntimePermission ("defineClassInPackage."+package) has
+# been granted.
+#
+# by default, no packages are restricted for definition, and none of
+# the class loaders supplied with the JDK call checkPackageDefinition.
+#
+loader.package.definition=sun.,java.,org.apache.catalina.,org.apache.coyote.,org.apache.tomcat.,org.apache.jasper.
+
+#
+#
+# List of comma-separated paths defining the contents of the "common" 
+# classloader. Prefixes should be used to define what is the repository type.
+# Path may be relative to the CATALINA_HOME or CATALINA_BASE path or absolute.
+# If left as blank,the JVM system loader will be used as Catalina's "common" 
+# loader.
+# Examples:
+#     "foo": Add this folder as a class repository
+#     "foo/*.jar": Add all the JARs of the specified folder as class 
+#                  repositories
+#     "foo/bar.jar": Add bar.jar as a class repository
+common.loader=${catalina.home}/bin/jmx.jar,\
+    ${catalina.home}/common/classes,\
+    ${catalina.home}/common/i18n/tomcat-i18n-en.jar,${catalina.home}/common/endorsed/*.jar,${catalina.home}/common/lib/*.jar
+
+#
+# List of comma-separated paths defining the contents of the "server" 
+# classloader. Prefixes should be used to define what is the repository type.
+# Path may be relative to the CATALINA_HOME or CATALINA_BASE path or absolute.
+# If left as blank, the "common" loader will be used as Catalina's "server" 
+# loader.
+# Examples:
+#     "foo": Add this folder as a class repository
+#     "foo/*.jar": Add all the JARs of the specified folder as class 
+#                  repositories
+#     "foo/bar.jar": Add bar.jar as a class repository
+server.loader=/ws/apache/jakarta-tomcat-connectors/util/build/classes,/ws/apache/jakarta-tomcat-catalina/build/classes,/ws/apache/jakarta-tomcat-connectors/build/classes,${catalina.home}/server/lib/commons-modeler.jar
+
+#
+# List of comma-separated paths defining the contents of the "shared" 
+# classloader. Prefixes should be used to define what is the repository type.
+# Path may be relative to the CATALINA_BASE path or absolute. If left as blank,
+# the "common" loader will be used as Catalina's "shared" loader.
+# Examples:
+#     "foo": Add this folder as a class repository
+#     "foo/*.jar": Add all the JARs of the specified folder as class 
+#                  repositories
+#     "foo/bar.jar": Add bar.jar as a class repository 
+shared.loader=${catalina.base}/shared/classes,${catalina.base}/shared/lib/*.jar
+
+# Classes to preload to avoid security exceptions
+loader.security.preload=\
+             org.apache.catalina.core.ApplicationContextFacade$1,\
+             org.apache.catalina.core.ApplicationDispatcher$PrivilegedForward,\
+             org.apache.catalina.core.ApplicationDispatcher$PrivilegedInclude,\
+             org.apache.catalina.core.ContainerBase$PrivilegedAddChild,\
+             org.apache.catalina.core.StandardWrapper$1,\
+             org.apache.catalina.loader.WebappClassLoader$PrivilegedFindResource,\
+             org.apache.catalina.session.StandardSession,\
+             org.apache.catalina.session.StandardSession$1,\
+             org.apache.catalina.session.StandardManager$PrivilegedDoUnload,\
+             org.apache.catalina.util.URL,\
+             org.apache.catalina.util.Enumerator,\
+             javax.servlet.http.Cookie,\
+            org.apache.coyote.http11Http11Processor$1,\
+            org.apache.coyote.http11InternalOutputBuffer$1,\
+            org.apache.coyote.http11InternalOutputBuffer$2,\
+             org.apache.catalina.connector.RequestFacade$GetAttributePrivilegedAction,\
+             org.apache.catalina.connector.RequestFacade$GetParameterMapPrivilegedAction,\
+             org.apache.catalina.connector.RequestFacade$GetRequestDispatcherPrivilegedAction,\
+             org.apache.catalina.connector.RequestFacade$GetParameterPrivilegedAction,\
+             org.apache.catalina.connector.RequestFacade$GetParameterNamesPrivilegedAction,\
+             org.apache.catalina.connector.RequestFacade$GetParameterValuePrivilegedAction,\
+             org.apache.catalina.connector.RequestFacade$GetCharacterEncodingPrivilegedAction,\
+             org.apache.catalina.connector.RequestFacade$GetHeadersPrivilegedAction,\
+             org.apache.catalina.connector.RequestFacade$GetHeaderNamesPrivilegedAction,\  
+             org.apache.catalina.connector.RequestFacade$GetCookiesPrivilegedAction,\
+             org.apache.catalina.connector.RequestFacade$GetLocalePrivilegedAction,\
+             org.apache.catalina.connector.RequestFacade$GetLocalesPrivilegedAction,\
+             org.apache.catalina.connector.ResponseFacade$SetContentTypePrivilegedAction,\
+             org.apache.catalina.connector.ResponseFacade$DateHeaderPrivilegedAction,\
+             org.apache.catalina.connector.RequestFacade$GetSessionPrivilegedAction,\
+             org.apache.catalina.connector.ResponseFacade$1,\
+             org.apache.catalina.connector.OutputBuffer$1,\
+             org.apache.catalina.connector.CoyoteInputStream$1,\
+             org.apache.catalina.connector.CoyoteInputStream$2,\
+             org.apache.catalina.connector.CoyoteInputStream$3,\
+             org.apache.catalina.connector.CoyoteInputStream$4,\
+             org.apache.catalina.connector.CoyoteInputStream$5,\
+             org.apache.catalina.connector.InputBuffer$1,\
+             org.apache.catalina.connector.Response$1,\
+             org.apache.catalina.connector.Response$2,\
+             org.apache.catalina.connector.Response$3,\
+             org.apache.jk.server.JkCoyoteHandler$1,\
+             org.apache.jk.server.JkCoyoteHandler$StatusLinePrivilegedAction
diff --git a/connectors/util/loader/org/apache/tomcat/util/loader/Loader.java b/connectors/util/loader/org/apache/tomcat/util/loader/Loader.java
new file mode 100755
index 0000000..29ec044
--- /dev/null
+++ b/connectors/util/loader/org/apache/tomcat/util/loader/Loader.java
@@ -0,0 +1,882 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.loader;
+
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+
+/**
+ * Boostrap loader for Catalina or other java apps. 
+ * 
+ * This application constructs a class loader
+ * for use in loading the Catalina internal classes (by accumulating all of the
+ * JAR files found in the "server" directory under "catalina.home"), and
+ * starts the regular execution of the container.  The purpose of this
+ * roundabout approach is to keep the Catalina internal classes (and any
+ * other classes they depend on, such as an XML parser) out of the system
+ * class path and therefore not visible to application level classes.
+ *
+ *
+ * Merged with CatalinaProperties:
+ * Load a properties file describing the modules and startup sequence.
+ * This is responsible for configuration of the loader. 
+ * TODO: support jmx-style configuration, including persistence.
+ * TODO: better separate legacy config and the new style
+ * 
+ * The properties file will be named "loader.properties" or 
+ * "catalina.properties" ( for backwad compatibility ) and
+ * will be searched in:
+ *  - TODO 
+ * 
+ * Properties used:
+ *  - TODO
+ *
+ * loader.* and *.loader properties are used internally by the loader ( 
+ *  *.loader is for backward compat with catalina ).
+ * All other properties in the config file are set as System properties.
+ * 
+ * Based on o.a.catalina.bootstrap.CatalinaProperties - utility class to read 
+ * the bootstrap Catalina configuration.
+
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @author Costin Manolache
+ */ 
+public final class Loader  {
+
+    private static final boolean DEBUG=true; //LoaderProperties.getProperty("loader.debug.Loader") != null;
+    // If flat, only one loader is created. If false - one loader per jar/dir
+    private static final boolean FLAT=false;//LoaderProperties.getProperty("loader.Loader.flat") != null;
+    
+    // -------------------------------------------------------------- Constants
+
+
+    private static final String CATALINA_HOME_TOKEN = "${catalina.home}";
+    private static final String CATALINA_BASE_TOKEN = "${catalina.base}";
+
+
+    // ------------------------------------------------------- Static Variables
+
+
+    /**
+     * Daemon object used by main.
+     */
+    private static Loader daemon = null;
+
+    // one should be enough
+    ModuleListener listener;
+
+
+    // -------------------------------------------------------------- Variables
+
+    protected Repository commonRepository = null;
+    protected Repository catalinaRepository = null;
+    protected Repository sharedRepository = null;
+
+    protected ClassLoader catalinaLoader = null;
+    private String[] args;
+    private Hashtable repositories=new Hashtable();
+    private ClassLoader parentClassLoader;
+    
+    private static Properties properties = null;
+
+
+    private static String propFile;
+
+    // -------------------------------------------------------- Private Methods
+    
+    /** Set the parent class loader - can be used instead of setParent, 
+     * in case this is the top loader and needs to delagate to embedding app.
+     * The common loader will delegate to this loader
+     * 
+     * @param myL
+     */
+    public void setParentClassLoader(ClassLoader myL) {
+        this.parentClassLoader=myL;
+    }
+
+
+
+    /** Initialize the loader, creating all repositories.
+     *  Will create common, server, shared. 
+     *  
+     *  TODO: create additional repos.
+     *
+     */
+    public void init() {
+        try {
+            commonRepository = initRepository("common", null, parentClassLoader);
+            catalinaRepository = initRepository("server", commonRepository,null);
+            catalinaLoader = catalinaRepository.getClassLoader();
+            sharedRepository = initRepository("shared", commonRepository,null);
+        } catch (Throwable t) {
+            log("Class loader creation threw exception", t);
+            System.exit(1);
+        }
+    }
+
+    /** Create a new repository. 
+     *  No Module is added ( currently )
+     *  TODO: use props to prepopulate, if any is present.
+     * 
+     * @param name
+     * @param parent
+     * @return
+     */
+    public Repository createRepository(String name, Repository parent) {
+        Repository lg=new Repository(this);
+
+        lg.setName(name);
+       
+        lg.setParent( parent );
+        
+        repositories.put(name, lg);
+        
+        if( listener != null ) {
+            listener.repositoryAdd(lg);
+        }
+        return lg;
+    }
+
+    public Enumeration getRepositoryNames() {
+        return repositories.keys();
+    }
+    
+    /** Create a module using the NAME.loader property to construct the 
+     *  classpath.
+     * 
+     * @param name
+     * @param parent
+     * @return
+     * @throws Exception
+     */
+    private Repository initRepository(String name, Repository parent, ClassLoader pcl)
+        throws Exception 
+    {
+        String value = getProperty(name + ".loader");
+
+        Repository lg=createRepository(name, parent );
+        if( pcl != null )
+            lg.setParentClassLoader( pcl );
+        if( DEBUG ) log( "Creating loading group " + name + " - " + value + " " + pcl);
+        
+        if ((value == null) || (value.equals("")))
+            return lg;
+
+        ArrayList unpackedList = new ArrayList();
+        ArrayList packedList = new ArrayList();
+        ArrayList urlList = new ArrayList();
+
+        Vector repo=split( value );
+        Enumeration elems=repo.elements();
+        while (elems.hasMoreElements()) {
+            String repository = (String)elems.nextElement();
+
+            // Local repository
+            boolean packed = false;
+            
+            if (repository.startsWith(CATALINA_HOME_TOKEN)) {
+                repository = getCatalinaHome()
+                    + repository.substring(CATALINA_HOME_TOKEN.length());
+            } else if (repository.startsWith(CATALINA_BASE_TOKEN)) {
+                repository = getCatalinaBase()
+                    + repository.substring(CATALINA_BASE_TOKEN.length());
+            }
+
+            // Check for a JAR URL repository
+            try {
+                urlList.add(new URL(repository));
+                continue;
+            } catch (MalformedURLException e) {
+                // Ignore
+            }
+
+            if (repository.endsWith("*.jar")) {
+                packed = true;
+                repository = repository.substring
+                    (0, repository.length() - "*.jar".length());
+            }
+            if (packed) {
+                packedList.add(new File(repository));
+            } else {
+                unpackedList.add(new File(repository));
+            }
+        }
+
+        File[] unpacked = (File[]) unpackedList.toArray(new File[0]);
+        File[] packed = (File[]) packedList.toArray(new File[0]);
+        URL[] urls = (URL[]) urlList.toArray(new URL[0]);
+
+        // previously: ClassLoaderFactory.createClassLoader
+        initRepository(lg, unpacked, packed, urls, parent); //new ModuleGroup();
+        
+        
+        // TODO: JMX registration for the group loader 
+        /*
+        // Register the server classloader
+        ObjectName objectName =
+            new ObjectName("Catalina:type=ServerClassLoader,name=" + name);
+        mBeanServer.registerMBean(classLoader, objectName);
+        */
+        
+        return lg; // classLoader;
+
+    }
+
+    /** Small hack to allow a loader.properties file to be passed in CLI, to
+     * allow a more convenient method to start with different params from 
+     * explorer/kde/etc.
+     * 
+     * If the first arg ends with ".loader", it is used as loader.properties
+     * file and removed from args[].  
+     */
+    private void processCLI() {
+        if( args!=null && args.length > 0 &&
+                (args[0].toLowerCase().endsWith(".tomcat") ||
+                        args[0].toLowerCase().endsWith(".loader") ||
+                        args[0].toLowerCase().endsWith("loader.properties") )) {
+            String props=args[0];
+            String args2[]=new String[args.length-1];
+            System.arraycopy(args, 1, args2, 0, args2.length);
+            args=args2;
+            setPropertiesFile(props);
+        } else {
+            loadProperties();
+        }
+    }
+    
+    /**
+     * Initialize:
+     *  - detect the home/base directories
+     *  - init the loaders / modules
+     *  - instantiate the "startup" class(es)
+     * 
+     */
+    public void main()
+        throws Exception
+    {
+        processCLI();
+        
+        // Set Catalina path
+        setCatalinaHome();
+        setCatalinaBase();
+
+        init();
+        
+        Thread.currentThread().setContextClassLoader(catalinaLoader);
+
+        securityPreload(catalinaLoader);
+
+        autostart();
+    }
+
+    private void autostart() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
+        // Load our startup classes and call its process() method
+        /* Why multiple classes ? 
+         * - maybe you want to start more "servers" ( tomcat ,something else )
+         * - easy hook for other load on startup modules ( like a jmx hook )
+         * - maybe split the loader-specific code from catalina 
+         */
+        String startupClasses=getProperty("loader.auto-startup",
+                "org.apache.catalina.startup.CatalinaModuleListener");
+        Vector v=split( startupClasses ); 
+        
+        for( int i=0; i<v.size(); i++ ) {
+            String startupCls=(String)v.elementAt(i);
+        
+            if (DEBUG)
+                log("Loading startup class " + startupCls);
+            
+            Class startupClass =
+                catalinaLoader.loadClass(startupCls);
+            
+            Object startupInstance = startupClass.newInstance();
+            
+            if( startupInstance instanceof ModuleListener ) {
+                addModuleListener((ModuleListener)startupInstance);
+
+                // it can get args[] and properties from Loader
+                listener.setLoader(this);
+                
+                // all arg processing moved there. Maybe we can make it consistent
+                // for all startup schemes
+                listener.start();
+            } else if ( startupInstance instanceof Runnable ) {
+                ((Runnable)startupInstance).run();
+            } else {
+                Class paramTypes[] = new Class[0];
+                Object paramValues[] = new Object[0];
+                Method method =
+                    startupInstance.getClass().getMethod("execute", paramTypes);
+                if( method==null ) 
+                    method = startupInstance.getClass().getMethod("start", paramTypes);
+                if( method!=null )
+                    method.invoke(startupInstance, paramValues);
+            }
+
+        }
+    }
+
+    /** Returns one of the repositories. 
+     * 
+     *  Typically at startup we create at least: "common", "shared" and "server", with 
+     *  same meaning as in tomcat. 
+     * 
+     * @param name
+     * @return
+     */
+    public Repository getRepository( String name ) {
+        return (Repository)repositories.get(name);
+    }
+    
+    private  static void securityPreload(ClassLoader loader)
+        throws Exception {
+
+        if( System.getSecurityManager() == null ){
+            return;
+        }
+
+        String value=getProperty("security.preload");
+        Vector repo=split( value );
+        Enumeration elems=repo.elements();
+        while (elems.hasMoreElements()) {
+            String classN = (String)elems.nextElement();
+            try {
+                loader.loadClass( classN);
+            } catch( Throwable t ) {
+                // ignore
+            }
+        }
+    }
+
+
+    // ----------------------------------------------------------- Main Program
+
+    /** Access to the command line arguments, when Loader is used to launc an app.
+     */
+    public String[] getArgs() {
+        return args;
+    }
+
+    /**
+     * Main method.
+     *
+     * @param args Command line arguments to be processed
+     */
+    public static void main(String args[]) {
+        
+        try {
+            if (daemon == null) {
+                daemon = new Loader();
+                daemon.args=args;
+
+                try {
+                    daemon.main();
+                } catch (Throwable t) {
+                    t.printStackTrace();
+                    return;
+                }
+            }
+        } catch (Throwable t) {
+            t.printStackTrace();
+        }
+
+    }
+
+
+    /**
+     * Initialize the loader properties explicitely. 
+     * 
+     * TODO: add setPropertiesRes
+     * 
+     * @param props
+     */
+    public void setPropertiesFile(String props) {
+        propFile=props;
+        loadProperties();
+    }
+
+    /**
+     * Return specified property value.
+     */
+    static String getProperty(String name) {
+        if( properties==null ) loadProperties();
+        return properties.getProperty(name);
+    }
+
+
+    /**
+     * Return specified property value.
+     */
+    static String getProperty(String name, String defaultValue) {
+        if( properties==null ) loadProperties();
+        return properties.getProperty(name, defaultValue);
+    }
+
+    /**
+     * Load properties.
+     * Will try: 
+     * - "catalina.config" system property ( a URL )
+     * - "catalina.base", "catalina.home", "user.dir" system properties +
+     *    "/conf/" "../conf" "/" +  "loader.properties" or "catalina.properties"
+     * - /org/apache/catalina/startup/catalina.properties
+     * 
+     * Properties will be loaded as system properties. 
+     * 
+     * loader.properties was added to allow coexistence with bootstrap.jar ( the 
+     * current scheme ), since the classpaths are slightly different. 
+     */
+    static void loadProperties() {
+        properties = new Properties();
+        
+        InputStream is = null;
+        Throwable error = null;
+
+        // TODO: paste the code to do ${} substitution 
+        // TODO: add the code to detect where tomcat-properties is loaded from
+        if( propFile != null ) {
+            try {
+                File properties = new File(propFile);
+                is = new FileInputStream(properties);
+                if( is!=null && DEBUG ) {
+                    log("Loaded from loader.properties " + properties );
+                }
+            } catch( Throwable t) {
+                System.err.println("Can't find " + propFile);
+                return;
+            }
+        }
+        
+        if( is == null ) {
+            try {
+                // "catalina.config" system property
+                String configUrl = System.getProperty("catalina.config");
+                if (configUrl != null) {
+                    is = (new URL(configUrl)).openStream();
+                    if( is!=null && DEBUG ) {
+                        log("Loaded from catalina.config " + configUrl );
+                    }
+                }
+            } catch (Throwable t) {
+                // Ignore
+            }
+        }
+
+        if( is == null ) {
+            try {
+                // "loader.config" system property
+                String configUrl = System.getProperty("loader.config");
+                if (configUrl != null) {
+                    is = (new URL(configUrl)).openStream();
+                    if( is!=null && DEBUG ) {
+                        log("Loaded from catalina.config " + configUrl );
+                    }
+                }
+            } catch (Throwable t) {
+                // Ignore
+            }
+        }
+
+        if (is == null) {
+            try {
+                setCatalinaBase(); // use system properties, then user.dir
+                File home = new File(getCatalinaBase());
+                File conf = new File(home, "conf");
+                
+                // use conf if exists, or the base directory otherwise
+                if( ! conf.exists() ) conf = new File(home, "../conf");
+                if( ! conf.exists() ) conf = home;
+                File propertiesF=null;
+                if(  conf.exists() )  
+                     propertiesF= new File(conf, "loader.properties");
+                if( ! propertiesF.exists() ) {
+                    propertiesF= new File( home, "loader.properties");
+                }
+                if( propertiesF.exists() )
+                    is = new FileInputStream(propertiesF);
+                if( is!=null && DEBUG ) {
+                    log("Loaded from loader.properties " + properties );
+                }
+            } catch (Throwable t) {
+                // Ignore
+            }
+        }
+
+        if (is == null) {
+            try {
+                File home = new File(getCatalinaBase());
+                File conf = new File(home, "conf");
+                File properties = new File(conf, "catalina.properties");
+                is = new FileInputStream(properties);
+                if( is!=null && DEBUG ) {
+                    log("Loaded from catalina.properties " + properties );
+                }
+            } catch (Throwable t) {
+                // Ignore
+            }
+        }
+
+        if (is == null) {
+            try {
+                is = Loader.class.getResourceAsStream
+                    ("/org/apache/catalina/startup/catalina.properties");
+                if( is!=null && DEBUG ) {
+                    log("Loaded from o/a/c/startup/catalina.properties " );
+                }
+
+            } catch (Throwable t) {
+                // Ignore
+            }
+        }
+
+        if (is == null) {
+            try {
+                is = Loader.class.getResourceAsStream
+                    ("loader.properties");
+                if( is!=null && DEBUG ) {
+                    log("Loaded from res loader.properties " );
+                }
+            } catch (Throwable t) {
+                // Ignore
+            }
+        }
+        
+        if (is != null) {
+            try {
+                properties.load(is);
+                is.close();
+            } catch (Throwable t) {
+                error = t;
+            }
+        }
+
+//        if ((is == null) || (error != null)) {
+//            // Do something
+//            log("Error: no properties found !!!");
+//        }
+
+        // Register the _unused_ properties as system properties
+        if( properties != null ) {
+            Enumeration enumeration = properties.propertyNames();
+            while (enumeration.hasMoreElements()) {
+                String name = (String) enumeration.nextElement();
+                String value = properties.getProperty(name);
+                if( "security.preload".equals( name )) continue;
+                if( "package.access".equals( name )) continue;
+                if( "package.definition".equals( name )) continue;
+                if( name.endsWith(".loader")) continue;
+                if( name.startsWith("loader.")) continue;
+                if (value != null) {
+                    System.setProperty(name, value);
+                }
+            }
+        }
+
+    }
+
+    static void setCatalinaHome(String s) {
+        System.setProperty( "catalina.home", s );
+    }
+
+    static void setCatalinaBase(String s) {
+        System.setProperty( "catalina.base", s );
+    }
+
+
+    /**
+     * Get the value of the catalina.home environment variable.
+     * 
+     * @deprecated
+     */
+    static String getCatalinaHome() {
+        if( properties==null ) loadProperties();
+        return System.getProperty("catalina.home",
+                                  System.getProperty("user.dir"));
+    }
+    
+    
+    /**
+     * Get the value of the catalina.base environment variable.
+     * 
+     * @deprecated
+     */
+    static String getCatalinaBase() {
+        if( properties==null ) loadProperties();
+        return System.getProperty("catalina.base", getCatalinaHome());
+    }
+
+    
+    /**
+     * Set the <code>catalina.base</code> System property to the current
+     * working directory if it has not been set.
+     */
+    static void setCatalinaBase() {
+        if( properties==null ) loadProperties();
+
+        if (System.getProperty("catalina.base") != null)
+            return;
+        if (System.getProperty("catalina.home") != null)
+            System.setProperty("catalina.base",
+                               System.getProperty("catalina.home"));
+        else
+            System.setProperty("catalina.base",
+                               System.getProperty("user.dir"));
+
+    }
+
+
+    /**
+     * Set the <code>catalina.home</code> System property to the current
+     * working directory if it has not been set.
+     */
+    static void setCatalinaHome() {
+
+        if (System.getProperty("catalina.home") != null)
+            return;
+        File bootstrapJar = 
+            new File(System.getProperty("user.dir"), "bootstrap.jar");
+        File tloaderJar = 
+            new File(System.getProperty("user.dir"), "tomcat-loader.jar");
+        if (bootstrapJar.exists() || tloaderJar.exists()) {
+            try {
+                System.setProperty
+                    ("catalina.home", 
+                     (new File(System.getProperty("user.dir"), ".."))
+                     .getCanonicalPath());
+            } catch (Exception e) {
+                // Ignore
+                System.setProperty("catalina.home",
+                                   System.getProperty("user.dir"));
+            }
+        } else {
+            System.setProperty("catalina.home",
+                               System.getProperty("user.dir"));
+        }
+
+    }
+
+
+    
+    /**
+     * Get the module from the classloader. Works only for classloaders created by
+     * this package - or extending ModuleClassLoader.
+     * 
+     * This shold be the only public method that allows this - Loader acts as a 
+     * guard, only if you have the loader instance you can access the internals.
+     * 
+     * 
+     * @param cl
+     * @return
+     */
+    public Module getModule(ClassLoader cl ) {
+        if( cl instanceof ModuleClassLoader ) {
+            return ((ModuleClassLoader)cl).getModule();
+        }
+        return null;
+    }
+    
+    /**
+     * Create and return a new class loader, based on the configuration
+     * defaults and the specified directory paths:
+     *
+     * @param unpacked Array of pathnames to unpacked directories that should
+     *  be added to the repositories of the class loader, or <code>null</code> 
+     * for no unpacked directories to be considered
+     * @param packed Array of pathnames to directories containing JAR files
+     *  that should be added to the repositories of the class loader, 
+     * or <code>null</code> for no directories of JAR files to be considered
+     * @param urls Array of URLs to remote repositories, designing either JAR 
+     *  resources or uncompressed directories that should be added to 
+     *  the repositories of the class loader, or <code>null</code> for no 
+     *  directories of JAR files to be considered
+     * @param parent Parent class loader for the new class loader, or
+     *  <code>null</code> for the system class loader.
+     *
+     * @exception Exception if an error occurs constructing the class loader
+     */
+    private void initRepository(Repository lg, File unpacked[],
+            File packed[],  URL urls[],  Repository parent)
+        throws Exception 
+    {
+        StringBuffer sb=new StringBuffer();
+
+        // Construct the "class path" for this class loader
+        ArrayList list = new ArrayList();
+        
+        // Add unpacked directories
+        if (unpacked != null) {
+            for (int i = 0; i < unpacked.length; i++)  {
+                File file = unpacked[i];
+                if (!file.exists() || !file.canRead()) {
+                    if (DEBUG)
+                        log("  Not found:  "+ file.getAbsolutePath());
+                    continue;
+                }
+//                String cPath=file.getCanonicalPath();
+//                URL url=null;
+//                
+//                if( cPath.toLowerCase().endsWith(".jar") ||
+//                        cPath.toLowerCase().endsWith(".zip") ) {
+//                    url = new URL("file", null, cPath);
+//                } else {
+//                    url = new URL("file", null, cPath + File.separator);
+//                }
+                URL url=file.toURL();
+                if (DEBUG)
+                    sb.append(" : "+ url);
+                if( ! FLAT ) {
+                    addLoader(lg, parent, new URL[] { url });
+                } else {
+                    list.add(url);
+                }
+            }
+        }
+        
+        // Add packed directory JAR files
+        if (packed != null) {
+            for (int i = 0; i < packed.length; i++) {
+                File directory = packed[i];
+                if (!directory.isDirectory() || !directory.exists() ||
+                        !directory.canRead()) {
+                    if (DEBUG)
+                        log("  Not found:  "+ directory.getAbsolutePath());
+                    continue;
+                }
+                String filenames[] = directory.list();
+                for (int j = 0; j < filenames.length; j++) {
+                    String filename = filenames[j].toLowerCase();
+                    if (!filename.endsWith(".jar"))
+                        continue;
+                    File file = new File(directory, filenames[j]);
+//                    if (DEBUG)
+//                        sb.append(" [pak]="+ file.getCanonicalPath());
+//                    URL url = new URL("file", null,
+//                            file.getCanonicalPath());
+                    URL url=file.toURL();
+                    if (DEBUG)
+                        sb.append(" pk="+ url);
+
+                    if( ! FLAT ) {
+                        addLoader(lg, parent, new URL[] { url });
+                    } else {
+                        list.add(url);
+                    }
+                }
+            }
+        }
+        
+        // Add URLs
+        if (urls != null) {
+            for (int i = 0; i < urls.length; i++) {
+                if( ! FLAT ) {
+                    addLoader(lg, parent, new URL[] { urls[i] });
+                } else {
+                    list.add(urls[i]);
+                }
+                if (DEBUG)
+                    sb.append(" "+ urls[i]);
+            }
+        }
+        
+        // Construct the class loader itself
+        
+        // TODO: experiment with loading each jar in a separate loader.
+        if (DEBUG)
+            log("Creating new class loader " + lg.getName() + " " + sb.toString());
+        
+        
+        URL[] array = (URL[]) list.toArray(new URL[list.size()]);
+        if( array.length > 0 ) {
+            addLoader(lg, parent, array);
+        }
+    }
+    
+    /**
+     * @param lg
+     * @param parent
+     * @param list
+     */
+    private void addLoader(Repository lg, Repository parent, URL array[]) 
+        throws Exception
+    {
+        Module module=new Module();
+        
+        module.setParent( parent );
+        module.setClasspath( array );
+        
+        lg.addModule(module);
+        
+    }
+
+    private static Vector split( String value ) {
+        Vector result=new Vector();
+        StringTokenizer tokenizer = new StringTokenizer(value, ",");
+        while (tokenizer.hasMoreElements()) {
+            String repository = tokenizer.nextToken();
+            repository.trim();
+            if( ! "".equals(repository) )
+                result.addElement(repository);
+        }
+        return result;
+    }
+
+    void notifyModuleStart(Module module) {
+        if(listener!=null) listener.moduleStart(module);
+    }
+
+    void notifyModuleStop(Module module) {
+        if( listener!=null ) listener.moduleStop(module);
+    }
+    
+    /** Add a module listener. 
+     * 
+     * To keep the dependencies minimal, the loader package only implements the
+     * basic class loading mechanism - but any advanced feature ( management, 
+     * policy, etc ) should be implemented by a module.
+     * 
+     * @param listener
+     */
+    public void addModuleListener(ModuleListener listener) {
+        this.listener=listener;
+    }
+
+    private static void log(String s) {
+        System.err.println("Main: " + s);
+    }
+    
+    private static void log( String msg, Throwable t ) {
+        System.err.println("Main: " + msg);
+        t.printStackTrace();
+    }
+
+}
diff --git a/connectors/util/loader/org/apache/tomcat/util/loader/Module.java b/connectors/util/loader/org/apache/tomcat/util/loader/Module.java
new file mode 100755
index 0000000..9656d29
--- /dev/null
+++ b/connectors/util/loader/org/apache/tomcat/util/loader/Module.java
@@ -0,0 +1,371 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.loader;
+
+
+import java.io.File;
+import java.io.Serializable;
+import java.lang.reflect.Constructor;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+// Based on org.apache.catalina.Loader - removed most of the catalina-specific
+
+/**
+ * Represent one unit of code - jar, webapp, etc. Modules can be reloaded independently,
+ * and may be part of a flat structure or a hierarchy.
+ * 
+ * @author Costin Manolache
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ */
+public class Module implements Serializable {
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * 
+     */
+    public Module() {
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    private static final boolean DEBUG=false; //LoaderProperties.getProperty("loader.Module.debug") != null;
+
+    /**
+     * The class loader being managed by this Loader component.
+     */
+    private transient ModuleClassLoader classLoader = null;
+
+    /**
+     * The "follow standard delegation model" flag that will be used to
+     * configure our ClassLoader.
+     */
+    private boolean delegate = false;
+
+    private Class classLoaderClass;
+
+    /**
+     * The Java class name of the ClassLoader implementation to be used.
+     * This class should extend ModuleClassLoader, otherwise, a different 
+     * loader implementation must be used.
+     */
+    private String loaderClass = null;
+//        "org.apache.catalina.loader.WebappClassLoader";
+
+    /**
+     * The parent class loader of the class loader we will create.
+     * Use Repository if the parent is also a repository, otherwise set 
+     * the ClassLoader
+     */
+    private transient ClassLoader parentClassLoader = null;
+    private Repository parent;
+
+    private Repository repository;
+
+    /**
+     * The set of repositories associated with this class loader.
+     */
+    private String repositories[] = new String[0];
+    private URL classpath[] ;
+
+    private File workDir;
+
+    /**
+     * Has this component been started?
+     */
+    private boolean started = false;
+
+    boolean hasIndex=false;
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the Java class loader to be used by this Container.
+     */
+    public ClassLoader getClassLoader() {
+        return classLoader;
+    }
+
+
+    /**
+     * Return the "follow standard delegation model" flag used to configure
+     * our ClassLoader.
+     */
+    public boolean getDelegate() {
+        return (this.delegate);
+    }
+
+
+    /**
+     * Set the "follow standard delegation model" flag used to configure
+     * our ClassLoader.
+     *
+     * @param delegate The new flag
+     */
+    public void setDelegate(boolean delegate) {
+        boolean oldDelegate = this.delegate;
+        this.delegate = delegate;
+        if( classLoader != null ) classLoader.setDelegate(delegate);
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Has the internal repository associated with this Loader been modified,
+     * such that the loaded classes should be reloaded?
+     */
+    public boolean modified() {
+        return (classLoader.modified());
+    }
+    
+    public boolean isStarted() {
+        return started;
+    }
+
+    public String getClasspathString() {
+        if(classpath==null ) {
+            return null;
+        }
+        StringBuffer sb=new StringBuffer();
+        for( int i=0; i<classpath.length; i++ ) {
+            if( i>0 ) sb.append(":");
+            sb.append( classpath[i].getFile() );
+        }
+        return sb.toString();
+    }
+    
+    /**
+     * Start this component, initializing our associated class loader.
+     *
+     * @exception LifecycleException if a lifecycle error occurs
+     */
+    public void start()  {
+        // Validate and update our current component state
+        if (started)
+            throw new RuntimeException
+                ("Already started");
+        started = true;
+
+        log("start()");
+
+        // Construct a class loader based on our current repositories list
+        try {
+
+            classLoader = createClassLoader();
+
+            //classLoader.setResources(container.getResources());
+            classLoader.setDelegate(this.delegate);
+
+            for (int i = 0; i < repositories.length; i++) {
+                classLoader.addRepository(repositories[i]);
+            }
+
+            classLoader.start();
+
+            getRepository().getLoader().notifyModuleStart(this);
+
+        } catch (Throwable t) {
+            log( "LifecycleException ", t );
+            throw new RuntimeException("start: ", t);
+        }
+
+    }
+
+
+    /**
+     * Stop this component, finalizing our associated class loader.
+     *
+     * @exception LifecycleException if a lifecycle error occurs
+     */
+    public void stop() {
+        if (!started)
+            throw new RuntimeException("stop: started=false");
+        
+        //if (DEBUG) 
+        log("stop()", null);
+        
+        getRepository().getLoader().notifyModuleStop(this);
+        
+        started = false;
+
+        // unregister this classloader in the server group
+        if( repository != null ) repository.removeClassLoader(classLoader);
+
+        // Throw away our current class loader
+        classLoader.stop();
+
+        classLoader = null;
+
+    }
+
+    // ------------------------------------------------------- Private Methods
+
+    /** 
+     * Experiment for basic lifecycle driven by higher layer.
+     * start() and stop() methods will be called on the class when the
+     * module is stopped and started.
+     * 
+     */
+    //public void addModuleClass(String s) {
+        
+    //}
+    
+    /** Set the class used to construct the class loader.
+     * 
+     * The alternative is to set the context class loader to allow loaderClass
+     * to be created. 
+     * 
+     * @param c
+     */
+    public void setClassLoaderClass( Class c ) {
+        classLoaderClass=c;
+    }
+
+    /**
+     * Create associated classLoader.
+     */
+    ModuleClassLoader createClassLoader()
+        throws Exception 
+    {
+
+        if( classLoader != null ) return classLoader;
+        if( classLoaderClass==null && loaderClass!=null) {
+            classLoaderClass = Class.forName(loaderClass);
+        }
+        
+        ModuleClassLoader classLoader = null;
+
+        if (parentClassLoader == null) {
+            if( parent != null ) {
+                parentClassLoader = parent.getClassLoader();
+            }
+        }
+        if (parentClassLoader == null) {
+            parentClassLoader = Thread.currentThread().getContextClassLoader();
+        }
+        
+        if( classLoaderClass != null ) {
+            Class[] argTypes = { URL[].class, ClassLoader.class };
+            Object[] args = { classpath, parentClassLoader };
+            Constructor constr = classLoaderClass.getConstructor(argTypes);
+            classLoader = (ModuleClassLoader) constr.newInstance(args);
+        } else {
+            classLoader=new ModuleClassLoader( classpath, parentClassLoader);
+        }
+        System.err.println("---- Created class loader " + classpath + " " + parentClassLoader + " repo=" + repository.getName() + " " + parent);
+        
+        classLoader.setModule(this);
+        classLoader.setDelegate( delegate );
+        
+        classLoader.start();
+        repository.addClassLoader(classLoader);
+        
+        return classLoader;
+    }
+
+
+    /**
+     * @param parent
+     */
+    public void setParent(Repository parent) {
+        this.parent=parent;
+    }
+
+    /**
+     * @param array
+     */
+    public void setClasspath(URL[] array) {
+        this.classpath=array;
+    }
+    
+    /** Set the path to the module.
+     * In normal use, each module will be associated with one jar or 
+     * classpath dir.
+     * 
+     * @param name
+     */
+    public void setPath(String name) {
+        this.classpath=new URL[1];
+        try {
+            classpath[0]=new URL(name);
+        } catch (MalformedURLException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+    }
+
+
+    /**
+     * @param lg
+     */
+    public void setRepository(Repository lg) {
+        this.repository=lg;
+    }
+
+    /**
+     * Return a String representation of this component.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("ModuleLoader[");
+        sb.append(getClasspathString());
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+    private void log( String s ) {
+        log(s,null);
+    }
+    
+    private void log(String s, Throwable t ) {
+        System.err.println("Module: " + s );
+        if( t!=null)
+            t.printStackTrace();
+    }
+
+
+    /**
+     * @return
+     */
+    public Repository getRepository() {
+        return repository;
+    }
+
+
+    /**
+     * @return
+     */
+    public String getName() {
+        return classpath[0].getFile(); // this.toString();
+    }
+
+
+    public void setParentClassLoader(ClassLoader parentClassLoader2) {
+        this.parentClassLoader=parentClassLoader2;
+    }
+
+
+}
diff --git a/connectors/util/loader/org/apache/tomcat/util/loader/ModuleClassLoader.java b/connectors/util/loader/org/apache/tomcat/util/loader/ModuleClassLoader.java
new file mode 100755
index 0000000..5ae1c31
--- /dev/null
+++ b/connectors/util/loader/org/apache/tomcat/util/loader/ModuleClassLoader.java
@@ -0,0 +1,693 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.loader;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Enumeration;
+import java.util.Vector;
+
+/*
+ * Initially, I started with WebappClassLoader attempting to clean up and
+ * refactor it. Because of complexity and very weird ( and likely buggy ) 
+ * behavior, I moved the other way, starting with URLClassLoader and adding
+ * the functionality from WebappClassLoader. 
+ * 
+ * The current version has a lot of unimplemented WebappClassLoader features and
+ * TODOs - all of them are needed in order to support a single/consistent loader
+ * for webapps and server/modules. 
+ * 
+ * - all ordering options and tricks
+ * - local loading - in case it can be made more efficient than URLCL
+ * - hook to plugin JNDI finder
+ * - ability to add extra permissions to loaded classes
+ * - ability to use work dir for anti-jar locking and generated classes ( incl getURLs)
+ * 
+ * 
+ * Things better kept out:
+ *  - negative cache - it'll waste space with little benefit, most not found classes
+ *  will not be asked multiple times, and most will be in other loaders
+ *  - binaryContent cache - it's unlikely same resource will be loaded multiple
+ * times, and some may be large  
+ * 
+ */
+
+/**
+ * Simple module class loader. Will search the repository if the class is not
+ * found locally.
+ * 
+ * TODO: findResources() - merge all responses from the repo and parent. 
+ *
+ * Based on StandardClassLoader and WebappClassLoader.
+ *   
+ * @author Costin Manolache
+ * @author Remy Maucherat
+ * @author Craig R. McClanahan
+ */
+public class ModuleClassLoader
+    extends URLClassLoader
+{
+    // Don't use commons logging or configs to debug loading - logging is dependent
+    // on loaders and drags a lot of stuff in the classpath 
+    //
+    private static final boolean DEBUG=false; //LoaderProperties.getProperty("loader.debug.ModuleClassLoader") != null;
+    private static final boolean DEBUGNF=false;//LoaderProperties.getProperty("loader.debug.ModuleClassLoaderNF") != null;
+    
+    // ----------------------------------------------------------- Constructors
+
+
+    public ModuleClassLoader(URL repositories[], ClassLoader parent) {
+        super(repositories, parent);
+        if(DEBUG) log( "NEW ModuleClassLoader  " + parent + " " + repositories.length);
+        updateStamp();
+    }
+    
+
+    public ModuleClassLoader(URL repositories[]) {
+        super(repositories);
+        if(DEBUG) log( "NEW ModuleClassLoader  -null-"+ " " + repositories.length);
+        updateStamp();
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    protected Repository repository;
+
+    /**
+     * Should this class loader delegate to the parent class loader
+     * <strong>before</strong> searching its own repositories (i.e. the
+     * usual Java2 delegation model)?  If set to <code>false</code>,
+     * this class loader will search its own repositories first, and
+     * delegate to the parent only if the class or resource is not
+     * found locally.
+     */
+    protected boolean delegate = false;
+
+    /**
+     * Last time a JAR was accessed. 
+     * TODO: change to last time the loader was accessed
+     */
+    protected long lastJarAccessed = 0L;
+
+    protected long lastModified=0L;
+    
+    /**
+     * Has this component been started?
+     */
+    protected boolean started = false;
+
+    protected Module module;
+
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Set the "delegate first" flag for this class loader.
+     *
+     * @param delegate The new "delegate first" flag
+     */
+    void setDelegate(boolean delegate) {
+        this.delegate = delegate;
+    }
+
+    void setRepository(Repository lg ) {
+        this.repository=lg;
+    }
+
+    void setModule(Module webappLoader) {
+        this.module=webappLoader;
+    }
+
+    /** Not public - his can only be called from package.
+     *  To get the module from a ClassLoader you need access to the Loader
+     * instance.
+     * 
+     * @return
+     */
+    Module getModule() {
+        return module;
+    }
+
+    void setWorkDir(File s) {
+        // TODO
+    }
+
+    /**
+     * Add a new repository to the set of places this ClassLoader can look for
+     * classes to be loaded.
+     *
+     * @param repository Name of a source of classes to be loaded, such as a
+     *  directory pathname, a JAR file pathname, or a ZIP file pathname
+     *
+     * @exception IllegalArgumentException if the specified repository is
+     *  invalid or does not exist
+     */
+    void addRepository(String repository) {
+        // Add this repository to our underlying class loader
+        try {
+            boolean mod=modified();
+            URL url = new URL(repository);
+            super.addURL(url);
+            if( ! mod ) {
+                // don't check if it is modified, so it works
+                updateStamp();
+            }
+        } catch (MalformedURLException e) {
+            IllegalArgumentException iae = new IllegalArgumentException
+                ("Invalid repository: " + repository); 
+            iae.initCause(e);
+            //jdkCompat.chainException(iae, e);
+            throw iae;
+        }
+    }
+
+    void updateStamp() {
+        URL cp[]=super.getURLs();
+        if (cp != null ) {
+            for (int i = 0; i <cp.length; i++) {
+                File f=new File(cp[i].getFile());
+                long lm=f.lastModified();
+                if( lm > lastModified ) lastModified=lm;
+            }
+        }
+    }
+    
+    private boolean dirCheck(File dir ) {
+        //log("Checking " + dir );
+        File subd[]=dir.listFiles();
+        for( int i=0; i< subd.length; i++ ) {
+            long lm=subd[i].lastModified();
+            if( lm > lastModified ) {
+                log("Modified file: " + dir + " " + subd[i] + " " + lm + " " + lastModified);
+                return true;
+            }
+            if( subd[i].isDirectory() ) {
+                return  dirCheck(subd[i]);
+            }
+        }
+        return false;
+    }
+    
+    /**
+     * Have one or more classes or resources been modified so that a reload
+     * is appropriate?
+     * 
+     * Not public - call it via Module
+     */
+    boolean modified() {
+        URL cp[]=super.getURLs();
+        if (cp != null ) {
+            for (int i = 0; i <cp.length; i++) {
+                File f=new File(cp[i].getFile());
+                long lm=f.lastModified();
+                if( lm > lastModified ) {
+                    log( "Modified file: " + f + " " + lm + " " + lastModified);
+                    return true;
+                }
+                // assume dirs are used only for debug and small
+                if( f.isDirectory() ) {
+                    return dirCheck(f);
+                }
+            }
+        }
+
+        if (DEBUG)
+            log("modified() false");
+
+        // TODO - check at least the jars 
+        return (false);
+    }
+
+    // ---------------------------------------------------- ClassLoader Methods
+
+
+    /**
+     * Find the specified class in our local repositories, if possible.  If
+     * not found, throw <code>ClassNotFoundException</code>.
+     *
+     * @param name Name of the class to be loaded
+     *
+     * @exception ClassNotFoundException if the class was not found
+     */
+    public Class findClass(String name) throws ClassNotFoundException {
+        return findClass2(name, true);
+    }
+    
+    public Class findClass2(String name, boolean del2repo) throws ClassNotFoundException {
+        if( del2repo ) {
+            return ((RepositoryClassLoader)repository.getClassLoader()).findClass(name);            
+        } // else 
+ 
+        Class clazz = null;
+            
+        try {
+            clazz = super.findClass(name);
+        } catch (RuntimeException e) {
+            if (DEBUG)
+                log("findClass() -->RuntimeException " + name, e);
+            throw e;
+        } catch( ClassNotFoundException ex ) {
+            URL cp[]=this.getURLs();
+            if (DEBUGNF)
+                log("findClass() NOTFOUND  " + name + " " + (( cp.length > 0 ) ? cp[0].toString() : "") );
+            throw ex;
+        }
+            
+        if (clazz == null) { // does it ever happen ? 
+            if (DEBUGNF)
+                log("findClass() NOTFOUND throw CNFE " + name);
+            throw new ClassNotFoundException(name);
+        }
+
+        // Return the class we have located
+        if (DEBUG) {
+            if( clazz.getClassLoader() != this ) 
+                log("findClass() FOUND " + clazz + " Loaded by " + clazz.getClassLoader());
+            else 
+                log("findClass() FOUND " + clazz );
+        }
+        return (clazz);
+    }
+    
+    /** Same as findClass, but also checks if the class has been previously 
+     * loaded.
+     * 
+     * In most implementations, findClass() doesn't check with findLoadedClass().
+     * In order to implement repository, we need to ask each loader in the group
+     * to load only from it's local resources - however this will lead to errors
+     * ( duplicated definition ) if findClass() is used.
+     *
+     * @param name
+     * @return
+     * @throws ClassNotFoundException
+     */
+    public Class findLocalClass(String name) throws ClassNotFoundException
+    {
+        Class clazz = findLoadedClass(name);
+        if (clazz != null) {
+            if (DEBUG)
+                log("findLocalClass() - FOUND " + name);
+            return (clazz);
+        }
+        
+        return findClass(name);
+    }
+
+
+
+    
+    /**
+     * Find the specified resource in our local repository, and return a
+     * <code>URL</code> refering to it, or <code>null</code> if this resource
+     * cannot be found.
+     *
+     * @param name Name of the resource to be found
+     */
+    public URL findResource(final String name) {
+        return findResource2( name, true);
+    }
+        
+    public URL findResource2(final String name, boolean del2repo ) {
+        if( del2repo ) {
+            return ((RepositoryClassLoader)repository.getClassLoader()).findResource(name);
+        } // else:
+
+        URL url = null;
+
+        url = super.findResource(name);
+        
+        if(url==null) {
+            // try the repository
+            // TODO
+        }
+        
+        if (url==null && DEBUG) {
+            if (DEBUGNF) log("findResource() NOTFOUND " + name );
+            return null;
+        }
+
+        if (DEBUG) log("findResource() found " + name + " " + url );
+        return (url);
+    }
+
+
+    /**
+     * Return an enumeration of <code>URLs</code> representing all of the
+     * resources with the given name.  If no resources with this name are
+     * found, return an empty enumeration.
+     *
+     * @param name Name of the resources to be found
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public Enumeration findResources(String name) throws IOException {
+        return findResources2(name, true);
+    }
+    
+    Enumeration findResources2(String name, boolean del2repo) throws IOException {
+        if( del2repo ) {
+            return ((RepositoryClassLoader)repository.getClassLoader()).findResources(name);
+        } else {
+            return super.findResources(name);
+        }
+    }
+
+    // Next methods implement the search alghoritm - parent, repo, delegation, etc 
+
+    /** getResource() - modified to implement the search alghoritm 
+     * 
+     */
+    public URL getResource(String name) {
+        return getResource2( name, null, true);
+    }
+
+    /** getResource() - same thing, but don't delegate to repo if called 
+     * from repo 
+     * 
+     */
+    URL getResource2(String name, ClassLoader originator, boolean delegate2repo ) {
+
+        URL url = null;
+
+        // (1) Delegate to parent if requested
+        if (delegate) {
+            url=getResourceParentDelegate(name);
+            if(url!=null ) return url;
+        }
+
+        // (2) Search local repositories
+        url = findResource(name);
+        if (url != null) {
+            // TODO: antijar locking - WebappClassLoader is making a copy ( is it ??)
+            if (DEBUG)
+                log("getResource() found locally " + delegate + " " + name + " " + url);
+            return (url);
+        }
+
+        // Finally, try the group loaders ( via super() in StandardClassLoader ).
+        // not found using normal loading mechanism. load from one of the classes in the group
+        if( delegate2repo && repository!=null ) {
+            url=repository.findResource(this, name);
+            if(url!=null ) {
+                if( DEBUG )
+                    log("getResource() FOUND from group " + repository.getName() + " " + name + " " + url);
+                return url;
+            }
+        }
+
+        // (3) Delegate to parent unconditionally if not already attempted
+        if( !delegate ) {
+            url=getResourceParentDelegate(name);
+            if(url!=null ) return url;
+        }
+
+        
+        // (4) Resource was not found
+        if (DEBUGNF)
+            log("getResource() NOTFOUND  " + delegate + " " + name + " " + url);
+        return (null);
+
+    }
+
+    
+    // to avoid duplication - get resource from parent, when delegating
+    private URL getResourceParentDelegate(String name) {
+        URL url=null;
+        ClassLoader loader = getParent();
+        
+        if (loader == null) {
+            loader = getSystemClassLoader();
+            if (url != null) {
+                if (DEBUG)
+                    log("getResource() found by system " +  delegate + " " + name + " " + url);
+                return (url);
+            }
+        } else {
+            url = loader.getResource(name);
+            if (url != null) {
+                if (DEBUG)
+                    log("getResource() found by parent " +  delegate + " " + name + " " + url);
+                return (url);
+            }
+        }
+        if( DEBUG ) log("getResource not found by parent " + loader);
+
+        return url;
+    }
+    
+    /**
+     * Load the class with the specified name, searching using the following
+     * algorithm until it finds and returns the class.  If the class cannot
+     * be found, returns <code>ClassNotFoundException</code>.
+     * <ul>
+     * <li>Call <code>findLoadedClass(String)</code> to check if the
+     *     class has already been loaded.  If it has, the same
+     *     <code>Class</code> object is returned.</li>
+     * <li>If the <code>delegate</code> property is set to <code>true</code>,
+     *     call the <code>loadClass()</code> method of the parent class
+     *     loader, if any.</li>
+     * <li>Call <code>findClass()</code> to find this class in our locally
+     *     defined repositories.</li>
+     * <li>Call the <code>loadClass()</code> method of our parent
+     *     class loader, if any.</li>
+     * </ul>
+     * If the class was found using the above steps, and the
+     * <code>resolve</code> flag is <code>true</code>, this method will then
+     * call <code>resolveClass(Class)</code> on the resulting Class object.
+     *
+     * @param name Name of the class to be loaded
+     * @param resolve If <code>true</code> then resolve the class
+     *
+     * @exception ClassNotFoundException if the class was not found
+     */
+    public Class loadClass(String name, boolean resolve)
+        throws ClassNotFoundException
+    {
+        return loadClass2( name, resolve, true );
+    }
+    
+    public Class loadClass2(String name, boolean resolve, boolean del2repo)
+        throws ClassNotFoundException
+    {
+
+        Class clazz = null;
+
+        // Don't load classes if class loader is stopped
+        if (!started) {
+            log("Not started " + this + " " + module);
+            //throw new ThreadDeath();
+            start();
+        }
+
+        // (0) Check our previously loaded local class cache
+        clazz = findLoadedClass(name);
+        if (clazz != null) {
+            if (DEBUG)
+                log("loadClass() FOUND findLoadedClass " + name + " , " + resolve);
+            if (resolve) resolveClass(clazz);
+            return (clazz);
+        }
+
+        // (0.2) Try loading the class with the system class loader, to prevent
+        //       the webapp from overriding J2SE classes
+        try {
+            clazz = getSystemClassLoader().loadClass(name);
+            if (clazz != null) {
+                // enabling this can result in ClassCircularityException
+//                if (DEBUG)
+//                    log("loadClass() FOUND system " + name + " , " + resolve);
+                if (resolve) resolveClass(clazz);
+                return (clazz);
+            }
+        } catch (ClassNotFoundException e) {
+            // Ignore
+        }
+
+        // TODO: delegate based on filter
+        boolean delegateLoad = delegate;// || filter(name);
+
+        // (1) Delegate to our parent if requested
+        if (delegateLoad) {
+
+            ClassLoader loader = getParent();
+            if( loader != null ) {
+                try {
+                    clazz = loader.loadClass(name);
+                    if (clazz != null) {
+                        if (DEBUG)
+                            log("loadClass() FOUND by parent " + delegate + " " + name + " , " + resolve);
+                        if (resolve)
+                            resolveClass(clazz);
+                        return (clazz);
+                    }
+                } catch (ClassNotFoundException e) {
+                    ;
+                }
+            }
+        }
+
+        // (2) Search local repositories
+        try {
+            clazz = findClass(name);
+            if (clazz != null) {
+                if (DEBUG)
+                    log("loadClass - FOUND findClass " + delegate + " " + name + " , " + resolve);
+                if (resolve) resolveClass(clazz);
+                return (clazz);
+            }
+        } catch (ClassNotFoundException e) {
+            ;
+        }
+
+        // Finally, try the group loaders ( via super() in StandardClassLoader ).
+        // not found using normal loading mechanism. load from one of the classes in the group
+        if( del2repo && repository!=null ) {
+            Class cls=repository.findClass(this, name);
+            if(cls!=null ) {
+                if( DEBUG )
+                    log("loadClass(): FOUND from group " + repository.getName() + " " + name);
+                if (resolve) resolveClass(clazz);
+                return cls;
+            }
+        }
+
+        // (3) Delegate to parent unconditionally
+        if (!delegateLoad) {
+            ClassLoader loader = getParent();
+            if( loader != null ) {
+                try {
+                    clazz = loader.loadClass(name);
+                    if (clazz != null) {
+                        if (DEBUG)
+                            log("loadClass() FOUND parent " + delegate + " " + name + " , " + resolve);
+                        if (resolve) resolveClass(clazz);
+                        return (clazz);
+                    }
+                } catch (ClassNotFoundException e) {
+                    ;
+                }
+            }
+        }
+
+        if( DEBUGNF ) log("loadClass(): NOTFOUND " + name + " xxx " + getParent() +  " " + repository.getName() );
+        throw new ClassNotFoundException(name);
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+
+    /**
+     * Start the class loader.
+     *
+     * @exception LifecycleException if a lifecycle error occurs
+     */
+    void start()  {
+
+        started = true;
+
+    }
+
+    /** Support for "disabled" state.
+    *
+    * @return
+    */
+    boolean isStarted() {
+        return started;
+    }
+
+
+    /**
+     * Stop the class loader.
+     *
+     * @exception LifecycleException if a lifecycle error occurs
+     */
+    void stop() {
+
+        started = false;
+
+    }
+
+
+
+
+    /**
+     * Validate a classname. As per SRV.9.7.2, we must restict loading of 
+     * classes from J2SE (java.*) and classes of the servlet API 
+     * (javax.servlet.*). That should enhance robustness and prevent a number
+     * of user error (where an older version of servlet.jar would be present
+     * in /WEB-INF/lib).
+     * 
+     * @param name class name
+     * @return true if the name is valid
+     */
+    protected boolean validate(String name) {
+
+        if (name == null)
+            return false;
+        if (name.startsWith("java."))
+            return false;
+
+        return true;
+
+    }
+
+
+    // ------------------ Local methods ------------------------
+
+    private void log(String s ) {
+        System.err.println("ModuleClassLoader: " + s);
+    }
+    private void log(String s, Throwable t ) {
+        System.err.println("ModuleClassLoader: " + s);
+        t.printStackTrace();
+    }
+    
+    Object debugObj=new Object();
+
+    /**
+     * Render a String representation of this object.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("ModuleCL ");
+        sb.append(debugObj).append(" delegate: ");
+        sb.append(delegate);
+        //sb.append("\r\n");
+        sb.append(" cp: ");
+        URL cp[]=super.getURLs();
+        if (cp != null ) {
+            for (int i = 0; i <cp.length; i++) {
+                sb.append("  ");
+                sb.append(cp[i].getFile());
+            }
+        }
+        if (getParent() != null) {
+            sb.append("\r\n----------> Parent: ");
+            sb.append(getParent().toString());
+            sb.append("\r\n");
+        }
+        return (sb.toString());
+    }
+}
+
diff --git a/connectors/util/loader/org/apache/tomcat/util/loader/ModuleListener.java b/connectors/util/loader/org/apache/tomcat/util/loader/ModuleListener.java
new file mode 100644
index 0000000..6f3945e
--- /dev/null
+++ b/connectors/util/loader/org/apache/tomcat/util/loader/ModuleListener.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.loader;
+
+/**
+ * Interface providing notifications on Module events.
+ * 
+ * @author Costin Manolache
+ */
+public interface ModuleListener {
+    
+    /** Called when a module group is created. This is only called when a new group
+     * is added to a running engine - we may cache a the list of groups and reuse 
+     * it on restarts. 
+     * 
+     * @param manager
+     */
+    public void repositoryAdd( Repository manager );
+    
+    /** Notification that a module has been added. You can get the group
+     * with getGroup().
+     * 
+     * Adding a module doesn't imply that the module is started or the class loader
+     * created - this happens only on start() ( TODO: or when a class is accessed ? ).
+     * 
+     * This callback is only called for new modules, deployed while the engine is 
+     * running, or in some cases when the module engine is reseting the cache. For
+     * old modules - you need to get a list from the ModuleGroup.
+     * 
+     * 
+     * @param module
+     */
+    public void moduleAdd( Module module );
+    
+    /** Notification that a module has been removed.
+     * 
+     * @param module
+     */
+    public void moduleRemove( Module module );
+    
+    /** Module reload - whenever reload happens, a reload notification will be generated.
+     * Sometimes a remove/add will do the same.
+     * 
+     * @param module
+     */
+    public void moduleReload( Module module );
+    
+    /** Called when a module is started. 
+     * 
+     * This is called after the class loader is created, to allow listeners to use classes
+     * from the module to initialize.
+     * 
+     * I think it would be good to have the module 'started' on first class loaded
+     * and 'stopped' explicitely.
+     * 
+     * @param module
+     */
+    public void moduleStart(Module module);
+    
+    /** 
+     *  Called when a module is stopped. Stoping a module will stop the class
+     * loader and remove all references to it. When a module is stopped, all attempts
+     * to load classes will result in exceptions. 
+     * 
+     * The callback is called before the class loader is stopped - this allows listeners
+     * to use classes from the module to deinitialize.
+     * 
+     * @param module
+     */
+    public void moduleStop(Module module);
+    
+    /** Pass a reference to the loader. 
+     * 
+     * This is the only supported way to get it - no static methods are 
+     * provided. From loader you can control all repositories and modules.
+     * 
+     * Note that ModuleClassLoader does not provide a way to retrieve the Module -
+     * you need to have a reference to the Loader to get the Module for a 
+     * ClassLoader. 
+     * @param main
+     */
+    public void setLoader(Loader main);
+
+    /** Start the listener.
+     * TODO: this is only used by Loader to pass control to the listener - 
+     * instead of introspection for main()
+     */
+    public void start();
+
+}
\ No newline at end of file
diff --git a/connectors/util/loader/org/apache/tomcat/util/loader/Repository.java b/connectors/util/loader/org/apache/tomcat/util/loader/Repository.java
new file mode 100755
index 0000000..d71b61e
--- /dev/null
+++ b/connectors/util/loader/org/apache/tomcat/util/loader/Repository.java
@@ -0,0 +1,481 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.loader;
+
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+
+/**
+ * A group of modules and libraries. 
+ * 
+ * Modules can have one or more jars and class dirs. Classloaders are created 
+ * from modules when the module is started are be disposed when the module is stopped.
+ * 
+ * The module will delegate to the associated repository in addition to the 
+ * normal delegation rules. The repository will search on all sibling modules.
+ * This mechanism is defined in the MLetClassLoader and is also used by JBoss and
+ * few other servers. 
+ * 
+ * TODO: explain more ( or point to the right jboss/mlet pages )
+ * TODO: explain how this can be used for webapps to support better partitioning 
+ *
+ * @author Costin Manolache
+ */
+public class Repository {
+   
+    private static final boolean DEBUG=Loader.getProperty("loader.debug.Repository") != null;
+    
+    // Allows the (experimental) use of jar indexes
+    // Right now ( for small set of jars, incomplete build ) it's a tiny 3.5 -> 3.4 sec dif.
+    private static final boolean USE_IDX=Loader.getProperty("loader.Repository.noIndex") == null;
+    
+    private Vector loaders=new Vector();
+    private String name;
+    private Vector grpModules=new Vector();
+    private transient Loader loader;
+    
+    private transient RepositoryClassLoader groupClassLoader;
+    private Hashtable prefixes=new Hashtable();
+
+    // For delegation
+    private ClassLoader parentClassLoader;
+    private Repository parent;
+
+
+    private Repository() {
+    }
+
+    public Repository(Loader loader) {
+        if( loader== null ) throw new NullPointerException();
+        this.loader=loader;
+    }
+
+    public Loader getLoader() {
+        return loader;
+    }
+    
+    void addModule(  Module mod ) {
+        mod.setRepository( this );
+
+        grpModules.addElement(mod);
+        if( loader.listener!=null ) {
+            loader.listener.moduleAdd(mod);
+        }
+        
+        if( parentClassLoader != null ) 
+            mod.setParentClassLoader( parentClassLoader );
+
+        if(! mod.isStarted()) {
+            mod.start();
+            //log("started " + mod);
+        } else {
+            //log("already started " + mod);
+        }
+        
+        try {
+            if( USE_IDX ) {
+                processJarIndex(mod);
+                // TODO: if we are in the initial starting, write cache only once
+                // TODO: write it only if there is a change in the timestamp
+                writeCacheIdx();
+            }
+        } catch (Exception e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+        
+    }
+    
+    public void newModule( String path ) {
+        Module m=new Module();
+        m.setPath( path );
+        addModule( m );
+    }
+    
+    public Enumeration getModules() {
+        return grpModules.elements();
+    }
+    
+    /** Reload any module that is modified
+     */
+    public void checkReload() {
+        try {
+        Enumeration mE=grpModules.elements();
+        while( mE.hasMoreElements() ) {
+            Module m=(Module)mE.nextElement();
+            boolean modif=m.modified();
+            log("Modified " + m + " " + modif);
+            
+            if( modif ) {
+                m.stop();
+                m.start();
+            }
+        }
+        } catch( Throwable t ) {
+            t.printStackTrace();
+        }
+    }
+
+    /** Verify if any module is modified. This is a deep search, including dirs.
+     *  Expensive operation.
+     *  
+     * @return
+     */
+    public boolean isModified() {
+        try {
+            Enumeration mE=grpModules.elements();
+            while( mE.hasMoreElements() ) {
+                Module m=(Module)mE.nextElement();
+                boolean modif=m.modified();
+                log("Modified " + m + " " + modif);
+                if( modif ) return true;
+            }
+        } catch( Throwable t ) {
+            t.printStackTrace();
+        }
+        return false;
+    }
+    
+    Repository getParent() {
+        return parent;
+    }
+    
+    public String toString() {
+        return "Repository " + name + "(" + getClasspathString() + ")";
+    }
+
+    private String getClasspathString() {
+        StringBuffer sb=new StringBuffer();
+        Enumeration mE=grpModules.elements();
+        while( mE.hasMoreElements() ) {
+            Module m=(Module)mE.nextElement();
+            sb.append( m.getClasspathString() + ":");
+        }
+        return sb.toString();
+    }
+
+    /**
+     * 
+     * @param parent The parent group
+     */
+    public void setParent(Repository parent) {
+        this.parent = parent;
+    }
+    
+    /** Set the parent class loader - can be used instead of setParent, 
+     * in case this is the top loader and needs to delagate to embedding app
+     * 
+     * @param myL
+     */
+    public void setParentClassLoader(ClassLoader myL) {
+        this.parentClassLoader=myL;
+    }
+
+
+    /** Add a class loder to the group.
+     *
+     *  If this is a StandardClassLoader instance, it will be able to delegate
+     * to the group.
+     *
+     *  If it's a regular ClassLoader - it'll be searched for classes, but
+     * it will not be able to delegate to peers.
+     *
+     * In future we may fine tune this by using manifests.
+     */
+    void addClassLoader(ClassLoader cl ) {
+        if( ( cl instanceof ModuleClassLoader )) {
+            ((ModuleClassLoader)cl).setRepository(this);
+        }
+        loaders.addElement(cl);
+        //    log("Adding classloader " + cl);
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void removeClassLoader(ClassLoader cl) {
+        int oldSize=loaders.size();
+        loaders.removeElement(cl);
+
+        if(DEBUG) log("removed " + loaders.size() + "/" + oldSize + ": "  + cl);
+        // TODO: remove from index
+    }
+
+    /** Return a class loader associated with the group.
+     *  This will delegate to all modules in the group, then to parent.
+     * 
+     * @return
+     */
+    public ClassLoader getClassLoader() {
+        if( groupClassLoader==null ) {
+            
+            ClassLoader pcl=parentClassLoader;
+            if( pcl==null && parent!=null ) {
+                pcl=parent.getClassLoader();
+            } 
+            if( pcl==null ) {
+                pcl=Thread.currentThread().getContextClassLoader();
+             }
+
+            if( pcl == null ) {
+                // allow delegation to embedding app
+                groupClassLoader=new RepositoryClassLoader(new URL[0], this);
+            } else {
+                groupClassLoader=new RepositoryClassLoader(new URL[0], pcl, this);
+            }
+            if( DEBUG ) log("---------- Created repository loader " + pcl );
+        }
+        return groupClassLoader;
+    }
+    
+    /** 
+     * Find a class in the group. It'll iterate over each loader
+     * and try to find the class - using only the method that
+     * search locally or on parent ( i.e. not in group, to avoid
+     * recursivity ).
+     *
+     *
+     * @param classN
+     * @return
+     */
+    Class findClass(ClassLoader caller, String classN ) {
+        Class clazz=null;
+        
+        // do we have it in index ?
+        if( USE_IDX ) {
+            int lastIdx=classN.lastIndexOf(".");
+            String prefix=(lastIdx>0) ? classN.substring(0, lastIdx) : classN;
+            Object mO=prefixes.get(prefix.replace('.', '/'));
+            if( mO!=null ) {
+                if( mO instanceof Module ) {
+                    Module m=(Module)mO;
+                    try {
+                        Class c=((ModuleClassLoader)m.getClassLoader()).findLocalClass(classN);
+                        //log("Prefix: " +prefix + " " + classN  + " " + m);
+                        return c;
+                    } catch (Exception e) {
+                        //log("Prefix err: " +prefix + " " + classN  + " " + m + " " + e);
+                        //return null;
+                    }
+                } else {
+                    Module mA[]=(Module[])mO;
+                    for( int i=0; i<mA.length; i++ ) {
+                        Module m=mA[i];
+                        try {
+                            Class c=((ModuleClassLoader)m.getClassLoader()).findLocalClass(classN);
+                            //log("Prefix: " +prefix + " " + classN  + " " + m);
+                            return c;
+                        } catch (Exception e) {
+                            //log("Prefix err: " +prefix + " " + classN  + " " + m + " " + e);
+                            //return null;
+                        }
+                    }
+                }
+            }
+        }
+
+        // TODO: move the vector to a []
+        for( int i=loaders.size()-1; i>=0; i-- ) {
+            
+            // TODO: for regular CL, just use loadClass, they'll not recurse
+            // The behavior for non-SCL or not in the group loader is the same as for parent loader
+            ModuleClassLoader cl=(ModuleClassLoader)loaders.elementAt(i);
+            // TODO: move loaders with index in separate vector
+            //if( cl.getModule().hasIndex ) continue;
+            if( cl== caller ) continue;
+            //if( classN.indexOf("SmtpCoyoteProtocolHandler") > 0 ) {
+            //log("try " + cl.debugObj + " " + name + " " + classN + " " + loaders.size());
+            //}
+            try {
+                if( cl instanceof ModuleClassLoader ) {
+                    clazz=((ModuleClassLoader)cl).findLocalClass(classN );
+                } else {
+                    clazz=cl.findClass(classN);
+                }
+
+                //System.err.println("GRPLD: " + classN + " from " + info.get(cl));
+                return clazz;
+            } catch (ClassNotFoundException e) {
+                //System.err.println("CNF: " + classN + " " + info.get(cl) );
+                //if( classN.indexOf("smtp") > 0 ) e.printStackTrace();
+            }
+        }
+        return null;
+    }
+    
+    /**
+     * @param loader
+     * @param name2
+     * @return
+     */
+    URL findResource(ModuleClassLoader caller, String classN) {
+        URL url=null;
+        if( DEBUG ) log("Repository.findResource " + classN + " " + caller );
+        for( int i=loaders.size()-1; i>=0; i-- ) {
+            // TODO: for regular CL, just use loadClass, they'll not recurse
+            // The behavior for non-SCL or not in the group loader is the same as for parent loader
+            ModuleClassLoader cl=(ModuleClassLoader)loaders.elementAt(i);
+            if( cl== caller ) continue;
+            url=((ModuleClassLoader)cl).findResource(classN );
+            if( url!=null )
+                return url;
+        }
+        return null;
+    }
+
+    private void log(String s) {
+        System.err.println("Repository (" + name + "): " + s );
+    }
+
+    /**
+     * @param name2
+     */
+    public void setName(String name2) {
+        this.name=name2;
+    }
+
+    /*
+     * Work in progress: 
+     * 
+     * -use the INDEX.LIST to get prefixes to avoid linear
+     * search in repositories.
+     * 
+     * - serialize the state ( including timestamps ) to improve startup time
+     * ( avoids the need to open all jars - if INDEX.LIST is ok)
+     */
+    
+    /**
+     * Read the index. The index contain packages and top level resources
+     * 
+     * @param cl
+     * @throws Exception
+     */
+    private void processJarIndex(Module m) throws Exception {
+        ModuleClassLoader cl=(ModuleClassLoader)m.createClassLoader();
+        // only support index for modules with a single jar in CP
+        String cp=m.getClasspathString();
+        if( ! cp.endsWith(".jar")) return;
+        URL urlIdx=cl.findResource("META-INF/INDEX.LIST");
+        if( urlIdx == null ) {
+            log("INDEX.LIST not found, run: jar -i " + m.getClasspathString());
+            return;
+        }
+        try {
+            InputStream is=urlIdx.openStream();
+            if( is==null ) {
+                log("Can't read " + urlIdx + " " + m.getClasspathString());
+                return;
+            }
+            BufferedReader br=new BufferedReader( new InputStreamReader(is) );
+            String line=br.readLine();
+            if( line==null ) return;
+            if( ! line.startsWith( "JarIndex-Version:") ||  
+                    ! line.endsWith("1.0")) {
+                log("Invalid signature " + line + " " + m.getClasspathString());
+            }
+            br.readLine(); // ""
+            
+            while( readSection(br, m) ) {
+            }
+           
+            m.hasIndex=true;
+        } catch (IOException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+    }
+    
+    private boolean readSection( BufferedReader br, Module m) throws IOException {
+        String jarName=br.readLine();
+        if( jarName==null ) return false; // done
+        if( "".equals( jarName )) {
+            log("Invalid jarName " + jarName + " " + m.getClasspathString() );
+            return false;
+        }
+        //log("Index " + jarName + " " + m.getClasspathString());
+        String prefix=null;
+        while( ((prefix=br.readLine()) != null ) && 
+                (! "".equals( prefix )) ) {
+            //log("found " + prefix + " " + m);
+            Object o=prefixes.get(prefix);
+            if( o == null ) {
+                prefixes.put(prefix, m);
+            } else {
+                Module mA[]=null;
+                if( o instanceof Module ) {
+                    mA=new Module[2];
+                    mA[0]=(Module)o;
+                    mA[1]=m;
+                } else {
+                    Object oldA[]=(Module[])o;
+                    mA=new Module[oldA.length + 1];
+                    System.arraycopy(oldA, 0, mA, 0, oldA.length);
+                    mA[oldA.length]=m;
+                }
+                prefixes.put( prefix, mA);
+                //log("Multiple prefixes: " + prefix + " " + mA);
+                
+            }
+        }
+        
+        return prefix!=null;
+    }
+
+
+    /** Read loader.REPO.cache from work dir
+     * 
+     * This file will hold timestamps for each module/jar and cache the INDEX -
+     * to avoid opening the jars/modules that are not used 
+     * 
+     * @throws IOException
+     */
+    private void readCachedIdx() throws IOException {
+        
+    }
+    
+    /** Check the index and verify that:
+     * - all jars are older than timestamp and still exist
+     * - there are no new jars 
+     * 
+     * @throws IOException
+     */
+    private void checkCacheIdx() throws IOException {
+        
+    }
+    
+    
+    private void writeCacheIdx() throws IOException {
+        // For each module we write the timestamp, filename then the index
+        // The idea is to load this single file to avoid scanning many jars
+
+        // we'll use the cache 
+        
+        
+    }
+
+}
diff --git a/connectors/util/loader/org/apache/tomcat/util/loader/RepositoryClassLoader.java b/connectors/util/loader/org/apache/tomcat/util/loader/RepositoryClassLoader.java
new file mode 100644
index 0000000..64a38f3
--- /dev/null
+++ b/connectors/util/loader/org/apache/tomcat/util/loader/RepositoryClassLoader.java
@@ -0,0 +1,254 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.loader;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Enumeration;
+import java.util.Vector;
+
+/**
+ * Class loader associated with a repository ( common, shared, server, etc ). 
+ *
+ * This class loader will never load any class by itself ( since it has no repository ),
+ * it will just delegate to modules. 
+ * 
+ * Refactored as a separate class to make the code cleaner.
+ * Based on catalina loader.
+ *   
+ * @author Costin Manolache
+ * @author Remy Maucherat
+ * @author Craig R. McClanahan
+ */
+public class RepositoryClassLoader
+    extends URLClassLoader
+{
+    private static final boolean DEBUG=false; //LoaderProperties.getProperty("loader.debug.ModuleClassLoader") != null;
+    private static final boolean DEBUGNF=false;//LoaderProperties.getProperty("loader.debug.ModuleClassLoaderNF") != null;
+    
+    // ----------------------------------------------------------- Constructors
+
+    public RepositoryClassLoader(URL repositories[], ClassLoader parent, Repository lg) {
+        super(repositories, parent);
+        this.repository=lg;
+    }
+    
+
+    public RepositoryClassLoader(URL repositories[], Repository lg) {
+        super(repositories);
+        this.repository=lg;
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    private Repository repository;
+
+    // ---------------------------------------------------- ClassLoader Methods
+
+
+    /**
+     * Find the specified class in our local repositories, if possible.  If
+     * not found, throw <code>ClassNotFoundException</code>.
+     *
+     * @param name Name of the class to be loaded
+     *
+     * @exception ClassNotFoundException if the class was not found
+     */
+    public Class findClass(String name) throws ClassNotFoundException {
+
+        Class clazz = null;
+        
+        Enumeration modulesE=repository.getModules();
+        while( modulesE.hasMoreElements() ) {
+            try {
+                Module m=(Module)modulesE.nextElement();
+                return ((ModuleClassLoader)m.getClassLoader()).findClass2(name, false);
+            } catch( ClassNotFoundException ex ) {
+                // ignore
+            }
+        }
+        throw new ClassNotFoundException( name );
+
+    }
+    
+    /** Same as findClass, but also checks if the class has been previously 
+     * loaded.
+     * 
+     * In most implementations, findClass() doesn't check with findLoadedClass().
+     * In order to implement repository, we need to ask each loader in the group
+     * to load only from it's local resources - however this will lead to errors
+     * ( duplicated definition ) if findClass() is used.
+     *
+     * @param name
+     * @return
+     * @throws ClassNotFoundException
+     */
+    public Class findLocalClass(String name) throws ClassNotFoundException
+    {
+        Enumeration modulesE=repository.getModules();
+        while( modulesE.hasMoreElements() ) {
+            try {
+                Module m=(Module)modulesE.nextElement();
+                return ((ModuleClassLoader)m.getClassLoader()).findLocalClass(name);
+            } catch( ClassNotFoundException ex ) {
+                // ignore
+            }
+        }
+        throw new ClassNotFoundException( name );
+    }
+
+
+
+    
+    /**
+     * Find the specified resource in our local repository, and return a
+     * <code>URL</code> refering to it, or <code>null</code> if this resource
+     * cannot be found.
+     *
+     * @param name Name of the resource to be found
+     */
+    public URL findResource(final String name) {
+        URL url = null;
+        Enumeration modulesE=repository.getModules();
+        while( modulesE.hasMoreElements() ) {
+                Module m=(Module)modulesE.nextElement();
+                url=((ModuleClassLoader)m.getClassLoader()).findResource2(name, false);
+                if( url!= null ) {
+                    return url;
+                }
+        }
+        
+        if (url==null && DEBUG) {
+            if (DEBUGNF) log("findResource() NOTFOUND " + name );
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Return an enumeration of <code>URLs</code> representing all of the
+     * resources with the given name.  If no resources with this name are
+     * found, return an empty enumeration.
+     *
+     * @param name Name of the resources to be found
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public Enumeration findResources(String name) throws IOException {
+        Vector result=new Vector();
+        
+        Enumeration modulesE=repository.getModules();
+        while( modulesE.hasMoreElements() ) {
+                Module m=(Module)modulesE.nextElement();
+                Enumeration myRes=((ModuleClassLoader)m.getClassLoader()).findResources2(name,false);
+                if( myRes!=null ) {
+                    while( myRes.hasMoreElements() ) {
+                        result.addElement(myRes.nextElement());
+                    }
+                }
+        }
+        
+        return result.elements();
+
+    }
+
+    // Next methods implement the search alghoritm - parent, repo, delegation, etc 
+
+    /** getResource() - modified to implement the search alghoritm 
+     * 
+     */
+    public URL getResource(String name) {
+        URL url = null;
+        Enumeration modulesE=repository.getModules();
+        while( modulesE.hasMoreElements() ) {
+                Module m=(Module)modulesE.nextElement();
+                url=((ModuleClassLoader)m.getClassLoader()).getResource2(name, null, false);
+                if( url!= null ) {
+                    return url;
+                }
+        }
+        
+        if (url==null && DEBUG) {
+            if (DEBUGNF) log("findResource() NOTFOUND " + name );
+        }
+
+        return null;
+    }
+    
+    /**
+     * Load the class with the specified name, searching using the following
+     * algorithm until it finds and returns the class.  If the class cannot
+     * be found, returns <code>ClassNotFoundException</code>.
+     * <ul>
+     * <li>Call <code>findLoadedClass(String)</code> to check if the
+     *     class has already been loaded.  If it has, the same
+     *     <code>Class</code> object is returned.</li>
+     * <li>If the <code>delegate</code> property is set to <code>true</code>,
+     *     call the <code>loadClass()</code> method of the parent class
+     *     loader, if any.</li>
+     * <li>Call <code>findClass()</code> to find this class in our locally
+     *     defined repositories.</li>
+     * <li>Call the <code>loadClass()</code> method of our parent
+     *     class loader, if any.</li>
+     * </ul>
+     * If the class was found using the above steps, and the
+     * <code>resolve</code> flag is <code>true</code>, this method will then
+     * call <code>resolveClass(Class)</code> on the resulting Class object.
+     *
+     * @param name Name of the class to be loaded
+     * @param resolve If <code>true</code> then resolve the class
+     *
+     * @exception ClassNotFoundException if the class was not found
+     */
+    public Class loadClass(String name, boolean resolve)
+        throws ClassNotFoundException
+    {
+
+        Class clazz = null;
+        Enumeration modulesE=repository.getModules();
+        while( modulesE.hasMoreElements() ) {
+            try {
+                Module m=(Module)modulesE.nextElement();
+                return ((ModuleClassLoader)m.getClassLoader()).loadClass2(name, resolve, false);
+            } catch( ClassNotFoundException ex ) {
+                // ignore
+            }
+        }
+        throw new ClassNotFoundException( name );
+
+    }
+
+
+    // ------------------ Local methods ------------------------
+
+    private void log(String s ) {
+        System.err.println("RepositoryClassLoader: " + s);
+    }
+    private void log(String s, Throwable t ) {
+        System.err.println("RepositoryClassLoader: " + s);
+        t.printStackTrace();
+    }
+
+}
+
diff --git a/connectors/util/loader/org/apache/tomcat/util/loader/package.html b/connectors/util/loader/org/apache/tomcat/util/loader/package.html
new file mode 100644
index 0000000..a5efc00
--- /dev/null
+++ b/connectors/util/loader/org/apache/tomcat/util/loader/package.html
@@ -0,0 +1,32 @@
+<html>
+<body>
+
+The goal of this package is to provide class loading functionality, similar in behavior with Jboss and MLET loaders. There 
+is no specific policy, just a mechanism - how it is used depends on the application. It is based on the tomcat5.x class
+loader, with additional support for the 'repository' delegation.
+
+The main class is Loader - it controls a hierarchy of Repositories, each consisting of one or more Modules. Each Module corresponds to one jar file 
+or directory - and will have a ModuleClassLoader that answers only for that file. The Repository is associated with a ModuleClassLoader that delegates to 
+each Module. It is possible to add/remove/replace Modules at runtime - just like in JMX and JBoss. In normal tomcat, only webapps can be reloaded - this also allow connectors, valves, and any internal server jar to be reloaded.
+
+The package only deals with class loading, with minimal the dependencies. Currently there is no dependency except bare JDK1.3. 
+
+The modules and loaders can be registered with JMX by a module using the ModuleListener, after jmx class loader is created. Note that JMX is not a dependency and doesn't have to be in the classpath - it can be loaded in a Repository, and then something like Modeler will do the mapping. 
+
+Configuration uses a simple properties file describing the classpaths and the classes to launch - i.e. all a class loader needs to know, and similar with the old catalina.properties. 
+
+To implement a good module system on top of this we need lifecycle ( already present in tomcat ) and discipline in making sure there are no stale references to  objects in a module after its death. 
+
+An OSGI-like system may seem to deal with the second problem - but it doesn't solve anything, it just makes 
+the references more visible and requires major changes in how you code, as well as rewriting of most apis and implementations - and in the end it still 
+doesn't solve the problem. JBoss and JMX are actually on the right track in this, as oposed to OSGI. 
+
+The loader is also trying to stick to the minimal classloading-related functionality - unlike OSGI wich is reinventing all weels. I started working on the loader after trying to see how OSGI would fit, and realizing that it is a wrong design.
+
+
+<h2>Using loader for launching</h2>
+
+Loader has a main(), and will look up the loader.properties file, create the class loaders, and then launch any 'auto-startup' classes. The must important part of launching an app is setting the classpath, and using Loader allows the app to use more advanced features than using simple CLASSPATH.
+
+</body>
+</html>
\ No newline at end of file
diff --git a/connectors/util/loader/tomcat-loader.manifest b/connectors/util/loader/tomcat-loader.manifest
new file mode 100644
index 0000000..06424bd
--- /dev/null
+++ b/connectors/util/loader/tomcat-loader.manifest
@@ -0,0 +1,8 @@
+Manifest-version: 1.0
+Extension-Name: org.apache.tomcat.util.loader
+Specification-Vendor: Apache Software Foundation
+Specification-Version: 1.0
+Implementation-Vendor-Id: org.apache
+Implementation-Vendor: Apache Software Foundation
+Implementation-Version: 1.0
+Main-Class: org.apache.tomcat.util.loader.Loader